mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-19 00:00:03 -04:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 488d6da71a | |||
| b19e45a911 | |||
| a5b6cf898d | |||
| ce597c437d | |||
| fa4b3787d0 | |||
| 3c18e8a510 | |||
| 206481038e | |||
| cff4d88991 | |||
| 8ea8e061a9 | |||
| 101ad18f71 | |||
| 08b441a17e | |||
| b6c76d8566 | |||
| 3f74b2ddf3 | |||
| 46b48f32c3 | |||
| a07d0285fe | |||
| 98de4ad4ec | |||
| aa82612765 | |||
| 9213da3184 | |||
| a5e2226a5a | |||
| 773bedccca | |||
| 6bcc17a020 | |||
| 37d1da9b9d | |||
| 8cf1405dfc | |||
| 8c37d19928 | |||
| 87cd506fdd | |||
| e0c7edebbd | |||
| 5d13bd653f | |||
| 2d974874a9 | |||
| f625622b10 | |||
| dbdd7ae3f1 | |||
| 73883ebf99 | |||
| 970b238066 | |||
| 6cb8ac4b68 | |||
| 1a2a4edfe8 | |||
| a12a1f73b5 | |||
| 46bfdd93d8 | |||
| 447ef6b8eb | |||
| c19b6ede0a | |||
| 401c6355a1 | |||
| be0cf16124 | |||
| 47b0cd6e9a | |||
| b52ab149b3 | |||
| 900c26a5ac | |||
| 7233c593ac | |||
| 8159ba1245 | |||
| 92581a077b | |||
| f87b4d6748 | |||
| 0495f6e266 | |||
| bc7c8ba20c | |||
| 0c270f8343 | |||
| e1db332dca | |||
| 016977e382 | |||
| 134eecc59f | |||
| 16c78052ee | |||
| b51e8ccf6e | |||
| 76a9ff1122 | |||
| e9996f096f | |||
| 93d42e1c24 | |||
| 5824167600 | |||
| 126956ebd0 | |||
| d54ceba3ff | |||
| 11ee7e5e23 | |||
| 954dffd213 | |||
| b7b2a61c93 | |||
| 3cf6a4b836 | |||
| a93be99933 | |||
| b19bd1441f | |||
| 482af60534 | |||
| f55a6d30dd | |||
| e5f6227479 | |||
| 01d6fc8b49 | |||
| f133ea7d61 | |||
| 3d5cf0eecd | |||
| 975e23c28f | |||
| be60f307f7 | |||
| 3e783fba92 | |||
| 35ffe29e03 | |||
| 55155aa61c | |||
| 9e9decd5dd | |||
| 3c0b78549e | |||
| 46db5a2fbf | |||
| f6a9477279 | |||
| b7192ae857 | |||
| c0b2769e3b | |||
| 6c27ec6b30 | |||
| 0c90196357 | |||
| 48f82a159f | |||
| 7105738811 | |||
| 10aa4ba41e | |||
| 6fb06da4d7 | |||
| a963e1c355 | |||
| 966a9da45d | |||
| 319b2c4e18 | |||
| e9bf7d080c | |||
| fb3691e2ee | |||
| 25f9cc5c55 | |||
| 94777ddc96 | |||
| a4c12d0d64 | |||
| 08a69886b1 | |||
| ab85ff0ec8 |
@@ -9,11 +9,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
java: [ 8, 11, 17, 18 ]
|
||||
java: [ 8, 11, 17 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ matrix.java }}
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Run Tests
|
||||
run: mvn test
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@v3
|
||||
uses: mikepenz/action-junit-report@v2
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
report_paths: "**/target/surefire-reports/TEST*.xml"
|
||||
@@ -35,11 +35,11 @@ jobs:
|
||||
matrix:
|
||||
kcms: [ true, false ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
download_url="https://javadl.oracle.com/webapps/download/AutoDL?BundleId=245038_d3c52aa6bfa54d3ca74e617f18309292"
|
||||
wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'jdkfile'
|
||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
- name: Run Tests
|
||||
run: mvn test
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@v3
|
||||
uses: mikepenz/action-junit-report@v2
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
report_paths: "**/target/surefire-reports/TEST*.xml"
|
||||
@@ -66,9 +66,9 @@ jobs:
|
||||
if: github.ref == 'refs/heads/master' # only perform on latest master
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Maven Central
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v2
|
||||
with: # running setup-java again overwrites the settings.xml
|
||||
distribution: 'temurin'
|
||||
java-version: '8'
|
||||
|
||||
@@ -50,7 +50,7 @@ As there is lots of legacy data out there, we see the need for open implementati
|
||||
|
||||
|
||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
|
||||
and make sure you use an updated and secure version.*
|
||||
and make sure you use version 1.14 or later.*
|
||||
|
||||
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
|
||||
[JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html).
|
||||
@@ -523,7 +523,7 @@ 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.
|
||||
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.
|
||||
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.
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.batik</project.jpms.module.name>
|
||||
<batik.version>1.16</batik.version>
|
||||
<batik.version>1.14</batik.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
-627
@@ -1,627 +0,0 @@
|
||||
package com.twelvemonkeys.imageio;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.twelvemonkeys.imageio.StandardImageMetadataSupport.ColorSpaceType.*;
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* Base class for easy read-only implementation of the standard image metadata format.
|
||||
* Chroma, Data and Transparency nodes values are based on the required
|
||||
* {@link ImageTypeSpecifier}.
|
||||
* Other values or overrides may be specified using the builder.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
public class StandardImageMetadataSupport extends AbstractMetadata {
|
||||
|
||||
// The only required field, most standard metadata can be extracted from the type
|
||||
private final ImageTypeSpecifier type;
|
||||
protected final ColorSpaceType colorSpaceType;
|
||||
protected final boolean blackIsZero;
|
||||
private final IndexColorModel palette;
|
||||
protected final String compressionName;
|
||||
protected final boolean compressionLossless;
|
||||
protected final PlanarConfiguration planarConfiguration;
|
||||
private final int[] bitsPerSample;
|
||||
private final int[] significantBits;
|
||||
private final int[] sampleMSB;
|
||||
protected final Double pixelAspectRatio;
|
||||
protected final ImageOrientation orientation;
|
||||
protected final String formatVersion;
|
||||
protected final SubimageInterpretation subimageInterpretation;
|
||||
private final Calendar documentCreationTime; // TODO: This field should be a LocalDateTime or other java.time type, Consider a long timestamp + TimeZone to avoid messing up the API...
|
||||
private final Collection<TextEntry> textEntries;
|
||||
|
||||
protected StandardImageMetadataSupport(Builder builder) {
|
||||
notNull(builder, "builder");
|
||||
|
||||
// Baseline
|
||||
type = builder.type;
|
||||
|
||||
// Chroma
|
||||
colorSpaceType = builder.colorSpaceType;
|
||||
blackIsZero = builder.blackIsZero;
|
||||
palette = builder.palette;
|
||||
|
||||
// Compression
|
||||
compressionName = builder.compressionName;
|
||||
compressionLossless = builder.compressionLossless;
|
||||
|
||||
// Data
|
||||
planarConfiguration = builder.planarConfiguration;
|
||||
bitsPerSample = builder.bitsPerSample;
|
||||
significantBits = builder.significantBits;
|
||||
sampleMSB = builder.sampleMSB;
|
||||
|
||||
// Dimension
|
||||
orientation = builder.orientation;
|
||||
pixelAspectRatio = builder.pixelAspectRatio;
|
||||
|
||||
// Document
|
||||
formatVersion = builder.formatVersion;
|
||||
documentCreationTime = builder.documentCreationTime;
|
||||
subimageInterpretation = builder.subimageInterpretation;
|
||||
|
||||
// Text
|
||||
textEntries = builder.textEntries;
|
||||
}
|
||||
|
||||
public static Builder builder(ImageTypeSpecifier type) {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final ImageTypeSpecifier type;
|
||||
private ColorSpaceType colorSpaceType;
|
||||
private boolean blackIsZero = true;
|
||||
private IndexColorModel palette;
|
||||
private String compressionName;
|
||||
private boolean compressionLossless = true;
|
||||
private PlanarConfiguration planarConfiguration;
|
||||
public int[] bitsPerSample;
|
||||
private int[] significantBits;
|
||||
private int[] sampleMSB;
|
||||
private Double pixelAspectRatio;
|
||||
private ImageOrientation orientation = ImageOrientation.Normal;
|
||||
private String formatVersion;
|
||||
private SubimageInterpretation subimageInterpretation;
|
||||
private Calendar documentCreationTime; // TODO: This field should be a LocalDateTime or other java.time type
|
||||
private final Collection<TextEntry> textEntries = new ArrayList<>();
|
||||
|
||||
protected Builder(ImageTypeSpecifier type) {
|
||||
this.type = notNull(type, "type");
|
||||
}
|
||||
|
||||
public Builder withColorSpaceType(ColorSpaceType colorSpaceType) {
|
||||
this.colorSpaceType = colorSpaceType;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withBlackIsZero(boolean blackIsZero) {
|
||||
this.blackIsZero = blackIsZero;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPalette(IndexColorModel palette) {
|
||||
this.palette = palette;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withCompressionTypeName(String compressionName) {
|
||||
this.compressionName = notNull(compressionName, "compressionName").equalsIgnoreCase("none") ? null : compressionName;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withCompressionLossless(boolean lossless) {
|
||||
this.compressionLossless = isTrue(lossless || compressionName != null, lossless, "Lossy compression requires compression name");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPlanarConfiguration(PlanarConfiguration planarConfiguration) {
|
||||
this.planarConfiguration = planarConfiguration;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withBitsPerSample(int... bitsPerSample) {
|
||||
this.bitsPerSample = bitsPerSample;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withSignificantBitsPerSample(int... significantBits) {
|
||||
this.significantBits = isTrue(significantBits.length == 1 || significantBits.length == type.getNumBands(),
|
||||
significantBits,
|
||||
String.format("single value or %d values expected", type.getNumBands()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withSampleMSB(int... sampleMSB) {
|
||||
this.sampleMSB = isTrue(sampleMSB.length == 1 || sampleMSB.length == type.getNumBands(),
|
||||
sampleMSB,
|
||||
String.format("single value or %d values expected", type.getNumBands()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPixelAspectRatio(Double pixelAspectRatio) {
|
||||
this.pixelAspectRatio = pixelAspectRatio;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withOrientation(ImageOrientation orientation) {
|
||||
this.orientation = notNull(orientation, "orientation");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withFormatVersion(String formatVersion) {
|
||||
this.formatVersion = notNull(formatVersion, "formatVersion");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withSubimageInterpretation(SubimageInterpretation interpretation) {
|
||||
this.subimageInterpretation = interpretation;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withDocumentCreationTime(Calendar creationTime) {
|
||||
this.documentCreationTime = creationTime;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withTextEntries(Map<String, String> entries) {
|
||||
return withTextEntries(toTextEntries(notNull(entries, "entries").entrySet()));
|
||||
}
|
||||
|
||||
private Collection<TextEntry> toTextEntries(Collection<Map.Entry<String, String>> entries) {
|
||||
TextEntry[] result = new TextEntry[entries.size()];
|
||||
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> entry : entries) {
|
||||
result[i++] = new TextEntry(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return Arrays.asList(result);
|
||||
}
|
||||
|
||||
public Builder withTextEntries(Collection<TextEntry> entries) {
|
||||
this.textEntries.addAll(notNull(entries, "entries"));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withTextEntry(String keyword, String value) {
|
||||
if (value != null && !value.isEmpty()) {
|
||||
this.textEntries.add(new TextEntry(notNull(keyword, "keyword"), value));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIOMetadata build() {
|
||||
return new StandardImageMetadataSupport(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected enum ColorSpaceType {
|
||||
XYZ(3),
|
||||
Lab(3),
|
||||
Luv(3),
|
||||
YCbCr(3),
|
||||
Yxy(3),
|
||||
YCCK(4),
|
||||
PhotoYCC(3),
|
||||
RGB(3),
|
||||
GRAY(1),
|
||||
HSV(3),
|
||||
HLS(3),
|
||||
CMYK(3),
|
||||
CMY(3),
|
||||
|
||||
// Generic types (so much extra work, because Java names can't start with a number, phew...)
|
||||
GENERIC_2CLR(2, "2CLR"),
|
||||
GENERIC_3CLR(3, "3CLR"),
|
||||
GENERIC_4CLR(4, "4CLR"),
|
||||
GENERIC_5CLR(5, "5CLR"),
|
||||
GENERIC_6CLR(6, "6CLR"),
|
||||
GENERIC_7CLR(7, "7CLR"),
|
||||
GENERIC_8CLR(8, "8CLR"),
|
||||
GENERIC_9CLR(9, "9CLR"),
|
||||
GENERIC_ACLR(0xA, "ACLR"),
|
||||
GENERIC_BCLR(0xB, "BCLR"),
|
||||
GENERIC_CCLR(0xC, "CCLR"),
|
||||
GENERIC_DCLR(0xD, "DCLR"),
|
||||
GENERIC_ECLR(0xE, "ECLR"),
|
||||
GENERIC_FCLR(0xF, "FCLR");
|
||||
|
||||
final int numChannels;
|
||||
private final String nameOverride;
|
||||
|
||||
ColorSpaceType(int numChannels) {
|
||||
this(numChannels, null);
|
||||
}
|
||||
ColorSpaceType(int numChannels, String nameOverride) {
|
||||
this.numChannels = numChannels;
|
||||
this.nameOverride = nameOverride;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return nameOverride != null ? nameOverride : super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected enum PlanarConfiguration {
|
||||
PixelInterleaved,
|
||||
PlaneInterleaved,
|
||||
LineInterleaved,
|
||||
TileInterleaved
|
||||
}
|
||||
|
||||
protected enum ImageOrientation {
|
||||
Normal,
|
||||
Rotate90,
|
||||
Rotate180,
|
||||
Rotate270,
|
||||
FlipH,
|
||||
FlipV,
|
||||
FlipHRotate90,
|
||||
FlipVRotate90
|
||||
}
|
||||
|
||||
protected enum SubimageInterpretation {
|
||||
Standalone,
|
||||
SinglePage,
|
||||
FullResolution,
|
||||
ReducedResolution,
|
||||
PyramidLayer,
|
||||
Preview,
|
||||
VolumeSlice,
|
||||
ObjectView,
|
||||
Panorama,
|
||||
AnimationFrame,
|
||||
TransparencyMask,
|
||||
CompositingLayer,
|
||||
SpectralSlice,
|
||||
Unknown
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chromaNode = new IIOMetadataNode("Chroma");
|
||||
|
||||
ColorModel colorModel = colorSpaceType != null ? null : type.getColorModel();
|
||||
ColorSpaceType csType = colorSpaceType != null ? colorSpaceType : colorSpaceType(colorModel.getColorSpace());
|
||||
int numComponents = colorSpaceType != null ? colorSpaceType.numChannels : colorModel.getNumComponents();
|
||||
|
||||
IIOMetadataNode colorSpaceTypeNode = new IIOMetadataNode("ColorSpaceType");
|
||||
chromaNode.appendChild(colorSpaceTypeNode);
|
||||
colorSpaceTypeNode.setAttribute("name", csType.toString());
|
||||
|
||||
IIOMetadataNode numChannelsNode = new IIOMetadataNode("NumChannels");
|
||||
numChannelsNode.setAttribute("value", String.valueOf(numComponents));
|
||||
chromaNode.appendChild(numChannelsNode);
|
||||
|
||||
IIOMetadataNode blackIsZeroNode = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZeroNode.setAttribute("value", booleanString(blackIsZero));
|
||||
chromaNode.appendChild(blackIsZeroNode);
|
||||
|
||||
if (colorModel instanceof IndexColorModel || palette != null) {
|
||||
IndexColorModel colorMap = palette != null ? palette : (IndexColorModel) colorModel;
|
||||
|
||||
IIOMetadataNode paletteNode = new IIOMetadataNode("Palette");
|
||||
chromaNode.appendChild(paletteNode);
|
||||
|
||||
for (int i = 0; i < colorMap.getMapSize(); i++) {
|
||||
IIOMetadataNode paletteEntryNode = new IIOMetadataNode("PaletteEntry");
|
||||
paletteNode.appendChild(paletteEntryNode);
|
||||
|
||||
paletteEntryNode.setAttribute("index", Integer.toString(i));
|
||||
paletteEntryNode.setAttribute("red", Integer.toString(colorMap.getRed(i)));
|
||||
paletteEntryNode.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
|
||||
paletteEntryNode.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
|
||||
|
||||
// Assumption: BITMASK transparency will use single transparent pixel
|
||||
if (colorMap.getTransparency() == Transparency.TRANSLUCENT) {
|
||||
paletteEntryNode.setAttribute("alpha", Integer.toString(colorMap.getAlpha(i)));
|
||||
}
|
||||
}
|
||||
|
||||
if (colorMap.getTransparentPixel() != -1) {
|
||||
IIOMetadataNode backgroundIndexNode = new IIOMetadataNode("BackgroundIndex");
|
||||
chromaNode.appendChild(backgroundIndexNode);
|
||||
backgroundIndexNode.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: BackgroundColor?
|
||||
|
||||
return chromaNode;
|
||||
}
|
||||
|
||||
private static ColorSpaceType colorSpaceType(ColorSpace colorSpace) {
|
||||
switch (colorSpace.getType()) {
|
||||
case ColorSpace.TYPE_XYZ:
|
||||
return XYZ;
|
||||
case ColorSpace.TYPE_Lab:
|
||||
return Lab;
|
||||
case ColorSpace.TYPE_Luv:
|
||||
return Luv;
|
||||
case ColorSpace.TYPE_YCbCr:
|
||||
return YCbCr;
|
||||
case ColorSpace.TYPE_Yxy:
|
||||
return Yxy;
|
||||
// Note: Can't map to YCCK or PhotoYCC, as there's no corresponding constant in java.awt.ColorSpace
|
||||
case ColorSpace.TYPE_RGB:
|
||||
return RGB;
|
||||
case ColorSpace.TYPE_GRAY:
|
||||
return GRAY;
|
||||
case ColorSpace.TYPE_HSV:
|
||||
return HSV;
|
||||
case ColorSpace.TYPE_HLS:
|
||||
return HLS;
|
||||
case ColorSpace.TYPE_CMYK:
|
||||
return CMYK;
|
||||
case ColorSpace.TYPE_CMY:
|
||||
return CMY;
|
||||
default:
|
||||
int numComponents = colorSpace.getNumComponents();
|
||||
if (numComponents == 1) {
|
||||
return GRAY;
|
||||
}
|
||||
else if (numComponents < 16) {
|
||||
return ColorSpaceType.valueOf("GENERIC_" + Integer.toHexString(numComponents) + "CLR");
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown ColorSpace type: " + colorSpace);
|
||||
}
|
||||
|
||||
protected static final class TextEntry {
|
||||
static final List<String> COMPRESSIONS = Arrays.asList("none", "lzw", "zip", "bzip", "other");
|
||||
|
||||
final String keyword;
|
||||
final String value;
|
||||
final String language;
|
||||
final String encoding;
|
||||
final String compression;
|
||||
|
||||
public TextEntry(final String keyword, final String value) {
|
||||
this(keyword, value, null, null, null);
|
||||
}
|
||||
|
||||
public TextEntry(final String keyword, final String value, final String language, final String encoding, final String compression) {
|
||||
this.keyword = keyword;
|
||||
this.value = notNull(value, "value");
|
||||
this.language = language;
|
||||
this.encoding = encoding;
|
||||
this.compression = isTrue(compression == null || COMPRESSIONS.contains(compression), compression, String.format("Unknown compression: %s (expected: %s)", compression, COMPRESSIONS));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
if (compressionName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", compressionName);
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", booleanString(compressionLossless));
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
protected static String booleanString(boolean booleanValue) {
|
||||
return booleanValue ? "TRUE" : "FALSE";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode dataNode = new IIOMetadataNode("Data");
|
||||
|
||||
IIOMetadataNode planarConfigurationNode = new IIOMetadataNode("PlanarConfiguration");
|
||||
dataNode.appendChild(planarConfigurationNode);
|
||||
planarConfigurationNode.setAttribute("value", planarConfiguration != null ? planarConfiguration.toString() :
|
||||
(type.getSampleModel() instanceof BandedSampleModel ? "PlaneInterleaved" : "PixelInterleaved"));
|
||||
|
||||
String sampleFormatValue = colorSpaceType == null && type.getColorModel() instanceof IndexColorModel
|
||||
? "Index"
|
||||
: sampleFormat(type.getSampleModel());
|
||||
|
||||
if (sampleFormatValue != null) {
|
||||
IIOMetadataNode sampleFormatNode = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormatNode.setAttribute("value", sampleFormatValue);
|
||||
dataNode.appendChild(sampleFormatNode);
|
||||
}
|
||||
|
||||
int[] bitsPerSample = this.bitsPerSample != null ? this.bitsPerSample : type.getSampleModel().getSampleSize();
|
||||
IIOMetadataNode bitsPerSampleNode = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSampleNode.setAttribute("value", createListValue(bitsPerSample.length, bitsPerSample));
|
||||
dataNode.appendChild(bitsPerSampleNode);
|
||||
|
||||
if (significantBits != null) {
|
||||
String significantBitsValue = createListValue(type.getNumBands(), significantBits);
|
||||
if (!significantBitsValue.equals(bitsPerSampleNode.getAttribute("value"))) {
|
||||
IIOMetadataNode significantBitsPerSampleNode = new IIOMetadataNode("SignificantBitsPerSample");
|
||||
significantBitsPerSampleNode.setAttribute("value", significantBitsValue);
|
||||
dataNode.appendChild(significantBitsPerSampleNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (sampleMSB != null) {
|
||||
// TODO: Only if different from default!
|
||||
IIOMetadataNode sampleMSBNode = new IIOMetadataNode("SampleMSB");
|
||||
sampleMSBNode.setAttribute("value", createListValue(type.getNumBands(), sampleMSB));
|
||||
dataNode.appendChild(sampleMSBNode);
|
||||
}
|
||||
|
||||
return dataNode;
|
||||
}
|
||||
|
||||
private static String createListValue(final int itemCount, final int... values) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
buffer.append(values[i % values.length]);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private static String sampleFormat(SampleModel sampleModel) {
|
||||
switch (sampleModel.getDataType()) {
|
||||
case DataBuffer.TYPE_SHORT:
|
||||
case DataBuffer.TYPE_INT:
|
||||
if (sampleModel instanceof ComponentSampleModel) {
|
||||
return "SignedIntegral";
|
||||
}
|
||||
// Otherwise fall-through, most likely a *PixelPackedSampleModel
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
return "UnsignedIntegral";
|
||||
case DataBuffer.TYPE_FLOAT:
|
||||
case DataBuffer.TYPE_DOUBLE:
|
||||
return "Real";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimensionNode = new IIOMetadataNode("Dimension");
|
||||
|
||||
if (pixelAspectRatio != null) {
|
||||
IIOMetadataNode pixelAspectRatioNode = new IIOMetadataNode("PixelAspectRatio");
|
||||
pixelAspectRatioNode.setAttribute("value", String.valueOf(pixelAspectRatio));
|
||||
dimensionNode.appendChild(pixelAspectRatioNode);
|
||||
}
|
||||
|
||||
IIOMetadataNode imageOrientationNode = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientationNode.setAttribute("value", orientation.toString());
|
||||
dimensionNode.appendChild(imageOrientationNode);
|
||||
|
||||
return dimensionNode.hasChildNodes() ? dimensionNode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode documentNode = new IIOMetadataNode("Document");
|
||||
|
||||
if (formatVersion != null) {
|
||||
IIOMetadataNode formatVersionNode = new IIOMetadataNode("FormatVersion");
|
||||
documentNode.appendChild(formatVersionNode);
|
||||
formatVersionNode.setAttribute("value", formatVersion);
|
||||
}
|
||||
|
||||
if (subimageInterpretation != null) {
|
||||
IIOMetadataNode subImageInterpretationNode = new IIOMetadataNode("SubimageInterpretation");
|
||||
documentNode.appendChild(subImageInterpretationNode);
|
||||
subImageInterpretationNode.setAttribute("value", subimageInterpretation.toString());
|
||||
}
|
||||
|
||||
if (documentCreationTime != null) {
|
||||
IIOMetadataNode imageCreationTimeNode = new IIOMetadataNode("ImageCreationTime");
|
||||
documentNode.appendChild(imageCreationTimeNode);
|
||||
|
||||
imageCreationTimeNode.setAttribute("year", String.valueOf(documentCreationTime.get(Calendar.YEAR)));
|
||||
imageCreationTimeNode.setAttribute("month", String.valueOf(documentCreationTime.get(Calendar.MONTH) + 1));
|
||||
imageCreationTimeNode.setAttribute("day", String.valueOf(documentCreationTime.get(Calendar.DAY_OF_MONTH)));
|
||||
imageCreationTimeNode.setAttribute("hour", String.valueOf(documentCreationTime.get(Calendar.HOUR_OF_DAY)));
|
||||
imageCreationTimeNode.setAttribute("minute", String.valueOf(documentCreationTime.get(Calendar.MINUTE)));
|
||||
imageCreationTimeNode.setAttribute("second", String.valueOf(documentCreationTime.get(Calendar.SECOND)));
|
||||
}
|
||||
|
||||
return documentNode.hasChildNodes() ? documentNode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
if (textEntries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode textNode = new IIOMetadataNode("Text");
|
||||
|
||||
// DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright:
|
||||
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
|
||||
|
||||
for (TextEntry entry : textEntries) {
|
||||
IIOMetadataNode textEntryNode = new IIOMetadataNode("TextEntry");
|
||||
textNode.appendChild(textEntryNode);
|
||||
if (entry.keyword != null) {
|
||||
textEntryNode.setAttribute("keyword", entry.keyword);
|
||||
}
|
||||
textEntryNode.setAttribute("value", entry.value);
|
||||
if (entry.language != null) {
|
||||
textEntryNode.setAttribute("language", entry.language);
|
||||
}
|
||||
if (entry.encoding != null) {
|
||||
textEntryNode.setAttribute("encoding", entry.encoding);
|
||||
}
|
||||
if (entry.compression != null) {
|
||||
textEntryNode.setAttribute("compression", entry.compression);
|
||||
}
|
||||
}
|
||||
|
||||
return textNode;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
IIOMetadataNode transparencyNode = new IIOMetadataNode("Transparency");
|
||||
|
||||
ColorModel colorModel = type.getColorModel();
|
||||
|
||||
IIOMetadataNode alphaNode = new IIOMetadataNode("Alpha");
|
||||
transparencyNode.appendChild(alphaNode);
|
||||
alphaNode.setAttribute("value", colorModel.hasAlpha() ? (colorModel.isAlphaPremultiplied() ? "premultiplied" : "nonpremultiplied") : "none");
|
||||
|
||||
if (colorModel instanceof IndexColorModel) {
|
||||
IndexColorModel icm = (IndexColorModel) colorModel;
|
||||
if (icm.getTransparentPixel() != -1) {
|
||||
IIOMetadataNode transparentIndexNode = new IIOMetadataNode("TransparentIndex");
|
||||
transparencyNode.appendChild(transparentIndexNode);
|
||||
transparentIndexNode.setAttribute("value", Integer.toString(icm.getTransparentPixel()));
|
||||
}
|
||||
}
|
||||
|
||||
return transparencyNode;
|
||||
}
|
||||
}
|
||||
-348
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.stream.ImageInputStreamImpl;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.lang.Math.max;
|
||||
|
||||
/**
|
||||
* A buffered {@link javax.imageio.stream.ImageInputStream} that is backed by a {@link java.nio.channels.SeekableByteChannel}
|
||||
* and provides greatly improved performance
|
||||
* compared to {@link javax.imageio.stream.FileCacheImageInputStream} or {@link javax.imageio.stream.MemoryCacheImageInputStream}
|
||||
* for shorter reads, like single byte or bit reads.
|
||||
*/
|
||||
final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
|
||||
private static final Closeable CLOSEABLE_STUB = new Closeable() {
|
||||
@Override public void close() {}
|
||||
};
|
||||
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
private ByteBuffer byteBuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
|
||||
private byte[] buffer = byteBuffer.array();
|
||||
private int bufferPos;
|
||||
private int bufferLimit;
|
||||
|
||||
private final ByteBuffer integralCache = ByteBuffer.allocate(8);
|
||||
private final byte[] integralCacheArray = integralCache.array();
|
||||
|
||||
private SeekableByteChannel channel;
|
||||
private Closeable closeable;
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code File}.
|
||||
*
|
||||
* @param file a {@code File} to read from.
|
||||
* @throws IllegalArgumentException if {@code file} is {@code null}.
|
||||
* @throws SecurityException if a security manager is installed, and it denies read access to the file.
|
||||
* @throws IOException if an I/O error occurs while opening the file.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final File file) throws IOException {
|
||||
this(notNull(file, "file").toPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code Path}.
|
||||
*
|
||||
* @param file a {@code Path} to read from.
|
||||
* @throws IllegalArgumentException if {@code file} is {@code null}.
|
||||
* @throws UnsupportedOperationException if the {@code file} is associated with a provider that does not support creating file channels.
|
||||
* @throws IOException if an I/O error occurs while opening the file.
|
||||
* @throws SecurityException if a security manager is installed, and it denies read access to the file.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final Path file) throws IOException {
|
||||
this(FileChannel.open(notNull(file, "file"), StandardOpenOption.READ), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code RandomAccessFile}.
|
||||
*
|
||||
* @param file a {@code RandomAccessFile} to read from.
|
||||
* @throws IllegalArgumentException if {@code file} is {@code null}.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final RandomAccessFile file) {
|
||||
// Closing the RAF is inconsistent, but emulates the behavior of javax.imageio.stream.FileImageInputStream
|
||||
this(notNull(file, "file").getChannel(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code FileInputStream}.
|
||||
* <p>
|
||||
* Closing this stream will <em>not</em> close the {@code FileInputStream}.
|
||||
* </p>
|
||||
*
|
||||
* @param inputStream a {@code FileInputStream} to read from.
|
||||
* @throws IllegalArgumentException if {@code inputStream} is {@code null}.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final FileInputStream inputStream) {
|
||||
this(notNull(inputStream, "inputStream").getChannel(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code SeekableByteChannel}.
|
||||
* <p>
|
||||
* Closing this stream will <em>not</em> close the {@code SeekableByteChannel}.
|
||||
* </p>
|
||||
*
|
||||
* @param channel a {@code SeekableByteChannel} to read from.
|
||||
* @throws IllegalArgumentException if {@code channel} is {@code null}.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final SeekableByteChannel channel) {
|
||||
this(notNull(channel, "channel"), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code Cache}.
|
||||
* <p>
|
||||
* Closing this stream will close the {@code Cache}.
|
||||
* </p>
|
||||
*
|
||||
* @param cache a {@code SeekableByteChannel} to read from.
|
||||
* @throws IllegalArgumentException if {@code channel} is {@code null}.
|
||||
*/
|
||||
BufferedChannelImageInputStream(final Cache cache) {
|
||||
this(notNull(cache, "cache"), true);
|
||||
}
|
||||
|
||||
private BufferedChannelImageInputStream(final SeekableByteChannel channel, boolean closeChannelOnClose) {
|
||||
this.channel = notNull(channel, "channel");
|
||||
this.closeable = closeChannelOnClose ? this.channel : CLOSEABLE_STUB;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean fillBuffer() throws IOException {
|
||||
byteBuffer.rewind();
|
||||
int length = channel.read(byteBuffer);
|
||||
bufferPos = 0;
|
||||
bufferLimit = max(length, 0);
|
||||
|
||||
return bufferLimit > 0;
|
||||
}
|
||||
|
||||
private boolean bufferEmpty() {
|
||||
return bufferPos >= bufferLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByteOrder(ByteOrder byteOrder) {
|
||||
super.setByteOrder(byteOrder);
|
||||
integralCache.order(byteOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
checkClosed();
|
||||
|
||||
if (bufferEmpty() && !fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
streamPos++;
|
||||
|
||||
return buffer[bufferPos++] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
checkClosed();
|
||||
bitOffset = 0;
|
||||
|
||||
if (bufferEmpty()) {
|
||||
// Bypass buffer if buffer is empty for reads longer than buffer
|
||||
if (length >= buffer.length) {
|
||||
return readDirect(bytes, offset, length);
|
||||
}
|
||||
else if (!fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fromBuffer = readBuffered(bytes, offset, length);
|
||||
|
||||
if (length > fromBuffer) {
|
||||
// Due to known bugs in certain JDK-bundled ImageIO plugins expecting read to behave as readFully,
|
||||
// we'll read as much as possible from the buffer, and the rest directly after
|
||||
return fromBuffer + max(0, readDirect(bytes, offset + fromBuffer, length - fromBuffer));
|
||||
}
|
||||
|
||||
return fromBuffer;
|
||||
}
|
||||
|
||||
private int readDirect(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
// Invalidate the buffer, as its contents is no longer in sync with the stream's position.
|
||||
bufferLimit = 0;
|
||||
|
||||
ByteBuffer wrapped = ByteBuffer.wrap(bytes, offset, length);
|
||||
int read = 0;
|
||||
while (wrapped.hasRemaining()) {
|
||||
int count = channel.read(wrapped);
|
||||
if (count == -1) {
|
||||
if (read == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
read += count;
|
||||
}
|
||||
|
||||
streamPos += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
private int readBuffered(final byte[] bytes, final int offset, final int length) {
|
||||
// Read as much as possible from buffer
|
||||
int available = Math.min(bufferLimit - bufferPos, length);
|
||||
|
||||
if (available > 0) {
|
||||
System.arraycopy(buffer, bufferPos, bytes, offset, available);
|
||||
bufferPos += available;
|
||||
streamPos += available;
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
public long length() {
|
||||
// WTF?! This method is allowed to throw IOException in the interface...
|
||||
try {
|
||||
checkClosed();
|
||||
return channel.size();
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
|
||||
buffer = null;
|
||||
byteBuffer = null;
|
||||
|
||||
channel = null;
|
||||
|
||||
try {
|
||||
closeable.close();
|
||||
}
|
||||
finally {
|
||||
closeable = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to override the readShort(), readInt() and readLong() methods,
|
||||
// because the implementations in ImageInputStreamImpl expects the
|
||||
// read(byte[], int, int) to always read the expected number of bytes,
|
||||
// causing uninitialized values, alignment issues and EOFExceptions at
|
||||
// random places...
|
||||
// Notes:
|
||||
// * readUnsignedXx() is covered by their signed counterparts
|
||||
// * readChar() is covered by readShort()
|
||||
// * readFloat() and readDouble() is covered by readInt() and readLong()
|
||||
// respectively.
|
||||
// * readLong() may be covered by two readInt()s, we'll override to be safe
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
readFully(integralCacheArray, 0, 2);
|
||||
|
||||
return integralCache.getShort(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
readFully(integralCacheArray, 0, 4);
|
||||
|
||||
return integralCache.getInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
readFully(integralCacheArray, 0, 8);
|
||||
|
||||
return integralCache.getLong(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
checkClosed();
|
||||
|
||||
if (position < flushedPos) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPos!");
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
|
||||
if (streamPos == position) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimized to not invalidate buffer if new position is within current buffer
|
||||
long newBufferPos = bufferPos + position - streamPos;
|
||||
if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
|
||||
bufferPos = (int) newBufferPos;
|
||||
}
|
||||
else {
|
||||
// Will invalidate buffer
|
||||
bufferLimit = 0;
|
||||
channel.position(position);
|
||||
}
|
||||
|
||||
streamPos = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBefore(final long pos) throws IOException {
|
||||
super.flushBefore(pos);
|
||||
|
||||
if (channel instanceof Cache) {
|
||||
// In case of memory cache, free up memory
|
||||
((Cache) channel).flushBefore(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -49,7 +49,6 @@ import static java.lang.Math.max;
|
||||
* {@link File} or {@link RandomAccessFile} can be used as input.
|
||||
*
|
||||
* @see javax.imageio.stream.FileImageInputStream
|
||||
* @deprecated Use {@link BufferedChannelImageInputStream} instead.
|
||||
*/
|
||||
// TODO: Create a memory-mapped version?
|
||||
// Or not... From java.nio.channels.FileChannel.map:
|
||||
@@ -58,7 +57,6 @@ import static java.lang.Math.max;
|
||||
// the usual {@link #read read} and {@link #write write} methods. From the
|
||||
// standpoint of performance it is generally only worth mapping relatively
|
||||
// large files into memory.
|
||||
@Deprecated
|
||||
public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
@@ -192,10 +190,10 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
|
||||
buffer = null;
|
||||
|
||||
raf.close();
|
||||
|
||||
raf = null;
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
// Need to override the readShort(), readInt() and readLong() methods,
|
||||
|
||||
+6
-9
@@ -37,19 +37,18 @@ import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamSpi
|
||||
* Experimental
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
|
||||
*/
|
||||
public final class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public BufferedFileImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
@@ -70,13 +69,12 @@ public final class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean useCacheFile, final File cacheDir) throws IOException {
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
|
||||
if (input instanceof File) {
|
||||
try {
|
||||
return new BufferedChannelImageInputStream((File) input);
|
||||
return new BufferedFileImageInputStream((File) input);
|
||||
}
|
||||
catch (FileNotFoundException | NoSuchFileException e) {
|
||||
catch (FileNotFoundException e) {
|
||||
// For consistency with the JRE bundled SPIs, we'll return null here,
|
||||
// even though the spec does not say that's allowed.
|
||||
// The problem is that the SPIs can only declare that they support an input type like a File,
|
||||
@@ -93,8 +91,7 @@ public final class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(final Locale locale) {
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a File";
|
||||
}
|
||||
|
||||
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BufferedInputStreamImageInputStreamSpi.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedInputStreamImageInputStreamSpi.java,v 1.0 08/09/2022 haraldk Exp$
|
||||
*/
|
||||
public final class BufferedInputStreamImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public BufferedInputStreamImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
|
||||
private BufferedInputStreamImageInputStreamSpi(ProviderInfo providerInfo) {
|
||||
super(providerInfo.getVendorName(), providerInfo.getVersion(), InputStream.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
Iterator<ImageInputStreamSpi> providers = registry.getServiceProviders(ImageInputStreamSpi.class, new InputStreamFilter(), true);
|
||||
|
||||
while (providers.hasNext()) {
|
||||
ImageInputStreamSpi provider = providers.next();
|
||||
if (provider != this) {
|
||||
registry.setOrdering(ImageInputStreamSpi.class, this, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean useCacheFile, final File cacheDir) throws IOException {
|
||||
if (input instanceof InputStream) {
|
||||
ReadableByteChannel channel = Channels.newChannel((InputStream) input);
|
||||
|
||||
if (channel instanceof SeekableByteChannel) {
|
||||
// Special case for FileInputStream/FileChannel, we can get a seekable channel directly
|
||||
return new BufferedChannelImageInputStream((SeekableByteChannel) channel);
|
||||
}
|
||||
|
||||
// Otherwise, create a cache for backwards seeking
|
||||
return new BufferedChannelImageInputStream(useCacheFile ? new FileCache(channel, cacheDir) : new MemoryCache(channel));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type InputStream: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseCacheFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Service provider that instantiates an ImageInputStream from an InputStream";
|
||||
}
|
||||
|
||||
private static class InputStreamFilter implements ServiceRegistry.Filter {
|
||||
@Override
|
||||
public boolean filter(final Object provider) {
|
||||
return ((ImageInputStreamSpi) provider).getInputClass() == InputStream.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-6
@@ -48,7 +48,7 @@ import java.util.Locale;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedRAFImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
|
||||
*/
|
||||
public final class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public BufferedRAFImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
@@ -69,10 +69,9 @@ public final class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean useCacheFile, final File cacheDir) {
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
|
||||
if (input instanceof RandomAccessFile) {
|
||||
return new BufferedChannelImageInputStream((RandomAccessFile) input);
|
||||
return new BufferedFileImageInputStream((RandomAccessFile) input);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type RandomAccessFile: " + input);
|
||||
@@ -83,8 +82,7 @@ public final class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(final Locale locale) {
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a RandomAccessFile";
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -48,18 +48,18 @@ public final class ByteArrayImageInputStream extends ImageInputStreamImpl {
|
||||
private final int dataOffset;
|
||||
private final int dataLength;
|
||||
|
||||
public ByteArrayImageInputStream(final byte[] data) {
|
||||
this(data, 0, data != null ? data.length : -1);
|
||||
public ByteArrayImageInputStream(final byte[] pData) {
|
||||
this(pData, 0, pData != null ? pData.length : -1);
|
||||
}
|
||||
|
||||
public ByteArrayImageInputStream(final byte[] data, int offset, int length) {
|
||||
this.data = notNull(data, "data");
|
||||
dataOffset = isMax(data.length, offset, "offset");
|
||||
dataLength = isMax(data.length - offset, length, "length");
|
||||
public ByteArrayImageInputStream(final byte[] pData, int offset, int length) {
|
||||
data = notNull(pData, "data");
|
||||
dataOffset = isBetween(0, pData.length, offset, "offset");
|
||||
dataLength = isBetween(0, pData.length - offset, length, "length");
|
||||
}
|
||||
|
||||
private static int isMax(final int high, final int value, final String name) {
|
||||
return isTrue(value >= 0 && value <= high, value, String.format("%s out of range [0, %d]: %d", name, high, value));
|
||||
private static int isBetween(final int low, final int high, final int value, final String name) {
|
||||
return isTrue(value >= low && value <= high, value, String.format("%s out of range [%d, %d]: %d", name, low, high, value));
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
@@ -72,14 +72,14 @@ public final class ByteArrayImageInputStream extends ImageInputStreamImpl {
|
||||
return data[((int) streamPos++) + dataOffset] & 0xff;
|
||||
}
|
||||
|
||||
public int read(byte[] buffer, int offset, int len) throws IOException {
|
||||
public int read(byte[] pBuffer, int pOffset, int pLength) throws IOException {
|
||||
if (streamPos >= dataLength) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int length = (int) Math.min(dataLength - streamPos, len);
|
||||
int length = (int) Math.min(this.dataLength - streamPos, pLength);
|
||||
bitOffset = 0;
|
||||
System.arraycopy(data, (int) streamPos + dataOffset, buffer, offset, length);
|
||||
System.arraycopy(data, (int) streamPos + dataOffset, pBuffer, pOffset, length);
|
||||
streamPos += length;
|
||||
|
||||
return length;
|
||||
|
||||
+8
-9
@@ -45,7 +45,7 @@ import java.util.Locale;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ByteArrayImageInputStreamSpi.java,v 1.0 May 15, 2008 2:12:12 PM haraldk Exp$
|
||||
*/
|
||||
public final class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
|
||||
public ByteArrayImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
@@ -55,17 +55,16 @@ public final class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
super(providerInfo.getVendorName(), providerInfo.getVersion(), byte[].class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageInputStream createInputStreamInstance(Object input, boolean useCacheFile, File cacheDir) {
|
||||
if (input instanceof byte[]) {
|
||||
return new ByteArrayImageInputStream((byte[]) input);
|
||||
public ImageInputStream createInputStreamInstance(Object pInput, boolean pUseCache, File pCacheDir) {
|
||||
if (pInput instanceof byte[]) {
|
||||
return new ByteArrayImageInputStream((byte[]) pInput);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Expected input of type byte[]: " + pInput);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type byte[]: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
public String getDescription(Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a byte array";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
|
||||
interface Cache extends SeekableByteChannel {
|
||||
void flushBefore(long pos);
|
||||
}
|
||||
+1
-1
@@ -125,7 +125,7 @@ public final class DirectImageInputStream extends ImageInputStreamImpl {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// We could seek to EOF here, but the usual case is we know where the next chunk of data is
|
||||
// We could seek to EOF here, but the usual case
|
||||
|
||||
stream.close();
|
||||
super.close();
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.NonWritableChannelException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.lang.Math.max;
|
||||
import static java.nio.file.StandardOpenOption.DELETE_ON_CLOSE;
|
||||
import static java.nio.file.StandardOpenOption.READ;
|
||||
import static java.nio.file.StandardOpenOption.WRITE;
|
||||
|
||||
// Note: We could consider creating a memory-mapped version...
|
||||
// But, from java.nio.channels.FileChannel.map:
|
||||
// For most operating systems, mapping a file into memory is more
|
||||
// expensive than reading or writing a few tens of kilobytes of data via
|
||||
// the usual {@link #read read} and {@link #write write} methods. From the
|
||||
// standpoint of performance it is generally only worth mapping relatively
|
||||
// large files into memory.
|
||||
final class FileCache implements Cache {
|
||||
final static int BLOCK_SIZE = 1 << 13;
|
||||
|
||||
private final FileChannel cache;
|
||||
private final ReadableByteChannel channel;
|
||||
|
||||
// TODO: Perhaps skip this constructor?
|
||||
FileCache(InputStream stream, File cacheDir) throws IOException {
|
||||
// Stream will be closed with channel, documented behavior
|
||||
this(Channels.newChannel(notNull(stream, "stream")), cacheDir);
|
||||
}
|
||||
|
||||
public FileCache(ReadableByteChannel channel, File cacheDir) throws IOException {
|
||||
this.channel = notNull(channel, "channel");
|
||||
isTrue(cacheDir == null || cacheDir.isDirectory(), cacheDir, "%s is not a directory");
|
||||
|
||||
// Create a temp file to hold our cache,
|
||||
// will be deleted when this channel is closed, as we close the cache
|
||||
Path cacheFile = cacheDir == null
|
||||
? Files.createTempFile("imageio", ".tmp")
|
||||
: Files.createTempFile(cacheDir.toPath(), "imageio", ".tmp");
|
||||
|
||||
cache = FileChannel.open(cacheFile, DELETE_ON_CLOSE, READ, WRITE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
void fetch() throws IOException {
|
||||
while (cache.position() >= cache.size() && cache.transferFrom(channel, cache.size(), max(cache.position() - cache.size(), BLOCK_SIZE)) > 0) {
|
||||
// Continue transfer...
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
cache.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dest) throws IOException {
|
||||
fetch();
|
||||
|
||||
if (cache.position() >= cache.size()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cache.read(dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
return cache.position();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position(long newPosition) throws IOException {
|
||||
cache.position(newPosition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
// We could allow the size to grow, but that means the stream cannot rely on this size, so we'll just pretend we don't know...
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src) {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate(long size) {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override public void flushBefore(long pos) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.NonWritableChannelException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
final class MemoryCache implements Cache {
|
||||
|
||||
final static int BLOCK_SIZE = 1 << 13;
|
||||
|
||||
private static final byte[] NULL_BLOCK = new byte[0];
|
||||
|
||||
private final List<byte[]> cache = new ArrayList<>();
|
||||
private final ReadableByteChannel channel;
|
||||
|
||||
private int maxBlock = Integer.MAX_VALUE;
|
||||
private long length;
|
||||
private long position;
|
||||
private long start;
|
||||
|
||||
// TODO: Maybe get rid of this constructor, as we don't want to do this if we have a FileInputStream/FileChannel...
|
||||
MemoryCache(InputStream stream) {
|
||||
this(Channels.newChannel(notNull(stream, "stream")));
|
||||
}
|
||||
|
||||
public MemoryCache(ReadableByteChannel channel) {
|
||||
this.channel = notNull(channel, "channel");
|
||||
}
|
||||
|
||||
byte[] fetchBlock() throws IOException {
|
||||
long currPos = position;
|
||||
long index = currPos / BLOCK_SIZE;
|
||||
|
||||
if (index >= Integer.MAX_VALUE) {
|
||||
throw new IOException("Memory cache max size exceeded");
|
||||
}
|
||||
if (index > maxBlock) {
|
||||
return NULL_BLOCK;
|
||||
}
|
||||
|
||||
while (index >= cache.size()) {
|
||||
byte[] block;
|
||||
try {
|
||||
block = new byte[BLOCK_SIZE];
|
||||
}
|
||||
catch (OutOfMemoryError e) {
|
||||
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
|
||||
}
|
||||
|
||||
cache.add(block);
|
||||
int bytesRead = readBlock(block);
|
||||
length += bytesRead;
|
||||
|
||||
if (bytesRead < BLOCK_SIZE) {
|
||||
// Last block, EOF found
|
||||
maxBlock = (int) index;
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
return cache.get((int) index);
|
||||
}
|
||||
|
||||
private int readBlock(final byte[] block) throws IOException {
|
||||
ByteBuffer wrapped = ByteBuffer.wrap(block);
|
||||
|
||||
while (wrapped.hasRemaining()) {
|
||||
int count = channel.read(wrapped);
|
||||
if (count == -1) {
|
||||
// Last block, EOF found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return wrapped.position();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dest) throws IOException {
|
||||
byte[] buffer = fetchBlock();
|
||||
|
||||
if (position >= length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bufferPos = (int) (position % BLOCK_SIZE);
|
||||
int len = min(dest.remaining(), (int) min(BLOCK_SIZE - bufferPos, length - position));
|
||||
dest.put(buffer, bufferPos, len);
|
||||
|
||||
position += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position(long newPosition) throws IOException {
|
||||
if (newPosition < start) {
|
||||
throw new IOException("Seek before flush position");
|
||||
}
|
||||
|
||||
this.position = newPosition;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
// We could allow the size to grow, but that means the stream cannot rely on this size, so we'll just pretend we don't know...
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src) {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate(long size) {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBefore(long pos) {
|
||||
if (pos < start) {
|
||||
throw new IndexOutOfBoundsException("pos < flushed position");
|
||||
}
|
||||
if (pos > position) {
|
||||
throw new IndexOutOfBoundsException("pos > current position");
|
||||
}
|
||||
|
||||
int blocks = (int) (pos / BLOCK_SIZE); // Overflow guarded for in fetchBlock
|
||||
|
||||
// Clear blocks no longer needed
|
||||
for (int i = 0; i < blocks; i++) {
|
||||
cache.set(i, null);
|
||||
}
|
||||
|
||||
start = pos;
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -53,20 +53,20 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
/**
|
||||
* Creates a {@link ImageInputStream}, reading up to a maximum number of bytes from the underlying stream.
|
||||
*
|
||||
* @param stream the underlying stream
|
||||
* @param length the maximum length to read from the stream.
|
||||
* Note that {@code stream} may contain less than this maximum number of bytes.
|
||||
* @param pStream the underlying stream
|
||||
* @param pLength the maximum length to read from the stream.
|
||||
* Note that {@code pStream} may contain less than this maximum number of bytes.
|
||||
*
|
||||
* @throws IOException if {@code stream}'s position can't be determined.
|
||||
* @throws IllegalArgumentException if {@code stream == null} or {@code length < 0}
|
||||
* @throws IOException if {@code pStream}'s position can't be determined.
|
||||
* @throws IllegalArgumentException if {@code pStream == null} or {@code pLength < 0}
|
||||
*/
|
||||
public SubImageInputStream(final ImageInputStream stream, final long length) throws IOException {
|
||||
Validate.notNull(stream, "stream");
|
||||
Validate.isTrue(length >= 0, length, "length < 0: %d");
|
||||
public SubImageInputStream(final ImageInputStream pStream, final long pLength) throws IOException {
|
||||
Validate.notNull(pStream, "stream");
|
||||
Validate.isTrue(pLength >= 0, pLength, "length < 0: %d");
|
||||
|
||||
this.stream = stream;
|
||||
this.startPos = stream.getStreamPosition();
|
||||
this.length = length;
|
||||
stream = pStream;
|
||||
startPos = pStream.getStreamPosition();
|
||||
length = pLength;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
@@ -84,14 +84,14 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
}
|
||||
}
|
||||
|
||||
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 (streamPos >= length) { // Local EOF
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Safe cast, as len can never cause int overflow
|
||||
int length = (int) Math.min(len, this.length - streamPos);
|
||||
int count = stream.read(bytes, off, length);
|
||||
// Safe cast, as pLength can never cause int overflow
|
||||
int length = (int) Math.min(pLength, this.length - streamPos);
|
||||
int count = stream.read(pBytes, pOffset, length);
|
||||
|
||||
if (count >= 0) {
|
||||
streamPos += count;
|
||||
@@ -113,18 +113,18 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(final long position) throws IOException {
|
||||
if (position < getFlushedPosition()) {
|
||||
public void seek(final long pPosition) throws IOException {
|
||||
if (pPosition < getFlushedPosition()) {
|
||||
throw new IndexOutOfBoundsException("pos < flushedPosition");
|
||||
}
|
||||
|
||||
stream.seek(startPos + position);
|
||||
streamPos = position;
|
||||
stream.seek(startPos + pPosition);
|
||||
streamPos = pPosition;
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
@SuppressWarnings({"FinalizeDoesntCallSuperFinalize"})
|
||||
@Override
|
||||
protected void finalize() {
|
||||
protected void finalize() throws Throwable {
|
||||
// Empty finalizer (for improved performance; no need to call super.finalize() in this case)
|
||||
}
|
||||
}
|
||||
|
||||
+41
-14
@@ -33,7 +33,9 @@ package com.twelvemonkeys.imageio.stream;
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.stream.FileCacheImageInputStream;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -50,7 +52,7 @@ import java.util.Locale;
|
||||
* @version $Id: URLImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
|
||||
*/
|
||||
// TODO: URI instead of URL?
|
||||
public final class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public URLImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
@@ -62,28 +64,53 @@ public final class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
// TODO: Create a URI or URLImageInputStream class, with a getUR[I|L] method, to allow for multiple file formats
|
||||
// The good thing with that is that it does not clash with the built-in Sun-stuff or other people's hacks
|
||||
// The bad thing is that most people don't expect there to be an UR[I|L]ImageInputStreamSpi..
|
||||
@Override
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean useCacheFile, final File cacheDir) throws IOException {
|
||||
if (input instanceof URL) {
|
||||
URL url = (URL) input;
|
||||
public ImageInputStream createInputStreamInstance(final Object pInput, final boolean pUseCache, final File pCacheDir) throws IOException {
|
||||
if (pInput instanceof URL) {
|
||||
URL url = (URL) pInput;
|
||||
|
||||
// Special case for file protocol, a lot faster than FileCacheImageInputStream
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
try {
|
||||
return new BufferedChannelImageInputStream(new File(url.toURI()));
|
||||
return new BufferedFileImageInputStream(new File(url.toURI()));
|
||||
}
|
||||
catch (URISyntaxException shouldNeverHappen) {
|
||||
// This should never happen, but if it does, we'll fall back to using the stream
|
||||
shouldNeverHappen.printStackTrace();
|
||||
catch (URISyntaxException ignore) {
|
||||
// This should never happen, but if it does, we'll fall back to using the stream
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise revert to cached
|
||||
InputStream urlStream = url.openStream();
|
||||
return new BufferedChannelImageInputStream(useCacheFile ? new FileCache(urlStream, cacheDir) : new MemoryCache(urlStream));
|
||||
final InputStream urlStream = url.openStream();
|
||||
if (pUseCache) {
|
||||
return new FileCacheImageInputStream(urlStream, pCacheDir) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return new MemoryCacheImageInputStream(urlStream) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Expected input of type URL: " + pInput);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type URL: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,7 +118,7 @@ public final class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getDescription(final Locale locale) {
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a URL";
|
||||
}
|
||||
}
|
||||
|
||||
+39
-90
@@ -30,15 +30,22 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.MultiPixelPackedSampleModel;
|
||||
import java.awt.image.SampleModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
|
||||
|
||||
/**
|
||||
* Factory class for creating {@code ImageTypeSpecifier}s.
|
||||
* Fixes some subtle bugs in {@code ImageTypeSpecifier}'s factory methods, but
|
||||
@@ -51,52 +58,28 @@ import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
*/
|
||||
public final class ImageTypeSpecifiers {
|
||||
|
||||
private static final ImageTypeSpecifier TYPE_INT_RGB = createPackedOddBits(ColorSpace.getInstance(ColorSpace.CS_sRGB), 24,
|
||||
0xFF0000,
|
||||
0x00FF00,
|
||||
0x0000FF,
|
||||
0x0,
|
||||
DataBuffer.TYPE_INT,
|
||||
false);
|
||||
private static final ImageTypeSpecifier TYPE_INT_BGR = createPackedOddBits(ColorSpace.getInstance(ColorSpace.CS_sRGB), 24,
|
||||
0x0000FF,
|
||||
0x00FF00,
|
||||
0xFF0000,
|
||||
0x0,
|
||||
DataBuffer.TYPE_INT,
|
||||
false);
|
||||
private static final ImageTypeSpecifier TYPE_USHORT_565_RGB = createPackedOddBits(ColorSpace.getInstance(ColorSpace.CS_sRGB), 16,
|
||||
0xF800,
|
||||
0x07E0,
|
||||
0x001F,
|
||||
0x0,
|
||||
DataBuffer.TYPE_USHORT,
|
||||
false);
|
||||
private static final ImageTypeSpecifier TYPE_USHORT_555_RGB = createPackedOddBits(ColorSpace.getInstance(ColorSpace.CS_sRGB), 15,
|
||||
0x7C00,
|
||||
0x03E0,
|
||||
0x001F,
|
||||
0x0,
|
||||
DataBuffer.TYPE_USHORT,
|
||||
false);
|
||||
|
||||
private ImageTypeSpecifiers() {}
|
||||
|
||||
public static ImageTypeSpecifier createFromBufferedImageType(final int bufferedImageType) {
|
||||
switch (bufferedImageType) {
|
||||
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround for the INT_RGB and USHORT types
|
||||
case BufferedImage.TYPE_INT_RGB:
|
||||
return TYPE_INT_RGB;
|
||||
|
||||
case BufferedImage.TYPE_INT_BGR:
|
||||
return TYPE_INT_BGR;
|
||||
|
||||
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround for the USHORT types
|
||||
case BufferedImage.TYPE_USHORT_565_RGB:
|
||||
return TYPE_USHORT_565_RGB;
|
||||
return createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0xF800,
|
||||
0x07E0,
|
||||
0x001F,
|
||||
0x0,
|
||||
DataBuffer.TYPE_USHORT,
|
||||
false);
|
||||
|
||||
case BufferedImage.TYPE_USHORT_555_RGB:
|
||||
return TYPE_USHORT_555_RGB;
|
||||
|
||||
return createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x7C00,
|
||||
0x03E0,
|
||||
0x001F,
|
||||
0x0,
|
||||
DataBuffer.TYPE_USHORT,
|
||||
false);
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -107,41 +90,23 @@ public final class ImageTypeSpecifiers {
|
||||
final int redMask, final int greenMask,
|
||||
final int blueMask, final int alphaMask,
|
||||
final int transferType, boolean isAlphaPremultiplied) {
|
||||
int bits = calculateRequiredBits(redMask | greenMask | blueMask | alphaMask);
|
||||
if (bits != 32) {
|
||||
if (transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT) {
|
||||
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround for BYTE/USHORT types
|
||||
return createPackedOddBits(colorSpace, bits, redMask, greenMask, blueMask, alphaMask, transferType, isAlphaPremultiplied);
|
||||
notNull(colorSpace, "colorSpace");
|
||||
isTrue(colorSpace.getType() == ColorSpace.TYPE_RGB, colorSpace, "ColorSpace must be TYPE_RGB");
|
||||
isTrue(redMask != 0 || greenMask != 0 || blueMask != 0 || alphaMask != 0, "No mask has at least 1 bit set");
|
||||
|
||||
int bits = transferType == DataBuffer.TYPE_BYTE ? 8 : 16;
|
||||
|
||||
ColorModel colorModel = new DirectColorModel(colorSpace, bits, redMask, greenMask, blueMask, alphaMask,
|
||||
isAlphaPremultiplied, transferType);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
|
||||
return ImageTypeSpecifier.createPacked(colorSpace, redMask, greenMask, blueMask, alphaMask, transferType, isAlphaPremultiplied);
|
||||
}
|
||||
|
||||
private static int calculateRequiredBits(int mask) {
|
||||
// See https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
|
||||
int r = 1;
|
||||
|
||||
while ((mask >>>= 1) != 0) {
|
||||
r++;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static ImageTypeSpecifier createPackedOddBits(final ColorSpace colorSpace, int bits,
|
||||
final int redMask, final int greenMask,
|
||||
final int blueMask, final int alphaMask,
|
||||
final int transferType, boolean isAlphaPremultiplied) {
|
||||
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround
|
||||
notNull(colorSpace, "colorSpace");
|
||||
isTrue(colorSpace.getType() == ColorSpace.TYPE_RGB, colorSpace, "ColorSpace must be TYPE_RGB");
|
||||
isTrue(redMask != 0 || greenMask != 0 || blueMask != 0 || alphaMask != 0, "No mask has at least 1 bit set");
|
||||
|
||||
ColorModel colorModel = new DirectColorModel(colorSpace, bits, redMask, greenMask, blueMask, alphaMask,
|
||||
isAlphaPremultiplied, transferType);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createInterleaved(final ColorSpace colorSpace,
|
||||
final int[] bandOffsets,
|
||||
final int dataType,
|
||||
@@ -270,20 +235,4 @@ public final class ImageTypeSpecifiers {
|
||||
ColorModel colorModel = new DiscreteAlphaIndexColorModel(pColorModel, extraSamples, hasAlpha);
|
||||
return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createFromRenderedImage(RenderedImage image) {
|
||||
if (image == null) {
|
||||
throw new IllegalArgumentException("image == null!");
|
||||
}
|
||||
|
||||
if (image instanceof BufferedImage) {
|
||||
int bufferedImageType = ((BufferedImage) image).getType();
|
||||
|
||||
if (bufferedImageType != BufferedImage.TYPE_CUSTOM) {
|
||||
return createFromBufferedImageType(bufferedImageType);
|
||||
}
|
||||
}
|
||||
|
||||
return new ImageTypeSpecifier(image);
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
com.twelvemonkeys.imageio.stream.BufferedFileImageInputStreamSpi
|
||||
com.twelvemonkeys.imageio.stream.BufferedRAFImageInputStreamSpi
|
||||
com.twelvemonkeys.imageio.stream.BufferedInputStreamImageInputStreamSpi
|
||||
# Use SPI loading as a hook for early profile activation
|
||||
com.twelvemonkeys.imageio.color.ProfileDeferralActivator$Spi
|
||||
|
||||
-332
@@ -1,332 +0,0 @@
|
||||
package com.twelvemonkeys.imageio;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport.ColorSpaceType;
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport.ImageOrientation;
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport.PlanarConfiguration;
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport.SubimageInterpretation;
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport.TextEntry;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.image.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static com.twelvemonkeys.imageio.StandardImageMetadataSupport.builder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class StandardImageMetadataSupportTest {
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void createNullBuilder() {
|
||||
new StandardImageMetadataSupport(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void createNullType() {
|
||||
new StandardImageMetadataSupport(builder(null));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void builderNullType() {
|
||||
builder(null).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createValid() {
|
||||
IIOMetadata metadata = new StandardImageMetadataSupport(builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB)));
|
||||
assertNotNull(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builderValid() {
|
||||
IIOMetadata metadata = builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB))
|
||||
.build();
|
||||
|
||||
assertNotNull(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compressionValuesUnspecified() {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.build();
|
||||
|
||||
assertNull(metadata.getStandardCompressionNode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compressionValuesNone() {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withCompressionTypeName("nOnE") // Case-insensitive
|
||||
.build();
|
||||
|
||||
assertNull(metadata.getStandardCompressionNode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compressionValuesName() {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withCompressionTypeName("foo")
|
||||
.build();
|
||||
|
||||
IIOMetadataNode compressionNode = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compressionNode);
|
||||
|
||||
IIOMetadataNode compressionName = (IIOMetadataNode) compressionNode.getElementsByTagName("CompressionTypeName").item(0);
|
||||
assertEquals("foo", compressionName.getAttribute("value"));
|
||||
|
||||
// Defaults to lossless true
|
||||
IIOMetadataNode compressionLossless = (IIOMetadataNode) compressionNode.getElementsByTagName("Lossless").item(0);
|
||||
assertEquals("TRUE", compressionLossless.getAttribute("value"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void withCompressionLossyIllegal() {
|
||||
builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withCompressionLossless(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compressionValuesLossy() {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withCompressionTypeName("bar")
|
||||
.withCompressionLossless(false)
|
||||
.build();
|
||||
|
||||
IIOMetadataNode compressionNode = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compressionNode);
|
||||
|
||||
IIOMetadataNode compressionName = (IIOMetadataNode) compressionNode.getElementsByTagName("CompressionTypeName").item(0);
|
||||
assertEquals("bar", compressionName.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode compressionLossless = (IIOMetadataNode) compressionNode.getElementsByTagName("Lossless").item(0);
|
||||
assertEquals("FALSE", compressionLossless.getAttribute("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withDocumentValuesDefault() {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.build();
|
||||
|
||||
IIOMetadataNode documentNode = metadata.getStandardDocumentNode();
|
||||
assertNull(documentNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withDocumentValues() {
|
||||
Calendar creationTime = Calendar.getInstance();
|
||||
creationTime.set(2022, Calendar.SEPTEMBER, 8, 14, 5, 0);
|
||||
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withFormatVersion("42")
|
||||
.withDocumentCreationTime(creationTime)
|
||||
.build();
|
||||
|
||||
IIOMetadataNode documentNode = metadata.getStandardDocumentNode();
|
||||
assertNotNull(documentNode);
|
||||
|
||||
IIOMetadataNode formatVersion = (IIOMetadataNode) documentNode.getElementsByTagName("FormatVersion").item(0);
|
||||
assertEquals("42", formatVersion.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode imageCreationTime = (IIOMetadataNode) documentNode.getElementsByTagName("ImageCreationTime").item(0);
|
||||
assertEquals("2022", imageCreationTime.getAttribute("year"));
|
||||
assertEquals("9", imageCreationTime.getAttribute("month"));
|
||||
assertEquals("8", imageCreationTime.getAttribute("day"));
|
||||
assertEquals("14", imageCreationTime.getAttribute("hour"));
|
||||
assertEquals("5", imageCreationTime.getAttribute("minute"));
|
||||
assertEquals("0", imageCreationTime.getAttribute("second"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withTextValuesDefault() {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.build();
|
||||
|
||||
IIOMetadataNode textNode = metadata.getStandardTextNode();
|
||||
assertNull(textNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withTextValuesSingle() {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withTextEntry("foo", "bar")
|
||||
.build();
|
||||
|
||||
IIOMetadataNode textNode = metadata.getStandardTextNode();
|
||||
assertNotNull(textNode);
|
||||
|
||||
IIOMetadataNode textEntry = (IIOMetadataNode) textNode.getElementsByTagName("TextEntry").item(0);
|
||||
assertEquals("foo", textEntry.getAttribute("keyword"));
|
||||
assertEquals("bar", textEntry.getAttribute("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withTextValuesMap() {
|
||||
Map<String, String> entries = new HashMap<>();
|
||||
entries.put("foo", "bar");
|
||||
entries.put("bar", "xyzzy");
|
||||
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withTextEntries(entries)
|
||||
.build();
|
||||
|
||||
IIOMetadataNode textNode = metadata.getStandardTextNode();
|
||||
assertNotNull(textNode);
|
||||
|
||||
NodeList textEntries = textNode.getElementsByTagName("TextEntry");
|
||||
assertEquals(entries.size(), textEntries.getLength());
|
||||
|
||||
int i = 0;
|
||||
for (Entry<String, String> entry : entries.entrySet()) {
|
||||
IIOMetadataNode textEntry = (IIOMetadataNode) textEntries.item(i);
|
||||
assertEquals(entry.getKey(), textEntry.getAttribute("keyword"));
|
||||
assertEquals(entry.getValue(), textEntry.getAttribute("value"));
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withTextValuesList() {
|
||||
List<TextEntry> entries = Arrays.asList(
|
||||
new TextEntry(null, "foo"), // No key allowed
|
||||
new TextEntry("foo", "bar"),
|
||||
new TextEntry("bar", "xyzzy"),
|
||||
new TextEntry("bar", "nothing happens..."), // Duplicates allowed
|
||||
new TextEntry("everything", "válüè", "unknown", "UTF-8", "zip")
|
||||
);
|
||||
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withTextEntries(entries)
|
||||
.build();
|
||||
|
||||
IIOMetadataNode textNode = metadata.getStandardTextNode();
|
||||
assertNotNull(textNode);
|
||||
|
||||
NodeList textEntries = textNode.getElementsByTagName("TextEntry");
|
||||
assertEquals(entries.size(), textEntries.getLength());
|
||||
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
TextEntry entry = entries.get(i);
|
||||
IIOMetadataNode textEntry = (IIOMetadataNode) textEntries.item(i);
|
||||
|
||||
assertAttributeEqualOrAbsent(entry.keyword, textEntry, "keyword");
|
||||
|
||||
assertEquals(entry.value, textEntry.getAttribute("value"));
|
||||
|
||||
assertAttributeEqualOrAbsent(entry.language, textEntry, "language");
|
||||
assertAttributeEqualOrAbsent(entry.encoding, textEntry, "encoding");
|
||||
assertAttributeEqualOrAbsent(entry.compression, textEntry, "compression");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertAttributeEqualOrAbsent(final String expectedValue, IIOMetadataNode node, final String attribute) {
|
||||
if (expectedValue != null) {
|
||||
assertEquals(expectedValue, node.getAttribute(attribute));
|
||||
}
|
||||
else {
|
||||
assertFalse(node.hasAttribute(attribute));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withPlanarColorspaceType() {
|
||||
// See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
|
||||
Collection<String> allowedValues = Arrays.asList(
|
||||
"XYZ", "Lab", "Luv", "YCbCr", "Yxy", "YCCK", "PhotoYCC",
|
||||
"RGB", "GRAY", "HSV", "HLS", "CMYK", "CMY",
|
||||
"2CLR", "3CLR", "4CLR", "5CLR", "6CLR", "7CLR", "8CLR",
|
||||
"9CLR", "ACLR", "BCLR", "CCLR", "DCLR", "ECLR", "FCLR"
|
||||
);
|
||||
|
||||
for (ColorSpaceType value : ColorSpaceType.values()) {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withColorSpaceType(value)
|
||||
.build();
|
||||
|
||||
assertNotNull(metadata);
|
||||
|
||||
IIOMetadataNode documentNode = metadata.getStandardChromaNode();
|
||||
assertNotNull(documentNode);
|
||||
|
||||
IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("ColorSpaceType").item(0);
|
||||
assertEquals(value.toString(), subImageInterpretation.getAttribute("name")); // Format oddity: Why is this not "value"?
|
||||
assertTrue(allowedValues.contains(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withPlanarConfiguration() {
|
||||
// See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
|
||||
Collection<String> allowedValues = Arrays.asList("PixelInterleaved", "PlaneInterleaved", "LineInterleaved", "TileInterleaved");
|
||||
|
||||
for (PlanarConfiguration value : PlanarConfiguration.values()) {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR))
|
||||
.withPlanarConfiguration(value)
|
||||
.build();
|
||||
|
||||
assertNotNull(metadata);
|
||||
|
||||
IIOMetadataNode documentNode = metadata.getStandardDataNode();
|
||||
assertNotNull(documentNode);
|
||||
|
||||
IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("PlanarConfiguration").item(0);
|
||||
assertEquals(value.toString(), subImageInterpretation.getAttribute("value"));
|
||||
assertTrue(allowedValues.contains(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withImageOrientation() {
|
||||
// See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
|
||||
Collection<String> allowedValues = Arrays.asList("Normal", "Rotate90", "Rotate180", "Rotate270", "FlipH", "FlipV", "FlipHRotate90", "FlipVRotate90");
|
||||
|
||||
for (ImageOrientation value : ImageOrientation.values()) {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
|
||||
.withOrientation(value)
|
||||
.build();
|
||||
|
||||
assertNotNull(metadata);
|
||||
|
||||
IIOMetadataNode documentNode = metadata.getStandardDimensionNode();
|
||||
assertNotNull(documentNode);
|
||||
|
||||
IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("ImageOrientation").item(0);
|
||||
assertEquals(value.toString(), subImageInterpretation.getAttribute("value"));
|
||||
assertTrue(allowedValues.contains(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withSubimageInterpretation() {
|
||||
// See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
|
||||
Collection<String> allowedValues = Arrays.asList(
|
||||
"Standalone", "SinglePage", "FullResolution", "ReducedResolution", "PyramidLayer",
|
||||
"Preview", "VolumeSlice", "ObjectView", "Panorama", "AnimationFrame",
|
||||
"TransparencyMask", "CompositingLayer", "SpectralSlice", "Unknown"
|
||||
);
|
||||
|
||||
for (SubimageInterpretation value : SubimageInterpretation.values()) {
|
||||
StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB))
|
||||
.withSubimageInterpretation(value)
|
||||
.build();
|
||||
|
||||
assertNotNull(metadata);
|
||||
|
||||
IIOMetadataNode documentNode = metadata.getStandardDocumentNode();
|
||||
assertNotNull(documentNode);
|
||||
|
||||
IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("SubimageInterpretation").item(0);
|
||||
assertEquals(value.toString(), subImageInterpretation.getAttribute("value"));
|
||||
assertTrue(allowedValues.contains(value.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
-434
@@ -1,434 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamTestCase
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
// TODO: Remove this test, and instead test the disk cache directly!
|
||||
public class BufferedChannelImageInputStreamFileCacheTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
private InputStream randomDataToInputStream(byte[] data) {
|
||||
random.nextBytes(data);
|
||||
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(new ByteArrayInputStream(new byte[0]), null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullStream() throws IOException {
|
||||
try {
|
||||
new FileCache((InputStream) null, null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("stream"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullChannel() throws IOException {
|
||||
try {
|
||||
new FileCache((ReadableByteChannel) null, null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("channel"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
}
|
||||
|
||||
assertEquals("Wrong data read", -1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadArray() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
|
||||
assertEquals("Wrong data read", -1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSkip() throws IOException {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[7];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSeek() throws IOException {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[9];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
// Read backwards
|
||||
long newPos = data.length - result.length - i * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadOutsideDataSeek0Read() throws IOException {
|
||||
byte[] data = new byte[256];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] buffer = new byte[data.length * 2];
|
||||
stream.read(buffer);
|
||||
stream.seek(0);
|
||||
assertNotEquals(-1, stream.read());
|
||||
assertNotEquals(-1, stream.read(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandomOffset() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2 % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShort() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadLong() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekPastEOF() throws IOException {
|
||||
byte[] bytes = new byte[9];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClose() throws IOException {
|
||||
// Create wrapper stream
|
||||
Cache cache = mock(Cache.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(cache);
|
||||
|
||||
stream.close();
|
||||
verify(cache, only()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
|
||||
// See #606 for details.
|
||||
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
|
||||
// Ie: Relies on read to return all bytes at once, without blocking
|
||||
int size = BufferedChannelImageInputStream.DEFAULT_BUFFER_SIZE * 7;
|
||||
byte[] bytes = new byte[size];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
byte[] result = new byte[size];
|
||||
int head = stream.read(result, 0, 12); // Provoke a buffered read
|
||||
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
|
||||
|
||||
assertEquals(size, len + head);
|
||||
assertArrayEquals(bytes, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
-452
@@ -1,452 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamTestCase
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
// TODO: Remove this test, and instead test the memory cache directly!
|
||||
public class BufferedChannelImageInputStreamMemoryCacheTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
private InputStream randomDataToInputStream(byte[] data) {
|
||||
random.nextBytes(data);
|
||||
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(new ByteArrayInputStream(new byte[0])))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullStream() {
|
||||
try {
|
||||
new MemoryCache((InputStream) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("stream"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullChannel() {
|
||||
try {
|
||||
new MemoryCache((ReadableByteChannel) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("channel"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
}
|
||||
|
||||
assertEquals("Wrong data read", -1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadArray() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
|
||||
assertEquals("Wrong data read", -1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSkip() throws IOException {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[7];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSeek() throws IOException {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[9];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
// Read backwards
|
||||
long newPos = data.length - result.length - i * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadOutsideDataSeek0Read() throws IOException {
|
||||
byte[] data = new byte[256];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] buffer = new byte[data.length * 2];
|
||||
stream.read(buffer);
|
||||
stream.seek(0);
|
||||
assertNotEquals(-1, stream.read());
|
||||
assertNotEquals(-1, stream.read(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandomOffset() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2 % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShort() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadLong() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekPastEOF() throws IOException {
|
||||
byte[] bytes = new byte[9];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void testSeekWayPastEOFShouldNotThrowOOME() throws IOException {
|
||||
byte[] bytes = new byte[9];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
stream.seek(Integer.MAX_VALUE * 4L * 512L); // ~4 TB
|
||||
|
||||
assertEquals(-1, stream.read()); // No OOME should happen...
|
||||
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClose() throws IOException {
|
||||
// Create wrapper stream
|
||||
Cache cache = mock(Cache.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(cache);
|
||||
|
||||
stream.close();
|
||||
verify(cache, only()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
|
||||
// See #606 for details.
|
||||
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
|
||||
// Ie: Relies on read to return all bytes at once, without blocking
|
||||
int size = BufferedChannelImageInputStream.DEFAULT_BUFFER_SIZE * 7;
|
||||
byte[] bytes = new byte[size];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) {
|
||||
byte[] result = new byte[size];
|
||||
int head = stream.read(result, 0, 12); // Provoke a buffered read
|
||||
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
|
||||
|
||||
assertEquals(size, len + head);
|
||||
assertArrayEquals(bytes, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
-431
@@ -1,431 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamTestCase
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
public class BufferedChannelImageInputStreamTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
private File randomDataToFile(byte[] data) throws IOException {
|
||||
random.nextBytes(data);
|
||||
|
||||
File file = File.createTempFile("read", ".tmp");
|
||||
Files.write(file.toPath(), data);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(File.createTempFile("empty", ".tmp")))) {
|
||||
assertEquals("Data length should be same as stream length", 0, stream.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullFileInputStream() {
|
||||
try {
|
||||
new BufferedChannelImageInputStream((FileInputStream) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("inputstream"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullByteChannel() {
|
||||
try {
|
||||
new BufferedChannelImageInputStream((SeekableByteChannel) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("channel"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadArray() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSkip() throws IOException {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[7];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSeek() throws IOException {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[9];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
// Read backwards
|
||||
long newPos = stream.length() - result.length - i * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadOutsideDataSeek0Read() throws IOException {
|
||||
byte[] data = new byte[256];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] buffer = new byte[data.length * 2];
|
||||
stream.read(buffer);
|
||||
stream.seek(0);
|
||||
assertNotEquals(-1, stream.read());
|
||||
assertNotEquals(-1, stream.read(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandomOffset() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2 % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShort() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadLong() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekPastEOF() throws IOException {
|
||||
byte[] bytes = new byte[9];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseChannel() throws IOException {
|
||||
SeekableByteChannel channel = mock(SeekableByteChannel.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(channel);
|
||||
|
||||
stream.close();
|
||||
verify(channel, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
|
||||
// See #606 for details.
|
||||
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
|
||||
// Ie: Relies on read to return all bytes at once, without blocking
|
||||
int size = BufferedChannelImageInputStream.DEFAULT_BUFFER_SIZE * 7;
|
||||
byte[] bytes = new byte[size];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileInputStream(file))) {
|
||||
byte[] result = new byte[size];
|
||||
int head = stream.read(result, 0, 12); // Provoke a buffered read
|
||||
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
|
||||
|
||||
assertEquals(size, len + head);
|
||||
assertArrayEquals(bytes, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -45,9 +45,7 @@ import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamTestCase
|
||||
@@ -56,7 +54,6 @@ import static org.mockito.Mockito.verify;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
@Deprecated
|
||||
public class BufferedFileImageInputStreamTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
@@ -75,7 +72,6 @@ public class BufferedFileImageInputStreamTest {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Test
|
||||
public void testCreateNullFile() throws IOException {
|
||||
try {
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* BufferedInputStreamImageInputStreamSpiTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedInputStreamImageInputStreamSpiTest.java,v 1.0 08/09/2022 haraldk Exp$
|
||||
*/
|
||||
public class BufferedFileInputStreamImageInputStreamSpiTest extends ImageInputStreamSpiTest<InputStream> {
|
||||
@Override
|
||||
protected ImageInputStreamSpi createProvider() {
|
||||
return new BufferedInputStreamImageInputStreamSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream createInput() throws IOException {
|
||||
return Files.newInputStream(File.createTempFile("test-", ".tst").toPath());
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* BufferedInputStreamImageInputStreamSpiTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedInputStreamImageInputStreamSpiTest.java,v 1.0 08/09/2022 haraldk Exp$
|
||||
*/
|
||||
public class BufferedInputStreamImageInputStreamSpiTest extends ImageInputStreamSpiTest<InputStream> {
|
||||
@Override
|
||||
protected ImageInputStreamSpi createProvider() {
|
||||
return new BufferedInputStreamImageInputStreamSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream createInput() {
|
||||
return new ByteArrayInputStream(new byte[0]);
|
||||
}
|
||||
}
|
||||
+37
-71
@@ -30,15 +30,20 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
public class ImageTypeSpecifiersTest {
|
||||
|
||||
@@ -65,19 +70,12 @@ public class ImageTypeSpecifiersTest {
|
||||
ImageTypeSpecifier expected;
|
||||
|
||||
switch (type) {
|
||||
// Special handling for INT_RGB and BGR, due to bug in ImageTypeSpecifier for these types (DirectColorModel is 32 bits)
|
||||
case BufferedImage.TYPE_INT_RGB:
|
||||
expected = createPacked(sRGB, 24, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, 0, DataBuffer.TYPE_INT, false);
|
||||
break;
|
||||
case BufferedImage.TYPE_INT_BGR:
|
||||
expected = createPacked(sRGB, 24, DCM_BGR_RED_MASK, DCM_BGR_GRN_MASK, DCM_BGR_BLU_MASK, 0, DataBuffer.TYPE_INT, false);
|
||||
break;
|
||||
// Special handling for USHORT_565 and 555, due to bug in ImageTypeSpecifier for these types (DirectColorModel is 32 bits)
|
||||
case BufferedImage.TYPE_USHORT_565_RGB:
|
||||
expected = createPacked(sRGB, 16, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false);
|
||||
expected = createPacked(sRGB, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false);
|
||||
break;
|
||||
case BufferedImage.TYPE_USHORT_555_RGB:
|
||||
expected = createPacked(sRGB, 15, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false);
|
||||
expected = createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false);
|
||||
break;
|
||||
default:
|
||||
expected = ImageTypeSpecifier.createFromBufferedImageType(type);
|
||||
@@ -88,24 +86,12 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePacked24() {
|
||||
public void testCreatePacked32() {
|
||||
// TYPE_INT_RGB
|
||||
assertEquals(
|
||||
createPacked(sRGB, 24, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, 0, DataBuffer.TYPE_INT, false),
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, 0, DataBuffer.TYPE_INT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, 0, DataBuffer.TYPE_INT, false)
|
||||
);
|
||||
// TYPE_INT_BGR
|
||||
assertEquals(
|
||||
createPacked(sRGB, 24, DCM_BGR_RED_MASK, DCM_BGR_GRN_MASK, DCM_BGR_BLU_MASK, 0, DataBuffer.TYPE_INT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_BGR_RED_MASK, DCM_BGR_GRN_MASK, DCM_BGR_BLU_MASK, 0, DataBuffer.TYPE_INT, false)
|
||||
);
|
||||
|
||||
// Extra: Make sure color models bits is actually 24 (ImageTypeSpecifier equivalent returns 32)
|
||||
assertEquals(24, ImageTypeSpecifiers.createPacked(sRGB, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, 0, DataBuffer.TYPE_INT, false).getColorModel().getPixelSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePacked32() {
|
||||
// TYPE_INT_ARGB
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, DCM_ALPHA_MASK, DataBuffer.TYPE_INT, false),
|
||||
@@ -116,36 +102,35 @@ public class ImageTypeSpecifiersTest {
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, DCM_ALPHA_MASK, DataBuffer.TYPE_INT, true),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, DCM_ALPHA_MASK, DataBuffer.TYPE_INT, true)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePacked15() {
|
||||
// TYPE_USHORT_555_RGB
|
||||
// TYPE_INT_BGR
|
||||
assertEquals(
|
||||
createPacked(sRGB, 15, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false)
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_BGR_RED_MASK, DCM_BGR_GRN_MASK, DCM_BGR_BLU_MASK, 0, DataBuffer.TYPE_INT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_BGR_RED_MASK, DCM_BGR_GRN_MASK, DCM_BGR_BLU_MASK, 0, DataBuffer.TYPE_INT, false)
|
||||
);
|
||||
// "SHORT 555 RGB" (impossible, only BYTE, USHORT, INT supported)
|
||||
|
||||
// Extra: Make sure color models bits is actually 15 (ImageTypeSpecifier equivalent returns 32)
|
||||
assertEquals(15, ImageTypeSpecifiers.createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false).getColorModel().getPixelSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePacked16() {
|
||||
// TYPE_USHORT_555_RGB
|
||||
assertEquals(
|
||||
createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false)
|
||||
);
|
||||
// "SHORT 555 RGB" (impossible, only BYTE, USHORT, INT supported)
|
||||
|
||||
// TYPE_USHORT_565_RGB
|
||||
assertEquals(
|
||||
createPacked(sRGB, 16, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
createPacked(sRGB, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false)
|
||||
);
|
||||
// "USHORT 4444 ARGB"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 16,0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, false),
|
||||
createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, false)
|
||||
);
|
||||
// "USHORT 4444 ARGB PRE"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 16, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, true),
|
||||
createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, true),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, true)
|
||||
);
|
||||
|
||||
@@ -157,17 +142,17 @@ public class ImageTypeSpecifiersTest {
|
||||
public void testCreatePacked8() {
|
||||
// "BYTE 332 RGB"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 8, 0xe0, 0x1c, 0x03, 0x0, DataBuffer.TYPE_BYTE, false),
|
||||
createPacked(sRGB, 0xe0, 0x1c, 0x03, 0x0, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xe0, 0x1c, 0x3, 0x0, DataBuffer.TYPE_BYTE, false)
|
||||
);
|
||||
// "BYTE 2222 ARGB"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 8, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, false),
|
||||
createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, false)
|
||||
);
|
||||
// "BYTE 2222 ARGB PRE"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 8, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, true),
|
||||
createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, true),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, true)
|
||||
);
|
||||
|
||||
@@ -175,12 +160,15 @@ public class ImageTypeSpecifiersTest {
|
||||
assertEquals(8, ImageTypeSpecifiers.createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, false).getColorModel().getPixelSize());
|
||||
}
|
||||
|
||||
private ImageTypeSpecifier createPacked(final ColorSpace colorSpace, final int bits,
|
||||
private ImageTypeSpecifier createPacked(final ColorSpace colorSpace,
|
||||
final int redMask, final int greenMask, final int blueMask, final int alphaMask,
|
||||
final int transferType, final boolean isAlphaPremultiplied) {
|
||||
Validate.isTrue(transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT || transferType == DataBuffer.TYPE_INT, transferType, "transferType: %s");
|
||||
Validate.isTrue(transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT, transferType, "transferType: %s");
|
||||
|
||||
ColorModel colorModel = new DirectColorModel(colorSpace, bits, redMask, greenMask, blueMask, alphaMask, isAlphaPremultiplied, transferType);
|
||||
int bits = transferType == DataBuffer.TYPE_BYTE ? 8 : 16;
|
||||
|
||||
ColorModel colorModel =
|
||||
new DirectColorModel(colorSpace, bits, redMask, greenMask, blueMask, alphaMask, isAlphaPremultiplied, transferType);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
@@ -728,28 +716,6 @@ public class ImageTypeSpecifiersTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromBufferedImageTypeShouldEqualConstructor() {
|
||||
for (int type = BufferedImage.TYPE_INT_RGB; type < BufferedImage.TYPE_BYTE_INDEXED; type++) {
|
||||
BufferedImage image = new BufferedImage(1, 1, type);
|
||||
ImageTypeSpecifier fromConstructor = new ImageTypeSpecifier(image);
|
||||
ImageTypeSpecifier fromType = ImageTypeSpecifiers.createFromBufferedImageType(type);
|
||||
|
||||
assertEquals(fromConstructor.getColorModel(), fromType.getColorModel());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromRenderedImageShouldEqualConstructor() {
|
||||
for (int type = BufferedImage.TYPE_INT_RGB; type < BufferedImage.TYPE_BYTE_INDEXED; type++) {
|
||||
BufferedImage image = new BufferedImage(1, 1, type);
|
||||
ImageTypeSpecifier fromConstructor = new ImageTypeSpecifier(image);
|
||||
ImageTypeSpecifier fromImage = ImageTypeSpecifiers.createFromRenderedImage(image);
|
||||
|
||||
assertEquals(fromConstructor.getColorModel(), fromImage.getColorModel());
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] createByteLut(final int count) {
|
||||
byte[] lut = new byte[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||
|
||||
+9
-3
@@ -40,8 +40,11 @@ import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
@@ -241,7 +244,10 @@ public final class HDRImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||
return new HDRMetadata(getRawImageType(imageIndex), header);
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return new HDRMetadata(header);
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
|
||||
+107
-9
@@ -1,19 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
public class HDRMetadata extends StandardImageMetadataSupport {
|
||||
public HDRMetadata(ImageTypeSpecifier type, HDRHeader header) {
|
||||
super(builder(type)
|
||||
.withCompressionTypeName("RLE")
|
||||
.withTextEntry("Software", header.getSoftware()));
|
||||
final class HDRMetadata extends AbstractMetadata {
|
||||
private final HDRHeader header;
|
||||
|
||||
HDRMetadata(final HDRHeader header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
csType.setAttribute("name", "RGB");
|
||||
// TODO: Support XYZ
|
||||
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
numChannels.setAttribute("value", "3");
|
||||
chroma.appendChild(numChannels);
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
chroma.appendChild(blackIsZero);
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
// No compression
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", "RLE");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// For HDR, the stored sample data is UnsignedIntegral and data is 4 channels (RGB+Exp),
|
||||
// but decoded to Real (float) 3 chanel RGB
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
@@ -28,4 +92,38 @@ public class HDRMetadata extends StandardImageMetadataSupport {
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
// TODO: Support other orientations
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientation.setAttribute("value", "Normal");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// No document node
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
if (header.getSoftware() != null) {
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
||||
textEntry.setAttribute("keyword", "Software");
|
||||
textEntry.setAttribute("value", header.getSoftware());
|
||||
text.appendChild(textEntry);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// No tiling
|
||||
|
||||
// No transparency
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.icns;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
final class ICNSImageMetadata extends StandardImageMetadataSupport {
|
||||
ICNSImageMetadata(ImageTypeSpecifier type, String compressionName) {
|
||||
super(builder(type).withCompressionTypeName(compressionName));
|
||||
}
|
||||
}
|
||||
+15
-29
@@ -35,16 +35,11 @@ import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
@@ -66,9 +61,10 @@ import java.util.List;
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Apple_Icon_Image_format">Apple Icon Image format (Wikipedia)</a>
|
||||
*/
|
||||
public final class ICNSImageReader extends ImageReaderBase {
|
||||
// TODO: Support ToC resource for faster parsing/faster determine number of icons?
|
||||
// TODO: Subsampled reading for completeness, even if never used?
|
||||
private final List<IconResource> icons = new ArrayList<>();
|
||||
private final List<IconResource> masks = new ArrayList<>();
|
||||
private List<IconResource> icons = new ArrayList<IconResource>();
|
||||
private List<IconResource> masks = new ArrayList<IconResource>();
|
||||
private IconResource lastResourceRead;
|
||||
|
||||
private int length;
|
||||
@@ -140,7 +136,7 @@ public final class ICNSImageReader extends ImageReaderBase {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
IconResource resource = readIconResource(imageIndex);
|
||||
|
||||
List<ImageTypeSpecifier> specifiers = new ArrayList<>();
|
||||
List<ImageTypeSpecifier> specifiers = new ArrayList<ImageTypeSpecifier>();
|
||||
|
||||
switch (resource.depth()) {
|
||||
case 1:
|
||||
@@ -234,9 +230,14 @@ public final class ICNSImageReader extends ImageReaderBase {
|
||||
packedSize -= 4;
|
||||
}
|
||||
|
||||
try (InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize)) {
|
||||
InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize);
|
||||
|
||||
try {
|
||||
ICNSUtil.decompress(new DataInputStream(input), data, 0, (data.length * 24) / 32); // 24 bit data
|
||||
}
|
||||
finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
data = new byte[resource.length - ICNS.RESOURCE_HEADER_SIZE];
|
||||
@@ -490,7 +491,7 @@ public final class ICNSImageReader extends ImageReaderBase {
|
||||
|
||||
String format;
|
||||
|
||||
if (Arrays.equals(ICNS.PNG_MAGIC, Arrays.copyOfRange(magic, 0, ICNS.PNG_MAGIC.length))) {
|
||||
if (Arrays.equals(ICNS.PNG_MAGIC, magic)) {
|
||||
format = "PNG";
|
||||
}
|
||||
else if (Arrays.equals(ICNS.JPEG_2000_MAGIC, magic)) {
|
||||
@@ -526,6 +527,7 @@ public final class ICNSImageReader extends ImageReaderBase {
|
||||
IconResource resource = IconResource.read(imageInput);
|
||||
|
||||
if (resource.isTOC()) {
|
||||
// TODO: IconResource.readTOC()?
|
||||
int resourceCount = (resource.length - ICNS.RESOURCE_HEADER_SIZE) / ICNS.RESOURCE_HEADER_SIZE;
|
||||
long pos = resource.start + resource.length;
|
||||
|
||||
@@ -568,23 +570,6 @@ public final class ICNSImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||
IconResource resource = readIconResource(imageIndex);
|
||||
|
||||
String compressionName;
|
||||
if (resource.isForeignFormat()) {
|
||||
// Special handling of PNG/JPEG 2000 icons
|
||||
imageInput.seek(resource.start + ICNS.RESOURCE_HEADER_SIZE);
|
||||
compressionName = getForeignFormat(imageInput);
|
||||
}
|
||||
else {
|
||||
compressionName = resource.isCompressed() ? "RLE" : "None";
|
||||
}
|
||||
|
||||
return new ICNSImageMetadata(getRawImageType(imageIndex), compressionName);
|
||||
}
|
||||
|
||||
private static final class ICNSBitMaskColorModel extends IndexColorModel {
|
||||
static final IndexColorModel INSTANCE = new ICNSBitMaskColorModel();
|
||||
|
||||
@@ -593,6 +578,7 @@ public final class ICNSImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"UnusedAssignment"})
|
||||
public static void main(String[] args) throws IOException {
|
||||
int argIndex = 0;
|
||||
|
||||
|
||||
+1
-8
@@ -34,13 +34,7 @@ import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOWriteWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
@@ -110,7 +104,6 @@ public final class ICNSImageWriter extends ImageWriterBase {
|
||||
sequenceIndex = 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@Override
|
||||
public void endWriteSequence() throws IOException {
|
||||
assertOutput();
|
||||
|
||||
+15
-9
@@ -38,13 +38,8 @@ import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.image.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
@@ -145,12 +140,17 @@ final class SipsJP2Reader {
|
||||
}
|
||||
|
||||
private static String checkErrorMessage(final Process process) throws IOException {
|
||||
try (InputStream stream = process.getErrorStream()) {
|
||||
InputStream stream = process.getErrorStream();
|
||||
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
||||
String message = reader.readLine();
|
||||
|
||||
return message != null && message.startsWith("Error: ") ? message.substring(7) : null;
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] buildCommand(final File sipsCommand, final File tempFile) {
|
||||
@@ -159,13 +159,19 @@ final class SipsJP2Reader {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static File dumpToFile(final ImageInputStream stream) throws IOException {
|
||||
File tempFile = File.createTempFile("imageio-icns-", ".png");
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(tempFile)) {
|
||||
FileOutputStream out = new FileOutputStream(tempFile);
|
||||
|
||||
try {
|
||||
FileUtil.copy(IIOUtil.createStreamAdapter(stream), out);
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.iff.IFF.*;
|
||||
import static com.twelvemonkeys.imageio.plugins.iff.IFFUtil.toChunkStr;
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
|
||||
/**
|
||||
* Form.
|
||||
@@ -27,7 +27,7 @@ abstract class Form {
|
||||
|
||||
abstract int width();
|
||||
abstract int height();
|
||||
abstract double aspect();
|
||||
abstract float aspect();
|
||||
abstract int bitplanes();
|
||||
abstract int compressionType();
|
||||
|
||||
@@ -118,7 +118,7 @@ abstract class Form {
|
||||
}
|
||||
|
||||
private ILBMForm(final int formType, final BMHDChunk bitmapHeader, final CAMGChunk viewMode, final CMAPChunk colorMap, final AbstractMultiPaletteChunk multiPalette, final XS24Chunk thumbnail, final BODYChunk body) {
|
||||
super(isTrue(validFormType(formType), formType, "Unknown IFF Form type: %s"));
|
||||
super(formType);
|
||||
this.bitmapHeader = bitmapHeader;
|
||||
this.viewMode = viewMode;
|
||||
this.colorMap = colorMap;
|
||||
@@ -127,19 +127,6 @@ abstract class Form {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
private static boolean validFormType(int formType) {
|
||||
switch (formType) {
|
||||
case TYPE_ACBM:
|
||||
case TYPE_ILBM:
|
||||
case TYPE_PBM:
|
||||
case TYPE_RGB8:
|
||||
case TYPE_RGBN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int width() {
|
||||
return bitmapHeader.width;
|
||||
@@ -161,8 +148,8 @@ abstract class Form {
|
||||
}
|
||||
|
||||
@Override
|
||||
double aspect() {
|
||||
return bitmapHeader.yAspect == 0 ? 0 : (bitmapHeader.xAspect / (double) bitmapHeader.yAspect);
|
||||
float aspect() {
|
||||
return bitmapHeader.yAspect == 0 ? 0 : (bitmapHeader.xAspect / (float) bitmapHeader.yAspect);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -310,7 +297,7 @@ abstract class Form {
|
||||
}
|
||||
|
||||
private DEEPForm(final int formType, final DGBLChunk deepGlobal, final DLOCChunk deepLocation, final DPELChunk deepPixel, final XS24Chunk thumbnail, final BODYChunk body) {
|
||||
super(isTrue(validFormType(formType), formType, "Unknown IFF Form type: %s"));
|
||||
super(formType);
|
||||
this.deepGlobal = deepGlobal;
|
||||
this.deepLocation = deepLocation;
|
||||
this.deepPixel = deepPixel;
|
||||
@@ -318,15 +305,6 @@ abstract class Form {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
private static boolean validFormType(int formType) {
|
||||
switch (formType) {
|
||||
case TYPE_DEEP:
|
||||
case TYPE_TVPP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int width() {
|
||||
@@ -359,8 +337,8 @@ abstract class Form {
|
||||
}
|
||||
|
||||
@Override
|
||||
double aspect() {
|
||||
return deepGlobal.yAspect == 0 ? 0 : deepGlobal.xAspect / (double) deepGlobal.yAspect;
|
||||
float aspect() {
|
||||
return deepGlobal.yAspect == 0 ? 0 : deepGlobal.xAspect / (float) deepGlobal.yAspect;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+235
-86
@@ -1,83 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.image.*;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.*;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.iff.IFF.*;
|
||||
import static com.twelvemonkeys.imageio.plugins.iff.IFFUtil.toChunkStr;
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
final class IFFImageMetadata extends StandardImageMetadataSupport {
|
||||
IFFImageMetadata(ImageTypeSpecifier type, Form header, IndexColorModel palette) {
|
||||
this(builder(type), notNull(header, "header"), palette);
|
||||
final class IFFImageMetadata extends AbstractMetadata {
|
||||
private final Form header;
|
||||
private final IndexColorModel colorMap;
|
||||
private final List<GenericChunk> meta;
|
||||
|
||||
IFFImageMetadata(Form header, IndexColorModel colorMap) {
|
||||
this.header = notNull(header, "header");
|
||||
isTrue(validFormType(header.formType), header.formType, "Unknown IFF Form type: %s");
|
||||
this.colorMap = colorMap;
|
||||
this.meta = header.meta;
|
||||
}
|
||||
|
||||
private IFFImageMetadata(Builder builder, Form header, IndexColorModel palette) {
|
||||
super(builder.withPalette(palette)
|
||||
.withCompressionTypeName(compressionName(header))
|
||||
.withBitsPerSample(bitsPerSample(header))
|
||||
.withPlanarConfiguration(planarConfiguration(header))
|
||||
.withPixelAspectRatio(header.aspect() != 0 ? header.aspect() : null)
|
||||
.withFormatVersion("1.0")
|
||||
.withTextEntries(textEntries(header)));
|
||||
}
|
||||
|
||||
private static String compressionName(Form header) {
|
||||
switch (header.compressionType()) {
|
||||
case BMHDChunk.COMPRESSION_NONE:
|
||||
return "None";
|
||||
case BMHDChunk.COMPRESSION_BYTE_RUN:
|
||||
return "RLE";
|
||||
case 4:
|
||||
// Compression type 4 means different things for different FORM types, we support
|
||||
// Impulse RGB8 RLE compression: 24 bit RGB + 1 bit mask + 7 bit run count
|
||||
if (header.formType == TYPE_RGB8) {
|
||||
return "RGB8";
|
||||
}
|
||||
private boolean validFormType(int formType) {
|
||||
switch (formType) {
|
||||
default:
|
||||
return "Unknown";
|
||||
return false;
|
||||
case TYPE_ACBM:
|
||||
case TYPE_DEEP:
|
||||
case TYPE_ILBM:
|
||||
case TYPE_PBM:
|
||||
case TYPE_RGB8:
|
||||
case TYPE_RGBN:
|
||||
case TYPE_TVPP:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] bitsPerSample(Form header) {
|
||||
int bitplanes = header.bitplanes();
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
|
||||
switch (header.bitplanes()) {
|
||||
case 8:
|
||||
if (colorMap == null) {
|
||||
csType.setAttribute("name", "GRAY");
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 24:
|
||||
case 25:
|
||||
case 32:
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
default:
|
||||
csType.setAttribute("name", "Unknown");
|
||||
}
|
||||
|
||||
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
chroma.appendChild(numChannels);
|
||||
if (colorMap == null && header.bitplanes() == 8) {
|
||||
numChannels.setAttribute("value", Integer.toString(1));
|
||||
}
|
||||
else if (header.bitplanes() == 25 || header.bitplanes() == 32) {
|
||||
numChannels.setAttribute("value", Integer.toString(4));
|
||||
}
|
||||
else {
|
||||
numChannels.setAttribute("value", Integer.toString(3));
|
||||
}
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
chroma.appendChild(blackIsZero);
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
|
||||
// NOTE: TGA files may contain a color map, even if true color...
|
||||
// Not sure if this is a good idea to expose to the meta data,
|
||||
// as it might be unexpected... Then again...
|
||||
if (colorMap != null) {
|
||||
IIOMetadataNode palette = new IIOMetadataNode("Palette");
|
||||
chroma.appendChild(palette);
|
||||
|
||||
for (int i = 0; i < colorMap.getMapSize(); i++) {
|
||||
IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
|
||||
palette.appendChild(paletteEntry);
|
||||
paletteEntry.setAttribute("index", Integer.toString(i));
|
||||
|
||||
paletteEntry.setAttribute("red", Integer.toString(colorMap.getRed(i)));
|
||||
paletteEntry.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
|
||||
paletteEntry.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
|
||||
}
|
||||
|
||||
if (colorMap.getTransparentPixel() != -1) {
|
||||
IIOMetadataNode backgroundIndex = new IIOMetadataNode("BackgroundIndex");
|
||||
chroma.appendChild(backgroundIndex);
|
||||
backgroundIndex.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: TVPP TVPaint Project files have a MIXR chunk with a background color
|
||||
// and also a BGP1 (background pen 1?) and BGP2 chunks
|
||||
// if (extensions != null && extensions.getBackgroundColor() != 0) {
|
||||
// Color background = new Color(extensions.getBackgroundColor(), true);
|
||||
//
|
||||
// IIOMetadataNode backgroundColor = new IIOMetadataNode("BackgroundColor");
|
||||
// chroma.appendChild(backgroundColor);
|
||||
//
|
||||
// backgroundColor.setAttribute("red", Integer.toString(background.getRed()));
|
||||
// backgroundColor.setAttribute("green", Integer.toString(background.getGreen()));
|
||||
// backgroundColor.setAttribute("blue", Integer.toString(background.getBlue()));
|
||||
// }
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
if (header.compressionType() == BMHDChunk.COMPRESSION_NONE) {
|
||||
return null; // All defaults
|
||||
}
|
||||
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", "RLE");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode data = new IIOMetadataNode("Data");
|
||||
|
||||
// PlanarConfiguration
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
switch (header.formType) {
|
||||
case TYPE_DEEP:
|
||||
case TYPE_TVPP:
|
||||
case TYPE_RGB8:
|
||||
case TYPE_PBM:
|
||||
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||
break;
|
||||
case TYPE_ILBM:
|
||||
planarConfiguration.setAttribute("value", "PlaneInterleaved");
|
||||
break;
|
||||
default:
|
||||
planarConfiguration.setAttribute("value", "Unknown " + IFFUtil.toChunkStr(header.formType));
|
||||
break;
|
||||
}
|
||||
data.appendChild(planarConfiguration);
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", colorMap != null ? "Index" : "UnsignedIntegral");
|
||||
data.appendChild(sampleFormat);
|
||||
|
||||
// BitsPerSample
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
String value = bitsPerSampleValue(header.bitplanes());
|
||||
bitsPerSample.setAttribute("value", value);
|
||||
data.appendChild(bitsPerSample);
|
||||
|
||||
// SignificantBitsPerSample not in format
|
||||
// SampleMSB not in format
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private String bitsPerSampleValue(int bitplanes) {
|
||||
switch (bitplanes) {
|
||||
case 1:
|
||||
case 2:
|
||||
@@ -87,47 +192,91 @@ final class IFFImageMetadata extends StandardImageMetadataSupport {
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
return new int[] {bitplanes};
|
||||
return Integer.toString(bitplanes);
|
||||
case 24:
|
||||
return new int[] {8, 8, 8};
|
||||
return "8 8 8";
|
||||
case 25:
|
||||
if (header.formType != TYPE_RGB8) {
|
||||
throw new IllegalArgumentException(String.format("25 bit depth only supported for FORM type RGB8: %s", IFFUtil.toChunkStr(header.formType)));
|
||||
}
|
||||
|
||||
return new int[] {8, 8, 8, 1};
|
||||
return "8 8 8 1";
|
||||
case 32:
|
||||
return new int[] {8, 8, 8, 8};
|
||||
return "8 8 8 8";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown bit count: " + bitplanes);
|
||||
}
|
||||
}
|
||||
|
||||
private static PlanarConfiguration planarConfiguration(Form header) {
|
||||
switch (header.formType) {
|
||||
case TYPE_DEEP:
|
||||
case TYPE_TVPP:
|
||||
case TYPE_RGB8:
|
||||
case TYPE_PBM:
|
||||
return PlanarConfiguration.PixelInterleaved;
|
||||
case TYPE_ILBM:
|
||||
return PlanarConfiguration.PlaneInterleaved;
|
||||
default:
|
||||
return null;
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
if (header.aspect() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
// PixelAspectRatio
|
||||
IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
|
||||
pixelAspectRatio.setAttribute("value", String.valueOf(header.aspect()));
|
||||
dimension.appendChild(pixelAspectRatio);
|
||||
|
||||
// TODO: HorizontalScreenSize?
|
||||
// TODO: VerticalScreenSize?
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
private static List<TextEntry> textEntries(Form header) {
|
||||
if (header.meta.isEmpty()) {
|
||||
return emptyList();
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||
|
||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||
document.appendChild(formatVersion);
|
||||
formatVersion.setAttribute("value", "1.0");
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
if (meta.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<TextEntry> text = new ArrayList<>();
|
||||
for (GenericChunk chunk : header.meta) {
|
||||
text.add(new TextEntry(toChunkStr(chunk.chunkId),
|
||||
new String(chunk.data, chunk.chunkId == IFF.CHUNK_UTF8 ? StandardCharsets.UTF_8:StandardCharsets.US_ASCII)));
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
|
||||
for (GenericChunk chunk : meta) {
|
||||
IIOMetadataNode node = new IIOMetadataNode("TextEntry");
|
||||
node.setAttribute("keyword", IFFUtil.toChunkStr(chunk.chunkId));
|
||||
node.setAttribute("value", new String(chunk.data, chunk.chunkId == IFF.CHUNK_UTF8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII));
|
||||
text.appendChild(node);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
if ((colorMap == null || !colorMap.hasAlpha()) && header.bitplanes() != 32 && header.bitplanes() != 25) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
if (header.bitplanes() == 25 || header.bitplanes() == 32) {
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
alpha.setAttribute("value", header.premultiplied() ? "premultiplied" : "nonpremultiplied");
|
||||
transparency.appendChild(alpha);
|
||||
}
|
||||
|
||||
if (colorMap != null && colorMap.getTransparency() == Transparency.BITMASK) {
|
||||
IIOMetadataNode transparentIndex = new IIOMetadataNode("TransparentIndex");
|
||||
transparentIndex.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
|
||||
transparency.appendChild(transparentIndex);
|
||||
}
|
||||
|
||||
return transparency;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-8
@@ -38,19 +38,14 @@ import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
@@ -355,7 +350,9 @@ public final class IFFImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||
return new IFFImageMetadata(getRawImageType(imageIndex), header, header.colorMap());
|
||||
init(imageIndex);
|
||||
|
||||
return new IFFImageMetadata(header, header.colorMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+79
-146
@@ -1,37 +1,24 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static java.awt.image.BufferedImage.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class IFFImageMetadataTest {
|
||||
|
||||
private static final ImageTypeSpecifier TYPE_8_BIT_GRAY = ImageTypeSpecifiers.createFromBufferedImageType(TYPE_BYTE_GRAY);
|
||||
private static final ImageTypeSpecifier TYPE_8_BIT_PALETTE = ImageTypeSpecifiers.createFromBufferedImageType(TYPE_BYTE_INDEXED);
|
||||
private static final ImageTypeSpecifier TYPE_24_BIT_RGB = ImageTypeSpecifiers.createFromBufferedImageType(TYPE_3BYTE_BGR);
|
||||
private static final ImageTypeSpecifier TYPE_32_BIT_ARGB = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
|
||||
private static final ImageTypeSpecifier TYPE_32_BIT_ARGB_DEEP = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE);
|
||||
|
||||
@Test
|
||||
public void testStandardFeatures() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
final IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
|
||||
final IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
// Standard metadata format
|
||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||
@@ -64,9 +51,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_GRAY, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
@@ -91,9 +78,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
@@ -119,10 +106,9 @@ public class IFFImageMetadataTest {
|
||||
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
|
||||
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
ImageTypeSpecifier fromIndexColorModel = ImageTypeSpecifiers.createFromIndexColorModel(new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(fromIndexColorModel, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
|
||||
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(5, chroma.getLength());
|
||||
@@ -133,7 +119,7 @@ public class IFFImageMetadataTest {
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("4", numChannels.getAttribute("value"));
|
||||
assertEquals("3", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
@@ -167,9 +153,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode compression = getStandardNode(metadata, "Compression");
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
assertEquals("Compression", compression.getNodeName());
|
||||
assertEquals(2, compression.getLength());
|
||||
@@ -190,9 +176,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_NONE, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
assertNull(getStandardNode(metadata, "Compression")); // No compression, all default...
|
||||
assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -200,9 +186,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_GRAY, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -227,9 +213,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -254,9 +240,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -283,10 +269,9 @@ public class IFFImageMetadataTest {
|
||||
.with(new BMHDChunk(300, 200, i, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
byte[] rgb = new byte[2 << i]; // Colors doesn't really matter here
|
||||
ImageTypeSpecifier fromIndexColorModel = ImageTypeSpecifiers.createFromIndexColorModel(new IndexColorModel(header.bitplanes(), rgb.length, rgb, rgb, rgb, 0));
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(fromIndexColorModel, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), rgb.length, rgb, rgb, rgb, 0));
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -312,9 +297,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_PBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_GRAY, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -339,9 +324,9 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_PBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -371,20 +356,10 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(bitmapHeader);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
|
||||
|
||||
if (dimension != null) {
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
||||
|
||||
assertNull(imageOrientation.getNextSibling()); // No more children
|
||||
}
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNull(dimension);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -393,24 +368,20 @@ public class IFFImageMetadataTest {
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||
.with(new CAMGChunk(4));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
|
||||
// No Dimension node is okay, or one with an aspect ratio of 1.0
|
||||
if (dimension != null) {
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(2, dimension.getLength());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
||||
|
||||
assertNull(imageOrientation.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,22 +398,18 @@ public class IFFImageMetadataTest {
|
||||
.with(bitmapHeader)
|
||||
.with(viewPort);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(2, dimension.getLength());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("2.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
||||
|
||||
assertNull(imageOrientation.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -458,22 +425,18 @@ public class IFFImageMetadataTest {
|
||||
.with(bitmapHeader)
|
||||
.with(viewPort);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(2, dimension.getLength());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("0.5", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
||||
|
||||
assertNull(imageOrientation.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -484,22 +447,18 @@ public class IFFImageMetadataTest {
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||
.with(viewPort);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(2, dimension.getLength());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
||||
|
||||
assertNull(imageOrientation.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -507,33 +466,32 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode document = getStandardNode(metadata, "Document");
|
||||
IIOMetadataNode document = metadata.getStandardDocumentNode();
|
||||
assertNotNull(document);
|
||||
assertEquals("Document", document.getNodeName());
|
||||
assertEquals(1, document.getLength());
|
||||
|
||||
IIOMetadataNode formatVersion = (IIOMetadataNode) document.getFirstChild();
|
||||
assertEquals("FormatVersion", formatVersion.getNodeName());
|
||||
assertEquals("1.0", formatVersion.getAttribute("value"));
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
|
||||
assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(formatVersion.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardText() throws IIOException {
|
||||
int[] chunks = {IFF.CHUNK_ANNO, IFF.CHUNK_ANNO, IFF.CHUNK_UTF8};
|
||||
String[] texts = {"annotation", "dupe", "äñnótâtïøñ"};
|
||||
int[] chunks = {IFF.CHUNK_ANNO, IFF.CHUNK_UTF8};
|
||||
String[] texts = {"annotation", "äñnótâtïøñ"};
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||
.with(new GenericChunk(chunks[0], texts[0].getBytes(StandardCharsets.US_ASCII)))
|
||||
.with(new GenericChunk(chunks[1], texts[1].getBytes(StandardCharsets.US_ASCII)))
|
||||
.with(new GenericChunk(chunks[2], texts[2].getBytes(StandardCharsets.UTF_8)));
|
||||
.with(new GenericChunk(chunks[1], texts[1].getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode text = getStandardNode(metadata, "Text");
|
||||
IIOMetadataNode text = metadata.getStandardTextNode();
|
||||
assertNotNull(text);
|
||||
assertEquals("Text", text.getNodeName());
|
||||
assertEquals(texts.length, text.getLength());
|
||||
@@ -551,21 +509,10 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
|
||||
if (transparency != null) {
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", alpha.getNodeName());
|
||||
assertEquals("none", alpha.getAttribute("value"));
|
||||
|
||||
assertNull(alpha.getNextSibling()); // No more children
|
||||
}
|
||||
// Otherwise, no transparency, just defaults
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNull(transparency); // No transparency, just defaults
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -573,18 +520,18 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_HAS_MASK, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", alpha.getNodeName());
|
||||
assertEquals("nonpremultiplied", alpha.getAttribute("value"));
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", pixelAspectRatio.getNodeName());
|
||||
assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(alpha.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -593,33 +540,28 @@ public class IFFImageMetadataTest {
|
||||
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
|
||||
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
ImageTypeSpecifier fromIndexColorModel = ImageTypeSpecifiers.createFromIndexColorModel(new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(fromIndexColorModel, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
|
||||
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(2, transparency.getLength());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", alpha.getNodeName());
|
||||
assertEquals("nonpremultiplied", alpha.getAttribute("value"));
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("TransparentIndex", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode transparentIndex = (IIOMetadataNode) alpha.getNextSibling();
|
||||
assertEquals("TransparentIndex", transparentIndex.getNodeName());
|
||||
assertEquals("1", transparentIndex.getAttribute("value"));
|
||||
|
||||
assertNull(transparentIndex.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardRGB8() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_RGB8)
|
||||
.with(new BMHDChunk(300, 200, 25, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
// Chroma
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
@@ -639,7 +581,7 @@ public class IFFImageMetadataTest {
|
||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||
|
||||
// Data
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -659,7 +601,7 @@ public class IFFImageMetadataTest {
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
|
||||
// Transparency
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
@@ -682,10 +624,10 @@ public class IFFImageMetadataTest {
|
||||
Form header = Form.ofType(IFF.TYPE_DEEP)
|
||||
.with(new DGBLChunk(8))
|
||||
.with(dpel);
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB_DEEP, header, header.colorMap());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
// Chroma
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
@@ -707,7 +649,7 @@ public class IFFImageMetadataTest {
|
||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||
|
||||
// Data
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -727,7 +669,7 @@ public class IFFImageMetadataTest {
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
|
||||
// Transparency
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
@@ -738,13 +680,4 @@ public class IFFImageMetadataTest {
|
||||
|
||||
assertNull(alpha.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
// TODO: Test RGB8 + ColorMap
|
||||
|
||||
private IIOMetadataNode getStandardNode(IIOMetadata metadata, String nodeName) {
|
||||
IIOMetadataNode asTree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
NodeList nodes = asTree.getElementsByTagName(nodeName);
|
||||
|
||||
return nodes.getLength() > 0 ? (IIOMetadataNode) nodes.item(0) : null;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jai-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jep262-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
|
||||
|
||||
+5
-7
@@ -45,7 +45,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
@@ -377,12 +377,10 @@ public final class PCXImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
// checkBounds(imageIndex);
|
||||
// readHeader();
|
||||
//
|
||||
// return new PCXMetadata(header, getVGAPalette());
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
return new PCXMetadata(rawType, header);
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return new PCXMetadata(header, getVGAPalette());
|
||||
}
|
||||
|
||||
private IndexColorModel getVGAPalette() throws IOException {
|
||||
|
||||
+225
-18
@@ -1,29 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
final class PCXMetadata extends StandardImageMetadataSupport {
|
||||
public PCXMetadata(ImageTypeSpecifier type, PCXHeader header) {
|
||||
super(builder(type)
|
||||
.withPlanarConfiguration(planarConfiguration(header))
|
||||
.withCompressionTypeName(compressionName(header))
|
||||
.withFormatVersion(String.valueOf(header.getVersion())));
|
||||
final class PCXMetadata extends AbstractMetadata {
|
||||
private final PCXHeader header;
|
||||
private final IndexColorModel vgaPalette;
|
||||
|
||||
PCXMetadata(final PCXHeader header, final IndexColorModel vgaPalette) {
|
||||
this.header = header;
|
||||
this.vgaPalette = vgaPalette;
|
||||
}
|
||||
|
||||
private static PlanarConfiguration planarConfiguration(PCXHeader header) {
|
||||
return header.getChannels() > 1 ? PlanarConfiguration.LineInterleaved : null;
|
||||
}
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
private static String compressionName(PCXHeader header) {
|
||||
switch (header.getCompression()) {
|
||||
case PCX.COMPRESSION_NONE:
|
||||
return "None";
|
||||
case PCX.COMPRESSION_RLE:
|
||||
return "RLE";
|
||||
IndexColorModel palette = null;
|
||||
boolean gray = false;
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
switch (header.getBitsPerPixel()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
palette = header.getEGAPalette();
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
case 8:
|
||||
// We may have IndexColorModel here for 1 channel images
|
||||
if (header.getChannels() == 1 && vgaPalette != null) {
|
||||
palette = vgaPalette;
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
}
|
||||
if (header.getChannels() == 1) {
|
||||
csType.setAttribute("name", "GRAY");
|
||||
gray = true;
|
||||
break;
|
||||
}
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
|
||||
case 24:
|
||||
// Some sources says this is possible... Untested.
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
|
||||
default:
|
||||
csType.setAttribute("name", "Unknown");
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
chroma.appendChild(csType);
|
||||
|
||||
// NOTE: Channels in chroma node reflects channels in color model, not data! (see data node)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
numChannels.setAttribute("value", gray ? "1" : "3");
|
||||
chroma.appendChild(numChannels);
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
chroma.appendChild(blackIsZero);
|
||||
|
||||
if (palette != null) {
|
||||
IIOMetadataNode paletteNode = new IIOMetadataNode("Palette");
|
||||
chroma.appendChild(paletteNode);
|
||||
|
||||
for (int i = 0; i < palette.getMapSize(); i++) {
|
||||
IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
|
||||
paletteEntry.setAttribute("index", Integer.toString(i));
|
||||
|
||||
paletteEntry.setAttribute("red", Integer.toString(palette.getRed(i)));
|
||||
paletteEntry.setAttribute("green", Integer.toString(palette.getGreen(i)));
|
||||
paletteEntry.setAttribute("blue", Integer.toString(palette.getBlue(i)));
|
||||
|
||||
paletteNode.appendChild(paletteEntry);
|
||||
}
|
||||
}
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
// No compression
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
if (header.getCompression() != PCX.COMPRESSION_NONE) {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", header.getCompression() == PCX.COMPRESSION_RLE ? "RLE" : "Uknown");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
// Planar configuration only makes sense for multi-channel images
|
||||
if (header.getChannels() > 1) {
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
planarConfiguration.setAttribute("value", "LineInterleaved");
|
||||
node.appendChild(planarConfiguration);
|
||||
}
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
|
||||
switch (header.getBitsPerPixel()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
sampleFormat.setAttribute("value", "Index");
|
||||
break;
|
||||
case 8:
|
||||
if (header.getChannels() == 1 && vgaPalette != null) {
|
||||
sampleFormat.setAttribute("value", "Index");
|
||||
break;
|
||||
}
|
||||
// Else fall through for GRAY
|
||||
default:
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
break;
|
||||
}
|
||||
|
||||
node.appendChild(sampleFormat);
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBitsPerPixel())));
|
||||
node.appendChild(bitsPerSample);
|
||||
|
||||
IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
|
||||
significantBitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBitsPerPixel())));
|
||||
node.appendChild(significantBitsPerSample);
|
||||
|
||||
IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
|
||||
sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private String createListValue(final int itemCount, final String... values) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
buffer.append(values[i % values.length]);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientation.setAttribute("value", "Normal");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Document");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("FormatVersion");
|
||||
imageOrientation.setAttribute("value", String.valueOf(header.getVersion()));
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// No text node
|
||||
|
||||
// No tiling
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
// NOTE: There doesn't seem to be any god way to determine transparency, other than by convention
|
||||
// 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
|
||||
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
alpha.setAttribute("value", header.getChannels() == 1 || header.getChannels() == 3 ? "none" : "nonpremultiplied");
|
||||
transparency.appendChild(alpha);
|
||||
|
||||
return transparency;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pdf</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pict</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
||||
|
||||
+9
-21
@@ -67,28 +67,18 @@ import com.twelvemonkeys.io.enc.Decoder;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Reader for Apple Mac Paint Picture (PICT) format.
|
||||
@@ -2621,9 +2611,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
return getYPtCoord(getPICTFrame().height);
|
||||
}
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) {
|
||||
// TODO: The images look slightly different in Preview.. Could indicate the color space is wrong...
|
||||
return Collections.singletonList(
|
||||
ImageTypeSpecifiers.createPacked(
|
||||
@@ -2635,10 +2623,10 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
checkBounds(imageIndex);
|
||||
getPICTFrame(); // TODO: Would probably be better to use readPictHeader here, but it isn't cached
|
||||
|
||||
return new PICTMetadata(rawType, version, screenImageXRatio, screenImageYRatio);
|
||||
return new PICTMetadata(version, screenImageXRatio, screenImageYRatio);
|
||||
}
|
||||
|
||||
protected static void showIt(final BufferedImage pImage, final String pTitle) {
|
||||
|
||||
+79
-40
@@ -1,38 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pict;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
/**
|
||||
* PICTMetadata.
|
||||
@@ -41,13 +11,82 @@ import javax.imageio.ImageTypeSpecifier;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PICTMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
||||
*/
|
||||
final class PICTMetadata extends StandardImageMetadataSupport {
|
||||
PICTMetadata(final ImageTypeSpecifier type, final int version, final double screenImageXRatio, final double screenImageYRatio) {
|
||||
super(builder(type)
|
||||
.withPixelAspectRatio(screenImageXRatio > 0.0d && screenImageYRatio > 0.0d ? screenImageXRatio / screenImageYRatio : 1)
|
||||
.withFormatVersion(Integer.toString(version))
|
||||
);
|
||||
// As this is a vector-ish format, some of the data makes no sense... :-P
|
||||
// It is, however, consistent with the getRawImageTyp/getImageTypes
|
||||
public class PICTMetadata extends AbstractMetadata {
|
||||
|
||||
private final int version;
|
||||
private final double screenImageXRatio;
|
||||
private final double screenImageYRatio;
|
||||
|
||||
PICTMetadata(final int version, final double screenImageXRatio, final double screenImageYRatio) {
|
||||
this.version = version;
|
||||
this.screenImageXRatio = screenImageXRatio;
|
||||
this.screenImageYRatio = screenImageYRatio;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
csType.setAttribute("name", "RGB");
|
||||
|
||||
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
chroma.appendChild(numChannels);
|
||||
numChannels.setAttribute("value", "3");
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
chroma.appendChild(blackIsZero);
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
if (screenImageXRatio > 0.0d && screenImageYRatio > 0.0d) {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Dimension");
|
||||
double ratio = screenImageXRatio / screenImageYRatio;
|
||||
IIOMetadataNode subNode = new IIOMetadataNode("PixelAspectRatio");
|
||||
subNode.setAttribute("value", "" + ratio);
|
||||
node.appendChild(subNode);
|
||||
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode data = new IIOMetadataNode("Data");
|
||||
|
||||
// As this is a vector-ish format, with possibly multiple regions of pixel data, this makes no sense... :-P
|
||||
// This is, however, consistent with the getRawImageTyp/getImageTypes
|
||||
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||
data.appendChild(planarConfiguration);
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
data.appendChild(sampleFormat);
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", "32");
|
||||
data.appendChild(bitsPerSample);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||
|
||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||
document.appendChild(formatVersion);
|
||||
formatVersion.setAttribute("value", Integer.toString(version));
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-32
@@ -1,33 +1,3 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
@@ -64,7 +34,7 @@ public final class PNTGImageReader extends ImageReaderBase {
|
||||
private static final Set<ImageTypeSpecifier> IMAGE_TYPES =
|
||||
Collections.singleton(ImageTypeSpecifiers.createIndexed(new int[] {-1, 0}, false, -1, 1, DataBuffer.TYPE_BYTE));
|
||||
|
||||
PNTGImageReader(final ImageReaderSpi provider) {
|
||||
protected PNTGImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@@ -153,7 +123,9 @@ public final class PNTGImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
return new PNTGMetadata(getRawImageType(imageIndex));
|
||||
checkBounds(imageIndex);
|
||||
|
||||
return new PNTGMetadata();
|
||||
}
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
|
||||
-30
@@ -1,33 +1,3 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
+73
-38
@@ -1,38 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
/**
|
||||
* PNTGMetadata.
|
||||
@@ -41,11 +11,76 @@ import javax.imageio.ImageTypeSpecifier;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PNTGMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
||||
*/
|
||||
final class PNTGMetadata extends StandardImageMetadataSupport {
|
||||
public PNTGMetadata(ImageTypeSpecifier type) {
|
||||
super(builder(type)
|
||||
.withBlackIsZero(false)
|
||||
.withCompressionTypeName("PackBits")
|
||||
.withFormatVersion("1.0"));
|
||||
public class PNTGMetadata extends AbstractMetadata {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
csType.setAttribute("name", "GRAY");
|
||||
|
||||
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
chroma.appendChild(numChannels);
|
||||
numChannels.setAttribute("value", "1");
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
chroma.appendChild(blackIsZero);
|
||||
blackIsZero.setAttribute("value", "FALSE");
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", "PackBits"); // RLE?
|
||||
compressionNode.appendChild(compressionTypeName);
|
||||
compressionNode.appendChild(new IIOMetadataNode("Lossless"));
|
||||
// "value" defaults to TRUE
|
||||
|
||||
return compressionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode data = new IIOMetadataNode("Data");
|
||||
|
||||
// PlanarConfiguration
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||
data.appendChild(planarConfiguration);
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
data.appendChild(sampleFormat);
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", "1");
|
||||
data.appendChild(bitsPerSample);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||
|
||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||
document.appendChild(formatVersion);
|
||||
formatVersion.setAttribute("value", "1.0");
|
||||
|
||||
// TODO: We could get the file creation time from MacBinary header here...
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
// TODO: We could get the file name from MacBinary header here...
|
||||
return super.getStandardTextNode();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* Copyright (c) 2015, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
||||
+1
-5
@@ -1,11 +1,7 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.image.*;
|
||||
|
||||
/**
|
||||
* PNTGMetadataTest.
|
||||
*
|
||||
@@ -16,6 +12,6 @@ import java.awt.image.*;
|
||||
public class PNTGMetadataTest {
|
||||
@Test
|
||||
public void testCreate() {
|
||||
new PNTGMetadata(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY));
|
||||
new PNTGMetadata();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pnm</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
|
||||
|
||||
+21
-25
@@ -30,6 +30,8 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
@@ -38,7 +40,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -51,7 +53,11 @@ abstract class HeaderWriter {
|
||||
this.imageOutput = imageOutput;
|
||||
}
|
||||
|
||||
static HeaderWriter createHeaderWriter(final String formatName, final ImageOutputStream imageOutput) {
|
||||
public static void write(final IIOImage image, final ImageWriterBase writer, final ImageOutputStream imageOutput) throws IOException {
|
||||
createHeaderWriter(writer.getFormatName(), imageOutput).writeHeader(image, writer.getOriginatingProvider());
|
||||
}
|
||||
|
||||
private static HeaderWriter createHeaderWriter(final String formatName, final ImageOutputStream imageOutput) {
|
||||
if (formatName.equals("pam")) {
|
||||
return new PAMHeaderWriter(imageOutput);
|
||||
}
|
||||
@@ -77,36 +83,25 @@ abstract class HeaderWriter {
|
||||
return image.hasRaster() ? image.getRaster().getNumBands() : image.getRenderedImage().getSampleModel().getNumBands();
|
||||
}
|
||||
|
||||
protected final SampleModel getSampleModel(final IIOImage image) {
|
||||
return image.hasRaster() ? image.getRaster().getSampleModel() : image.getRenderedImage().getSampleModel();
|
||||
}
|
||||
|
||||
protected int getMaxVal(final IIOImage image) {
|
||||
int transferType = getTransferType(image);
|
||||
|
||||
switch (transferType) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return PNM.MAX_VAL_8BIT;
|
||||
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
return PNM.MAX_VAL_16BIT;
|
||||
|
||||
case DataBuffer.TYPE_INT:
|
||||
// We support TYPE_INT through conversion, if number of channels is 3 or 4 (TYPE_INT_RGB, TYPE_INT_ARGB)
|
||||
SampleModel sampleModel = getSampleModel(image);
|
||||
int numBands = sampleModel.getNumBands();
|
||||
|
||||
if (sampleModel instanceof SinglePixelPackedSampleModel && (numBands == 3 || numBands == 4)) {
|
||||
return PNM.MAX_VAL_8BIT;
|
||||
}
|
||||
// ...else fall through
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported data type: " + transferType);
|
||||
if (transferType == DataBuffer.TYPE_BYTE) {
|
||||
return PNM.MAX_VAL_8BIT;
|
||||
}
|
||||
else if (transferType == DataBuffer.TYPE_USHORT) {
|
||||
return PNM.MAX_VAL_16BIT;
|
||||
}
|
||||
// else if (transferType == DataBuffer.TYPE_INT) {
|
||||
// TODO: Support TYPE_INT through conversion, if number of channels is 3 or 4 (TYPE_INT_RGB, TYPE_INT_ARGB)
|
||||
// }
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported data type: " + transferType);
|
||||
}
|
||||
}
|
||||
|
||||
protected final int getTransferType(final IIOImage image) {
|
||||
return image.hasRaster() ? image.getRaster().getTransferType() : image.getRenderedImage().getSampleModel().getTransferType();
|
||||
return image.hasRaster() ? image.getRaster().getTransferType() : image.getRenderedImage().getSampleModel().getTransferType();
|
||||
}
|
||||
|
||||
protected final void writeComments(final IIOMetadata metadata, final ImageWriterSpi provider) throws IOException {
|
||||
@@ -125,4 +120,5 @@ abstract class HeaderWriter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-16
@@ -30,8 +30,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
@@ -46,8 +44,6 @@ final class PAMHeaderWriter extends HeaderWriter {
|
||||
|
||||
@Override
|
||||
public void writeHeader(final IIOImage image, final ImageWriterSpi provider) throws IOException {
|
||||
TupleType tupleType = tupleType(image);
|
||||
|
||||
// Write PAM magic
|
||||
imageOutput.writeShort(PNM.PAM);
|
||||
imageOutput.write('\n');
|
||||
@@ -56,19 +52,12 @@ final class PAMHeaderWriter extends HeaderWriter {
|
||||
// Write width/height and number of channels
|
||||
imageOutput.write(String.format("WIDTH %s\nHEIGHT %s\n", getWidth(image), getHeight(image)).getBytes(UTF_8));
|
||||
imageOutput.write(String.format("DEPTH %s\n", getNumBands(image)).getBytes(UTF_8));
|
||||
|
||||
// TODO: maxSample (8 or16 bit)
|
||||
imageOutput.write(String.format("MAXVAL %s\n", getMaxVal(image)).getBytes(UTF_8));
|
||||
|
||||
// TODO: Determine tuple type based on input color model and image data
|
||||
TupleType tupleType = getNumBands(image) > 3 ? TupleType.RGB_ALPHA : TupleType.RGB;
|
||||
imageOutput.write(String.format("TUPLTYPE %s\nENDHDR\n", tupleType).getBytes(UTF_8));
|
||||
}
|
||||
|
||||
private static TupleType tupleType(IIOImage image) {
|
||||
TupleType tupleType = image.hasRaster()
|
||||
? TupleType.forPAM(image.getRaster())
|
||||
: TupleType.forPAM(ImageTypeSpecifiers.createFromRenderedImage(image.getRenderedImage()));
|
||||
|
||||
if (tupleType == null) {
|
||||
throw new IllegalArgumentException("Unknown TupleType for " + (image.hasRaster() ? image.getRaster() : image.getRenderedImage()));
|
||||
}
|
||||
|
||||
return tupleType;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -45,11 +45,12 @@ public final class PAMImageWriterSpi extends ImageWriterSpiBase {
|
||||
super(new PAMProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||
return TupleType.forPAM(type) != null;
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier pType) {
|
||||
// TODO: FixMe
|
||||
return true;
|
||||
}
|
||||
|
||||
public ImageWriter createWriterInstance(final Object extension) {
|
||||
public ImageWriter createWriterInstance(final Object pExtension) {
|
||||
return new PNMImageWriter(this);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ final class PFMHeaderParser extends HeaderParser {
|
||||
public PNMHeader parse() throws IOException {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
float maxSample = 0;
|
||||
float maxSample = tupleType == TupleType.BLACKANDWHITE_WHITE_IS_ZERO ? 1 : 0; // PBM has no maxSample line
|
||||
|
||||
List<String> comments = new ArrayList<>();
|
||||
|
||||
@@ -77,7 +77,7 @@ final class PFMHeaderParser extends HeaderParser {
|
||||
String line = input.readLine();
|
||||
|
||||
if (line == null) {
|
||||
throw new IIOException("Unexpected end of stream");
|
||||
throw new IIOException("Unexpeced end of stream");
|
||||
}
|
||||
|
||||
int commentStart = line.indexOf('#');
|
||||
|
||||
+2
-34
@@ -30,8 +30,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
@@ -47,7 +45,8 @@ final class PNMHeaderWriter extends HeaderWriter {
|
||||
@Override
|
||||
public void writeHeader(final IIOImage image, final ImageWriterSpi provider) throws IOException {
|
||||
// Write P4/P5/P6 magic (Support only RAW formats for now; if we are to support PLAIN formats, pass parameter)
|
||||
short type = type(image);
|
||||
// TODO: Determine PBM, PBM or PPM based on input color model and image data?
|
||||
short type = PNM.PPM;
|
||||
imageOutput.writeShort(type);
|
||||
imageOutput.write('\n');
|
||||
|
||||
@@ -62,35 +61,4 @@ final class PNMHeaderWriter extends HeaderWriter {
|
||||
imageOutput.write(String.format("%s\n", getMaxVal(image)).getBytes(UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private short type(IIOImage image) {
|
||||
TupleType type = tupleType(image);
|
||||
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case BLACKANDWHITE_WHITE_IS_ZERO:
|
||||
return PNM.PBM;
|
||||
case GRAYSCALE:
|
||||
return PNM.PGM;
|
||||
case RGB:
|
||||
return PNM.PPM;
|
||||
default:
|
||||
// fall through...
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported tupleType: " + type);
|
||||
}
|
||||
|
||||
private static TupleType tupleType(IIOImage image) {
|
||||
TupleType tupleType = image.hasRaster()
|
||||
? TupleType.forPNM(image.getRaster())
|
||||
: TupleType.forPNM(ImageTypeSpecifiers.createFromRenderedImage(image.getRenderedImage()));
|
||||
|
||||
if (tupleType == null) {
|
||||
throw new IllegalArgumentException("Unknown TupleType for " + (image.hasRaster() ? image.getRaster() : image.getRenderedImage()));
|
||||
}
|
||||
|
||||
return tupleType;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -43,7 +43,7 @@ import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
@@ -468,7 +468,10 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
return new PNMMetadata(getRawImageType(imageIndex), header);
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return new PNMMetadata(header);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
+8
-15
@@ -31,17 +31,14 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.util.RasterUtils;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -72,7 +69,7 @@ public final class PNMImageWriter extends ImageWriterBase {
|
||||
// TODO: Issue warning if streamMetadata is non-null?
|
||||
// TODO: Issue warning if IIOImage contains thumbnails or other data we can't store?
|
||||
|
||||
writeHeader(image, this, imageOutput);
|
||||
HeaderWriter.write(image, this, imageOutput);
|
||||
|
||||
// TODO: Sub region
|
||||
// TODO: Subsampling
|
||||
@@ -83,20 +80,16 @@ public final class PNMImageWriter extends ImageWriterBase {
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
private void writeHeader(final IIOImage image, final ImageWriterBase writer, final ImageOutputStream imageOutput) throws IOException {
|
||||
HeaderWriter.createHeaderWriter(writer.getFormatName(), imageOutput).writeHeader(image, writer.getOriginatingProvider());
|
||||
}
|
||||
|
||||
private void writeImageData(final IIOImage image) throws IOException {
|
||||
// - dump data as is (or convert, if TYPE_INT_xxx
|
||||
// - dump data as is (or convert, if TYPE_INT_xxx)
|
||||
// Enforce RGB/CMYK order for such data!
|
||||
|
||||
// TODO: Loop over x/y tiles, using 0,0 is only valid for BufferedImage
|
||||
// TODO: PNM/PAM does not support tiling, we must iterate all tiles along the x-axis for each row we write
|
||||
Raster tile = image.hasRaster() ? image.getRaster() : image.getRenderedImage().getTile(0, 0);
|
||||
tile = tile.getTransferType() == DataBuffer.TYPE_INT ? RasterUtils.asByteRaster(tile) : tile;
|
||||
|
||||
SampleModel sampleModel = tile.getSampleModel();
|
||||
|
||||
DataBuffer dataBuffer = tile.getDataBuffer();
|
||||
|
||||
int tileWidth = tile.getWidth();
|
||||
|
||||
+4
-3
@@ -48,11 +48,12 @@ public final class PNMImageWriterSpi extends ImageWriterSpiBase {
|
||||
super(new PNMProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||
return TupleType.forPNM(type) != null;
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier pType) {
|
||||
// TODO: FixMe: Support only 1 bit b/w, 8-16 bit gray and 8-16 bit/sample RGB
|
||||
return true;
|
||||
}
|
||||
|
||||
public ImageWriter createWriterInstance(final Object extension) {
|
||||
public ImageWriter createWriterInstance(final Object pExtension) {
|
||||
return new PNMImageWriter(this);
|
||||
}
|
||||
|
||||
|
||||
+118
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -30,35 +30,92 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.image.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* PNMMetadata.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
final class PNMMetadata extends StandardImageMetadataSupport {
|
||||
final class PNMMetadata extends AbstractMetadata {
|
||||
private final PNMHeader header;
|
||||
|
||||
PNMMetadata(ImageTypeSpecifier type, PNMHeader header) {
|
||||
super(builder(type)
|
||||
.withColorSpaceType(colorSpace(header))
|
||||
// TODO: Might make sense to set gamma?
|
||||
.withBlackIsZero(header.getTupleType() != TupleType.BLACKANDWHITE_WHITE_IS_ZERO)
|
||||
.withSignificantBitsPerSample(significantBits(header))
|
||||
.withSampleMSB(header.getByteOrder() == ByteOrder.BIG_ENDIAN ? 0 : header.getBitsPerSample() - 1)
|
||||
.withOrientation(orientation(header))
|
||||
);
|
||||
|
||||
PNMMetadata(final PNMHeader header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
private static int significantBits(PNMHeader header) {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
switch (header.getTupleType()) {
|
||||
case BLACKANDWHITE:
|
||||
case BLACKANDWHITE_ALPHA:
|
||||
case BLACKANDWHITE_WHITE_IS_ZERO:
|
||||
case GRAYSCALE:
|
||||
case GRAYSCALE_ALPHA:
|
||||
csType.setAttribute("name", "GRAY");
|
||||
break;
|
||||
case RGB:
|
||||
case RGB_ALPHA:
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
case CMYK:
|
||||
case CMYK_ALPHA:
|
||||
csType.setAttribute("name", "CMYK");
|
||||
break;
|
||||
}
|
||||
|
||||
if (csType.getAttribute("name") != null) {
|
||||
chroma.appendChild(csType);
|
||||
}
|
||||
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
numChannels.setAttribute("value", Integer.toString(header.getSamplesPerPixel()));
|
||||
chroma.appendChild(numChannels);
|
||||
|
||||
// TODO: Might make sense to set gamma?
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZero.setAttribute("value", header.getTupleType() == TupleType.BLACKANDWHITE_WHITE_IS_ZERO
|
||||
? "FALSE"
|
||||
: "TRUE");
|
||||
chroma.appendChild(blackIsZero);
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
// No compression
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", header.getTransferType() == DataBuffer.TYPE_FLOAT
|
||||
? "Real"
|
||||
: "UnsignedIntegral");
|
||||
node.appendChild(sampleFormat);
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", createListValue(header.getSamplesPerPixel(), Integer.toString(header.getBitsPerSample())));
|
||||
node.appendChild(bitsPerSample);
|
||||
|
||||
IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
|
||||
significantBitsPerSample.setAttribute("value", createListValue(header.getSamplesPerPixel(), Integer.toString(computeSignificantBits())));
|
||||
node.appendChild(significantBitsPerSample);
|
||||
|
||||
String msb = header.getByteOrder() == ByteOrder.BIG_ENDIAN
|
||||
? "0"
|
||||
: Integer.toString(header.getBitsPerSample() - 1);
|
||||
IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
|
||||
sampleMSB.setAttribute("value", createListValue(header.getSamplesPerPixel(), msb));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private int computeSignificantBits() {
|
||||
if (header.getTransferType() == DataBuffer.TYPE_FLOAT) {
|
||||
return header.getBitsPerSample();
|
||||
}
|
||||
@@ -75,30 +132,38 @@ final class PNMMetadata extends StandardImageMetadataSupport {
|
||||
return significantBits;
|
||||
}
|
||||
|
||||
private static ColorSpaceType colorSpace(PNMHeader header) {
|
||||
switch (header.getTupleType()) {
|
||||
case BLACKANDWHITE:
|
||||
case BLACKANDWHITE_ALPHA:
|
||||
case BLACKANDWHITE_WHITE_IS_ZERO:
|
||||
case GRAYSCALE:
|
||||
case GRAYSCALE_ALPHA:
|
||||
return ColorSpaceType.GRAY;
|
||||
default:
|
||||
return null; // Fall back to color model's type
|
||||
}
|
||||
}
|
||||
private String createListValue(final int itemCount, final String... values) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
private static ImageOrientation orientation(PNMHeader header) {
|
||||
// For some reason, the float values are stored bottom-up
|
||||
return header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
|
||||
? ImageOrientation.FlipH
|
||||
: ImageOrientation.Normal;
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
buffer.append(values[i % values.length]);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientation.setAttribute("value",
|
||||
header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
|
||||
? "FlipH"
|
||||
: "Normal");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// No document node
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
// TODO: Could avoid this override, by changing the StandardImageMetadataSupport to
|
||||
// use List<Entry<String, String>> instead of Map<String, String> (we use duplicate "comment"s).
|
||||
if (!header.getComments().isEmpty()) {
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
@@ -114,4 +179,17 @@ final class PNMMetadata extends StandardImageMetadataSupport {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// No tiling
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
alpha.setAttribute("value", header.getTransparency() == Transparency.OPAQUE ? "none" : "nonpremultiplied");
|
||||
transparency.appendChild(alpha);
|
||||
|
||||
return transparency;
|
||||
}
|
||||
}
|
||||
|
||||
-149
@@ -30,10 +30,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
enum TupleType {
|
||||
// Official:
|
||||
@@ -83,150 +80,4 @@ enum TupleType {
|
||||
public boolean isValidMaxSample(int maxSample) {
|
||||
return maxSample >= minMaxSample && maxSample <= maxMaxSample;
|
||||
}
|
||||
|
||||
|
||||
static TupleType forPNM(Raster raster) {
|
||||
return filterPNM(forPAM(raster));
|
||||
}
|
||||
|
||||
static TupleType forPNM(ImageTypeSpecifier type) {
|
||||
return filterPNM(forPAM(type));
|
||||
}
|
||||
|
||||
private static TupleType filterPNM(TupleType tupleType) {
|
||||
if (tupleType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (tupleType) {
|
||||
case BLACKANDWHITE:
|
||||
return BLACKANDWHITE_WHITE_IS_ZERO;
|
||||
case GRAYSCALE:
|
||||
case RGB:
|
||||
return tupleType;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static TupleType forPAM(Raster raster) {
|
||||
SampleModel sampleModel = raster.getSampleModel();
|
||||
switch (sampleModel.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
case DataBuffer.TYPE_INT:
|
||||
// B/W, Gray or RGB
|
||||
int bands = sampleModel.getNumBands();
|
||||
|
||||
if (bands == 1 && sampleModel.getSampleSize(0) == 1) {
|
||||
return TupleType.BLACKANDWHITE;
|
||||
}
|
||||
else if (bands == 2 && sampleModel.getSampleSize(0) == 1 && sampleModel.getSampleSize(1) == 1) {
|
||||
return TupleType.BLACKANDWHITE_ALPHA;
|
||||
}
|
||||
|
||||
// We can only write 8 or 16 bits/band
|
||||
if (!(sampleModel.getSampleSize(0) == 8 || sampleModel.getSampleSize(0) == 16)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 1; i < bands; i++) {
|
||||
if (sampleModel.getSampleSize(0) != sampleModel.getSampleSize(i)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (bands == 1) {
|
||||
return TupleType.GRAYSCALE;
|
||||
}
|
||||
else if (bands == 2) {
|
||||
return TupleType.GRAYSCALE_ALPHA;
|
||||
}
|
||||
else if (bands == 3) {
|
||||
return TupleType.RGB;
|
||||
}
|
||||
else if (bands == 4) {
|
||||
return TupleType.RGB_ALPHA;
|
||||
}
|
||||
// ...else fall through...
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static TupleType forPAM(ImageTypeSpecifier type) {
|
||||
// Support only 1 bit b/w, 8-16 bit gray and 8-16 bit/sample RGB
|
||||
switch (type.getBufferedImageType()) {
|
||||
// 1 bit b/w or b/w + a
|
||||
case BufferedImage.TYPE_BYTE_BINARY:
|
||||
switch (type.getNumBands()) {
|
||||
case 1:
|
||||
return type.getBitsPerBand(0) == 1 ? TupleType.BLACKANDWHITE : null;
|
||||
case 2:
|
||||
return type.getBitsPerBand(0) == 2 || type.getBitsPerBand(0) == 1 && type.getBitsPerBand(1) == 1 ? TupleType.BLACKANDWHITE_ALPHA : null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
// Gray
|
||||
case BufferedImage.TYPE_BYTE_GRAY:
|
||||
case BufferedImage.TYPE_USHORT_GRAY:
|
||||
return TupleType.GRAYSCALE;
|
||||
// RGB
|
||||
case BufferedImage.TYPE_3BYTE_BGR:
|
||||
case BufferedImage.TYPE_INT_RGB:
|
||||
case BufferedImage.TYPE_INT_BGR:
|
||||
return TupleType.RGB;
|
||||
// RGBA
|
||||
case BufferedImage.TYPE_4BYTE_ABGR:
|
||||
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
|
||||
case BufferedImage.TYPE_INT_ARGB:
|
||||
case BufferedImage.TYPE_INT_ARGB_PRE:
|
||||
return TupleType.RGB_ALPHA;
|
||||
default:
|
||||
// BYTE, USHORT or INT (packed)
|
||||
switch (type.getSampleModel().getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
case DataBuffer.TYPE_INT:
|
||||
// Gray or RGB
|
||||
ColorModel colorModel = type.getColorModel();
|
||||
|
||||
if (!(colorModel instanceof IndexColorModel)) {
|
||||
ColorSpace cs = colorModel.getColorSpace();
|
||||
|
||||
// We can only write 8 or 16 bits/band
|
||||
int bands = type.getNumBands();
|
||||
if (!(type.getBitsPerBand(0) == 8 || type.getBitsPerBand(0) == 16)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 1; i < bands; i++) {
|
||||
if (type.getBitsPerBand(0) != type.getBitsPerBand(i)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cs.getType() == ColorSpace.TYPE_GRAY && bands == 1) {
|
||||
return TupleType.GRAYSCALE;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_GRAY && colorModel.hasAlpha() && bands == 2) {
|
||||
return TupleType.GRAYSCALE_ALPHA;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_RGB && bands == 3) {
|
||||
return TupleType.RGB;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_RGB && colorModel.hasAlpha() && bands == 4) {
|
||||
return TupleType.RGB_ALPHA;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_CMYK && bands == 4) {
|
||||
return TupleType.CMYK;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_CMYK && colorModel.hasAlpha() && bands == 5) {
|
||||
return TupleType.CMYK_ALPHA;
|
||||
}
|
||||
// ...else fall through...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
-89
@@ -30,23 +30,15 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest.assertClassExists;
|
||||
import static com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest.assertClassesExist;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* PAMImageWriterSpiTest.
|
||||
@@ -73,85 +65,4 @@ public class PAMImageWriterSpiTest {
|
||||
public void getOutputTypes() {
|
||||
assertNotNull(spi.getOutputTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageBinary() {
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canNotEncodeImageIndexed() {
|
||||
assertFalse(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED)));
|
||||
assertFalse(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_INDEXED)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageGray() {
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_USHORT_GRAY)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageGrayAlpha() {
|
||||
ComponentColorModel grayAlphaByte = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
|
||||
assertTrue(spi.canEncodeImage(new ImageTypeSpecifier(grayAlphaByte, grayAlphaByte.createCompatibleSampleModel(1, 1))));
|
||||
|
||||
ComponentColorModel grayAlphaUShort = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT);
|
||||
assertTrue(spi.canEncodeImage(new ImageTypeSpecifier(grayAlphaUShort, grayAlphaUShort.createCompatibleSampleModel(1, 1))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageRGB() {
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_BGR)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_BGR)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageARGB() {
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR_PRE)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB_PRE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageCMYK() {
|
||||
ComponentColorModel cmykByte = new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
assertTrue(spi.canEncodeImage(new ImageTypeSpecifier(cmykByte, cmykByte.createCompatibleSampleModel(1, 1))));
|
||||
|
||||
ComponentColorModel cmykUShort = new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
|
||||
assertTrue(spi.canEncodeImage(new ImageTypeSpecifier(cmykUShort, cmykUShort.createCompatibleSampleModel(1, 1))));
|
||||
|
||||
ComponentColorModel cmykAlphaByte = new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
|
||||
assertTrue(spi.canEncodeImage(new ImageTypeSpecifier(cmykAlphaByte, cmykAlphaByte.createCompatibleSampleModel(1, 1))));
|
||||
|
||||
ComponentColorModel cmykAlphaUShort = new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT);
|
||||
assertTrue(spi.canEncodeImage(new ImageTypeSpecifier(cmykAlphaUShort, cmykAlphaUShort.createCompatibleSampleModel(1, 1))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canNotEncodeImageNonGrayOrRGB() {
|
||||
ComponentColorModel xyzByte = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_CIEXYZ), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
assertFalse(spi.canEncodeImage(new ImageTypeSpecifier(xyzByte, xyzByte.createCompatibleSampleModel(1, 1))));
|
||||
|
||||
ComponentColorModel xyzUshort = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_CIEXYZ), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
|
||||
assertFalse(spi.canEncodeImage(new ImageTypeSpecifier(xyzUshort, xyzUshort.createCompatibleSampleModel(1, 1))));
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PAMImageWriterTest extends ImageWriterAbstractTest<PNMImageWriter> {
|
||||
// NOTE: It's the same writer, however, the different SPI configures PAM mode, and enables extra formats
|
||||
@Override
|
||||
protected ImageWriterSpi createProvider() {
|
||||
return new PAMImageWriterSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends RenderedImage> getTestData() {
|
||||
return Arrays.asList(
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_BYTE_BINARY),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_BYTE_GRAY),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_USHORT_GRAY),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_4BYTE_ABGR),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_INT_BGR),
|
||||
new BufferedImage(10, 10, BufferedImage.TYPE_INT_BGR),
|
||||
new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB),
|
||||
new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB_PRE),
|
||||
new BufferedImage(new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpace.CS_GRAY), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT), Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, 10, 10, 2, null), false, null),
|
||||
new BufferedImage(new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE), Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 10, 10, 4, null), false, null),
|
||||
new BufferedImage(new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT), Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, 10, 10, 5, null), false, null)
|
||||
);
|
||||
}
|
||||
}
|
||||
-72
@@ -32,19 +32,13 @@ package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest.assertClassExists;
|
||||
import static com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest.assertClassesExist;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* PNMImageWriterSpiTest.
|
||||
@@ -71,70 +65,4 @@ public class PNMImageWriterSpiTest {
|
||||
public void getOutputTypes() {
|
||||
assertNotNull(spi.getOutputTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageBinary() {
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canNotEncodeImageIndexed() {
|
||||
assertFalse(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED)));
|
||||
assertFalse(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_INDEXED)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageGray() {
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_USHORT_GRAY)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canNotEncodeImageGrayAlpha() {
|
||||
ComponentColorModel grayAlphaByte = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
|
||||
assertFalse(spi.canEncodeImage(new ImageTypeSpecifier(grayAlphaByte, grayAlphaByte.createCompatibleSampleModel(1, 1))));
|
||||
|
||||
ComponentColorModel grayAlphaUShort = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT);
|
||||
assertFalse(spi.canEncodeImage(new ImageTypeSpecifier(grayAlphaUShort, grayAlphaUShort.createCompatibleSampleModel(1, 1))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canEncodeImageRGB() {
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB)));
|
||||
|
||||
assertTrue(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_BGR)));
|
||||
assertTrue(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_BGR)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canNotEncodeImageARGB() {
|
||||
assertFalse(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR)));
|
||||
assertFalse(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)));
|
||||
|
||||
assertFalse(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR_PRE)));
|
||||
assertFalse(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE)));
|
||||
|
||||
assertFalse(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)));
|
||||
assertFalse(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB)));
|
||||
|
||||
assertFalse(spi.canEncodeImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE)));
|
||||
assertFalse(spi.canEncodeImage(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB_PRE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canNotEncodeImageNonGrayOrRGB() {
|
||||
ComponentColorModel xyzByte = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_CIEXYZ), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
assertFalse(spi.canEncodeImage(new ImageTypeSpecifier(xyzByte, xyzByte.createCompatibleSampleModel(1, 1))));
|
||||
|
||||
ComponentColorModel xyzUshort = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_CIEXYZ), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
|
||||
assertFalse(spi.canEncodeImage(new ImageTypeSpecifier(xyzUshort, xyzUshort.createCompatibleSampleModel(1, 1))));
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -3,7 +3,8 @@ package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -16,12 +17,10 @@ public class PNMImageWriterTest extends ImageWriterAbstractTest<PNMImageWriter>
|
||||
@Override
|
||||
protected List<? extends RenderedImage> getTestData() {
|
||||
return Arrays.asList(
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_BYTE_BINARY),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_BYTE_GRAY),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_USHORT_GRAY),
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_INT_BGR),
|
||||
new BufferedImage(10, 10, BufferedImage.TYPE_INT_BGR)
|
||||
new BufferedImage(100, 100, BufferedImage.TYPE_4BYTE_ABGR)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-psd</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
|
||||
|
||||
+1
-1
@@ -944,7 +944,7 @@ public final class PSDImageReader extends ImageReaderBase {
|
||||
if (metadata.layerInfo == null) {
|
||||
while (imageInput.getStreamPosition() + 12 < metadata.layerAndMaskInfoStart + layerAndMaskInfoLength) {
|
||||
int resSig = imageInput.readInt();
|
||||
if (resSig != PSD.RESOURCE_TYPE && resSig != PSD.RESOURCE_TYPE_LONG) {
|
||||
if (resSig != PSD.RESOURCE_TYPE) {
|
||||
processWarningOccurred(String.format("Bad resource alignment, expected: '8BIM' was '%s'", PSDUtil.intToStr(resSig)));
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-2
@@ -103,8 +103,7 @@ final class PSDUtil {
|
||||
final int[] byteCounts, long compressedLength) throws IOException {
|
||||
switch (compression) {
|
||||
case PSD.COMPRESSION_NONE:
|
||||
long streamLength = stream.length();
|
||||
return new SubImageInputStream(stream, streamLength < 0 ? Long.MAX_VALUE : streamLength);
|
||||
return new SubImageInputStream(stream, stream.length());
|
||||
|
||||
case PSD.COMPRESSION_RLE:
|
||||
return new DirectImageInputStream(new SequenceInputStream(new LazyPackBitsStreamEnumeration(byteCounts, stream)));
|
||||
|
||||
-25
@@ -31,12 +31,10 @@
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.imageio.stream.DirectImageInputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.psd.PSDUtil.createDecompressorStream;
|
||||
@@ -67,29 +65,6 @@ public class PSDUtilDecompressorStreamTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUncompressedUnknownLength() throws IOException {
|
||||
// Data represents 3 x 3 raster with 8 bit samples, all 0x7f's
|
||||
byte[] data = new byte[] {
|
||||
0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f
|
||||
};
|
||||
try (ImageInputStream input = createDecompressorStream(new DirectImageInputStream(new ByteArrayInputStream(data)), PSD.COMPRESSION_NONE, 3, 8, null, 9)) {
|
||||
byte[] row = new byte[3];
|
||||
|
||||
for (int y = 0; y < 3; y++) {
|
||||
input.readFully(row);
|
||||
|
||||
for (byte b : row) {
|
||||
assertEquals((byte) 0x7f, b);
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(-1, input.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackBits() throws IOException {
|
||||
// Data represents 3 x 3 raster with 8 bit samples, all 42's
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-reference</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-sgi</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ package com.twelvemonkeys.imageio.plugins.sgi;
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
final class SGIHeader {
|
||||
private int compression;
|
||||
@@ -157,6 +157,6 @@ final class SGIHeader {
|
||||
}
|
||||
}
|
||||
|
||||
return new String(bytes, 0, len, StandardCharsets.US_ASCII);
|
||||
return new String(bytes, 0, len, Charset.forName("ASCII"));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -45,7 +45,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
@@ -378,10 +378,11 @@ public final class SGIImageReader extends ImageReaderBase {
|
||||
imageInput.seek(imageInput.getFlushedPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
return new SGIMetadata(rawType, header);
|
||||
@Override public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return new SGIMetadata(header);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
+191
-21
@@ -1,39 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.sgi;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
final class SGIMetadata extends StandardImageMetadataSupport {
|
||||
public SGIMetadata(ImageTypeSpecifier type, SGIHeader header) {
|
||||
super(builder(type)
|
||||
.withSignificantBitsPerSample(computeSignificantBits(header))
|
||||
.withCompressionTypeName(compressionName(header))
|
||||
.withOrientation(ImageOrientation.FlipV)
|
||||
.withTextEntry("DocumentName", header.getName())
|
||||
);
|
||||
final class SGIMetadata extends AbstractMetadata {
|
||||
private final SGIHeader header;
|
||||
|
||||
SGIMetadata(final SGIHeader header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
private static int computeSignificantBits(SGIHeader header) {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
// NOTE: There doesn't seem to be any god way to determine color space, other than by convention
|
||||
// 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
switch (header.getColorMode()) {
|
||||
case SGI.COLORMODE_NORMAL:
|
||||
switch (header.getChannels()) {
|
||||
case 1:
|
||||
case 2:
|
||||
csType.setAttribute("name", "GRAY");
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
default:
|
||||
csType.setAttribute("name", Integer.toHexString(header.getChannels()).toUpperCase() + "CLR");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// SGIIMAGE.TXT describes these as RGB
|
||||
case SGI.COLORMODE_DITHERED:
|
||||
case SGI.COLORMODE_SCREEN:
|
||||
case SGI.COLORMODE_COLORMAP:
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
}
|
||||
|
||||
if (csType.getAttribute("name") != null) {
|
||||
chroma.appendChild(csType);
|
||||
}
|
||||
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
numChannels.setAttribute("value", Integer.toString(header.getChannels()));
|
||||
chroma.appendChild(numChannels);
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
chroma.appendChild(blackIsZero);
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
// No compression
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
if (header.getCompression() != SGI.COMPRESSION_NONE) {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", header.getCompression() == SGI.COMPRESSION_RLE
|
||||
? "RLE"
|
||||
: "Uknown");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
node.appendChild(sampleFormat);
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBytesPerPixel() * 8)));
|
||||
node.appendChild(bitsPerSample);
|
||||
|
||||
IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
|
||||
significantBitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(computeSignificantBits())));
|
||||
node.appendChild(significantBitsPerSample);
|
||||
|
||||
IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
|
||||
sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private int computeSignificantBits() {
|
||||
int significantBits = 0;
|
||||
|
||||
int maxSample = header.getMaxValue();
|
||||
|
||||
int significantBits = 1;
|
||||
|
||||
while ((maxSample >>>= 1) != 0) {
|
||||
while (maxSample > 0) {
|
||||
maxSample >>>= 1;
|
||||
significantBits++;
|
||||
}
|
||||
|
||||
return significantBits;
|
||||
}
|
||||
|
||||
private static String compressionName(SGIHeader header) {
|
||||
switch (header.getCompression()) {
|
||||
case SGI.COMPRESSION_NONE:
|
||||
return "None";
|
||||
case SGI.COMPRESSION_RLE:
|
||||
return "RLE";
|
||||
private String createListValue(final int itemCount, final String... values) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
buffer.append(values[i % values.length]);
|
||||
}
|
||||
|
||||
return "Uknown";
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientation.setAttribute("value", "FlipV");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// No document node
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
if (!header.getName().isEmpty()) {
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
||||
textEntry.setAttribute("keyword", "name");
|
||||
textEntry.setAttribute("value", header.getName());
|
||||
text.appendChild(textEntry);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// No tiling
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
// NOTE: There doesn't seem to be any god way to determine transparency, other than by convention
|
||||
// 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
|
||||
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
alpha.setAttribute("value", header.getChannels() == 1 || header.getChannels() == 3
|
||||
? "none"
|
||||
: "nonpremultiplied");
|
||||
transparency.appendChild(alpha);
|
||||
|
||||
return transparency;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tga</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
|
||||
|
||||
+10
-12
@@ -31,16 +31,18 @@
|
||||
package com.twelvemonkeys.imageio.plugins.tga;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.awt.color.ColorSpace.*;
|
||||
import static java.awt.color.ColorSpace.TYPE_GRAY;
|
||||
import static java.awt.color.ColorSpace.TYPE_RGB;
|
||||
|
||||
final class TGAHeader {
|
||||
|
||||
@@ -116,14 +118,10 @@ final class TGAHeader {
|
||||
'}';
|
||||
}
|
||||
|
||||
static TGAHeader from(final ImageTypeSpecifier type, final boolean compressed) {
|
||||
return from(type, 0, 0, compressed);
|
||||
}
|
||||
static TGAHeader from(final RenderedImage image, final boolean compressed) {
|
||||
notNull(image, "image");
|
||||
|
||||
static TGAHeader from(final ImageTypeSpecifier type, int width, int height, final boolean compressed) {
|
||||
notNull(type, "type");
|
||||
|
||||
ColorModel colorModel = type.getColorModel();
|
||||
ColorModel colorModel = image.getColorModel();
|
||||
IndexColorModel colorMap = colorModel instanceof IndexColorModel ? (IndexColorModel) colorModel : null;
|
||||
|
||||
TGAHeader header = new TGAHeader();
|
||||
@@ -137,8 +135,8 @@ final class TGAHeader {
|
||||
header.x = 0;
|
||||
header.y = 0;
|
||||
|
||||
header.width = width;
|
||||
header.height = height;
|
||||
header.width = image.getWidth(); // TODO: Param source region/subsampling might affect this
|
||||
header.height = image.getHeight(); // // TODO: Param source region/subsampling might affect this
|
||||
header.pixelDepth = colorModel.getPixelSize() == 15 ? 16 : colorModel.getPixelSize();
|
||||
|
||||
header.origin = TGA.ORIGIN_UPPER_LEFT; // TODO: Allow parameter to control this?
|
||||
|
||||
+5
-3
@@ -47,7 +47,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
@@ -538,8 +538,10 @@ final class TGAImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
return new TGAMetadata(rawType, header, extensions);
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return new TGAMetadata(header, extensions);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
+9
-13
@@ -37,16 +37,12 @@ import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataOutput;
|
||||
import java.io.File;
|
||||
@@ -69,7 +65,8 @@ final class TGAImageWriter extends ImageWriterBase {
|
||||
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
|
||||
Validate.notNull(imageType, "imageType");
|
||||
|
||||
return new TGAMetadata(imageType, TGAHeader.from(imageType, isRLE(param, null)), null);
|
||||
TGAHeader header = TGAHeader.from(imageType.createBufferedImage(1, 1), isRLE(param, null));
|
||||
return new TGAMetadata(header, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,8 +107,7 @@ final class TGAImageWriter extends ImageWriterBase {
|
||||
|
||||
final boolean compressed = isRLE(param, image.getMetadata());
|
||||
RenderedImage renderedImage = image.getRenderedImage();
|
||||
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromRenderedImage(renderedImage);
|
||||
TGAHeader header = TGAHeader.from(type, renderedImage.getWidth(), renderedImage.getHeight(), compressed);
|
||||
TGAHeader header = TGAHeader.from(renderedImage, compressed);
|
||||
|
||||
header.write(imageOutput);
|
||||
|
||||
@@ -121,7 +117,7 @@ final class TGAImageWriter extends ImageWriterBase {
|
||||
? ImageTypeSpecifiers.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {2, 1, 0, 3}, DataBuffer.TYPE_BYTE, true, false).createBufferedImage(renderedImage.getWidth(), 1).getRaster()
|
||||
: renderedImage.getSampleModel().getTransferType() == DataBuffer.TYPE_INT
|
||||
? ImageTypeSpecifiers.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, false, false).createBufferedImage(renderedImage.getWidth(), 1).getRaster()
|
||||
: type.createBufferedImage(renderedImage.getWidth(), 1).getRaster();
|
||||
: ImageTypeSpecifier.createFromRenderedImage(renderedImage).createBufferedImage(renderedImage.getWidth(), 1).getRaster();
|
||||
|
||||
final DataBuffer buffer = rowRaster.getDataBuffer();
|
||||
|
||||
@@ -139,7 +135,7 @@ final class TGAImageWriter extends ImageWriterBase {
|
||||
break;
|
||||
}
|
||||
|
||||
DataOutput imageOutput = compressed ? createRLEStream(this.imageOutput, header.getPixelDepth()) : this.imageOutput;
|
||||
DataOutput imageOutput = compressed ? createRLEStream(header, this.imageOutput) : this.imageOutput;
|
||||
|
||||
switch (buffer.getDataType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
@@ -178,8 +174,8 @@ final class TGAImageWriter extends ImageWriterBase {
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
private static LittleEndianDataOutputStream createRLEStream(final ImageOutputStream stream, int pixelDepth) {
|
||||
return new LittleEndianDataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(stream), new RLEEncoder(pixelDepth)));
|
||||
private static LittleEndianDataOutputStream createRLEStream(final TGAHeader header, final ImageOutputStream stream) {
|
||||
return new LittleEndianDataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(stream), new RLEEncoder(header.getPixelDepth())));
|
||||
}
|
||||
|
||||
// TODO: Refactor to common util
|
||||
|
||||
+300
-43
@@ -1,83 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tga;
|
||||
|
||||
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.*;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.util.Calendar;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
final class TGAMetadata extends StandardImageMetadataSupport {
|
||||
TGAMetadata(ImageTypeSpecifier type, TGAHeader header, TGAExtensions extensions) {
|
||||
super(builder(type)
|
||||
.withCompressionTypeName(compressionName(header))
|
||||
.withPixelAspectRatio(pixelAspectRatio(extensions))
|
||||
.withOrientation(orientation(header))
|
||||
.withFormatVersion(extensions == null ? "1.0" : "2.0")
|
||||
.withDocumentCreationTime(documentCreationTime(extensions))
|
||||
.withTextEntries(textEntries(header, extensions))
|
||||
);
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
final class TGAMetadata extends AbstractMetadata {
|
||||
private final TGAHeader header;
|
||||
private final TGAExtensions extensions;
|
||||
|
||||
TGAMetadata(final TGAHeader header, final TGAExtensions extensions) {
|
||||
this.header = notNull(header, "header");
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
private static String compressionName(TGAHeader header) {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
|
||||
switch (header.getImageType()) {
|
||||
case TGA.IMAGETYPE_NONE:
|
||||
case TGA.IMAGETYPE_COLORMAPPED:
|
||||
case TGA.IMAGETYPE_TRUECOLOR:
|
||||
case TGA.IMAGETYPE_MONOCHROME:
|
||||
return "None";
|
||||
case TGA.IMAGETYPE_MONOCHROME_RLE:
|
||||
csType.setAttribute("name", "GRAY");
|
||||
break;
|
||||
|
||||
case TGA.IMAGETYPE_TRUECOLOR:
|
||||
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
||||
case TGA.IMAGETYPE_COLORMAPPED:
|
||||
case TGA.IMAGETYPE_COLORMAPPED_RLE:
|
||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
|
||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
default:
|
||||
csType.setAttribute("name", "Unknown");
|
||||
}
|
||||
|
||||
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
chroma.appendChild(numChannels);
|
||||
switch (header.getPixelDepth()) {
|
||||
case 8:
|
||||
if (header.getImageType() == TGA.IMAGETYPE_MONOCHROME || header.getImageType() == TGA.IMAGETYPE_MONOCHROME_RLE) {
|
||||
numChannels.setAttribute("value", Integer.toString(1));
|
||||
}
|
||||
else {
|
||||
numChannels.setAttribute("value", Integer.toString(3));
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
if (header.getAttributeBits() > 0 && extensions != null && extensions.hasAlpha()) {
|
||||
numChannels.setAttribute("value", Integer.toString(4));
|
||||
}
|
||||
else {
|
||||
numChannels.setAttribute("value", Integer.toString(3));
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
numChannels.setAttribute("value", Integer.toString(3));
|
||||
break;
|
||||
case 32:
|
||||
numChannels.setAttribute("value", Integer.toString(4));
|
||||
break;
|
||||
}
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
chroma.appendChild(blackIsZero);
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
|
||||
// NOTE: TGA files may contain a color map, even if true color...
|
||||
// Not sure if this is a good idea to expose to the meta data,
|
||||
// as it might be unexpected... Then again...
|
||||
IndexColorModel colorMap = header.getColorMap();
|
||||
if (colorMap != null) {
|
||||
IIOMetadataNode palette = new IIOMetadataNode("Palette");
|
||||
chroma.appendChild(palette);
|
||||
|
||||
for (int i = 0; i < colorMap.getMapSize(); i++) {
|
||||
IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
|
||||
palette.appendChild(paletteEntry);
|
||||
paletteEntry.setAttribute("index", Integer.toString(i));
|
||||
|
||||
paletteEntry.setAttribute("red", Integer.toString(colorMap.getRed(i)));
|
||||
paletteEntry.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
|
||||
paletteEntry.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions != null && extensions.getBackgroundColor() != 0) {
|
||||
Color background = new Color(extensions.getBackgroundColor(), true);
|
||||
|
||||
IIOMetadataNode backgroundColor = new IIOMetadataNode("BackgroundColor");
|
||||
chroma.appendChild(backgroundColor);
|
||||
|
||||
backgroundColor.setAttribute("red", Integer.toString(background.getRed()));
|
||||
backgroundColor.setAttribute("green", Integer.toString(background.getGreen()));
|
||||
backgroundColor.setAttribute("blue", Integer.toString(background.getBlue()));
|
||||
}
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
switch (header.getImageType()) {
|
||||
case TGA.IMAGETYPE_COLORMAPPED_RLE:
|
||||
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
||||
case TGA.IMAGETYPE_MONOCHROME_RLE:
|
||||
return "RLE";
|
||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
|
||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
node.appendChild(compressionTypeName);
|
||||
String value = header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN || header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE
|
||||
? "Unknown" : "RLE";
|
||||
compressionTypeName.setAttribute("value", value);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
node.appendChild(lossless);
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
|
||||
return node;
|
||||
default:
|
||||
return "Unknown";
|
||||
// No compression
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static double pixelAspectRatio(TGAExtensions extensions) {
|
||||
return extensions != null ? extensions.getPixelAspectRatio() : 1f;
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
node.appendChild(planarConfiguration);
|
||||
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
node.appendChild(sampleFormat);
|
||||
|
||||
switch (header.getImageType()) {
|
||||
case TGA.IMAGETYPE_COLORMAPPED:
|
||||
case TGA.IMAGETYPE_COLORMAPPED_RLE:
|
||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
|
||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
|
||||
sampleFormat.setAttribute("value", "Index");
|
||||
break;
|
||||
default:
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
break;
|
||||
}
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
node.appendChild(bitsPerSample);
|
||||
|
||||
switch (header.getPixelDepth()) {
|
||||
case 8:
|
||||
bitsPerSample.setAttribute("value", createListValue(1, "8"));
|
||||
break;
|
||||
case 16:
|
||||
if (header.getImageType() == TGA.IMAGETYPE_MONOCHROME || header.getImageType() == TGA.IMAGETYPE_MONOCHROME_RLE) {
|
||||
bitsPerSample.setAttribute("value", "16");
|
||||
}
|
||||
else if (header.getAttributeBits() > 0 && extensions != null && extensions.hasAlpha()) {
|
||||
bitsPerSample.setAttribute("value", "5, 5, 5, 1");
|
||||
}
|
||||
else {
|
||||
bitsPerSample.setAttribute("value", createListValue(3, "5"));
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
bitsPerSample.setAttribute("value", createListValue(3, "8"));
|
||||
break;
|
||||
case 32:
|
||||
bitsPerSample.setAttribute("value", createListValue(4, "8"));
|
||||
break;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static ImageOrientation orientation(TGAHeader header) {
|
||||
switch (header.origin) {
|
||||
private String createListValue(final int itemCount, final String... values) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
buffer.append(values[i % values.length]);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
switch (header.getOrigin()) {
|
||||
case TGA.ORIGIN_LOWER_LEFT:
|
||||
return ImageOrientation.FlipH;
|
||||
imageOrientation.setAttribute("value", "FlipH");
|
||||
break;
|
||||
case TGA.ORIGIN_LOWER_RIGHT:
|
||||
return ImageOrientation.Rotate180;
|
||||
imageOrientation.setAttribute("value", "Rotate180");
|
||||
break;
|
||||
case TGA.ORIGIN_UPPER_LEFT:
|
||||
return ImageOrientation.Normal;
|
||||
imageOrientation.setAttribute("value", "Normal");
|
||||
break;
|
||||
case TGA.ORIGIN_UPPER_RIGHT:
|
||||
return ImageOrientation.FlipV;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown orientation: " + header.origin);
|
||||
imageOrientation.setAttribute("value", "FlipV");
|
||||
break;
|
||||
}
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
|
||||
dimension.appendChild(pixelAspectRatio);
|
||||
pixelAspectRatio.setAttribute("value", extensions != null ? String.valueOf(extensions.getPixelAspectRatio()) : "1.0");
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
private static Calendar documentCreationTime(TGAExtensions extensions) {
|
||||
return extensions != null ? extensions.creationDate : null;
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||
|
||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||
document.appendChild(formatVersion);
|
||||
formatVersion.setAttribute("value", extensions == null ? "1.0" : "2.0");
|
||||
|
||||
// ImageCreationTime from extensions date
|
||||
if (extensions != null && extensions.getCreationDate() != null) {
|
||||
IIOMetadataNode imageCreationTime = new IIOMetadataNode("ImageCreationTime");
|
||||
document.appendChild(imageCreationTime);
|
||||
|
||||
Calendar date = extensions.getCreationDate();
|
||||
|
||||
imageCreationTime.setAttribute("year", String.valueOf(date.get(Calendar.YEAR)));
|
||||
imageCreationTime.setAttribute("month", String.valueOf(date.get(Calendar.MONTH) + 1));
|
||||
imageCreationTime.setAttribute("day", String.valueOf(date.get(Calendar.DAY_OF_MONTH)));
|
||||
imageCreationTime.setAttribute("hour", String.valueOf(date.get(Calendar.HOUR_OF_DAY)));
|
||||
imageCreationTime.setAttribute("minute", String.valueOf(date.get(Calendar.MINUTE)));
|
||||
imageCreationTime.setAttribute("second", String.valueOf(date.get(Calendar.SECOND)));
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private static Map<String, String> textEntries(TGAHeader header, TGAExtensions extensions) {
|
||||
LinkedHashMap<String, String> textEntries = new LinkedHashMap<>();
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
// NOTE: Keywords follow TIFF standard naming
|
||||
putIfValue(textEntries, "DocumentName", header.getIdentification());
|
||||
// NOTE: Names corresponds to equivalent fields in TIFF
|
||||
appendTextEntry(text, "DocumentName", header.getIdentification());
|
||||
|
||||
if (extensions != null) {
|
||||
putIfValue(textEntries, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : (extensions.getSoftware() + " " + extensions.getSoftwareVersion()));
|
||||
putIfValue(textEntries, "Artist", extensions.getAuthorName());
|
||||
putIfValue(textEntries, "UserComment", extensions.getAuthorComments());
|
||||
appendTextEntry(text, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : (extensions.getSoftware() + " " + extensions.getSoftwareVersion()));
|
||||
appendTextEntry(text, "Artist", extensions.getAuthorName());
|
||||
appendTextEntry(text, "UserComment", extensions.getAuthorComments());
|
||||
}
|
||||
|
||||
return textEntries;
|
||||
return text.hasChildNodes() ? text : null;
|
||||
}
|
||||
|
||||
private static void putIfValue(final Map<String, String> textEntries, final String keyword, final String value) {
|
||||
private void appendTextEntry(final IIOMetadataNode parent, final String keyword, final String value) {
|
||||
if (value != null && !value.isEmpty()) {
|
||||
textEntries.put(keyword, value);
|
||||
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
||||
parent.appendChild(textEntry);
|
||||
textEntry.setAttribute("keyword", keyword);
|
||||
textEntry.setAttribute("value", value);
|
||||
}
|
||||
}
|
||||
|
||||
// No tiling
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
transparency.appendChild(alpha);
|
||||
|
||||
if (extensions != null) {
|
||||
if (extensions.hasAlpha()) {
|
||||
alpha.setAttribute("value", extensions.isAlphaPremultiplied() ? "premultiplied" : "nonpremultiplied");
|
||||
}
|
||||
else {
|
||||
alpha.setAttribute("value", "none");
|
||||
}
|
||||
}
|
||||
else if (header.getAttributeBits() == 8) {
|
||||
alpha.setAttribute("value", "nonpremultiplied");
|
||||
}
|
||||
else {
|
||||
alpha.setAttribute("value", "none");
|
||||
}
|
||||
|
||||
return transparency;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-18
@@ -30,17 +30,12 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tga;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
|
||||
/**
|
||||
@@ -51,9 +46,6 @@ import static org.junit.Assume.assumeFalse;
|
||||
* @version $Id: TGAImageWriteParamTest.java,v 1.0 08/04/2021 haraldk Exp$
|
||||
*/
|
||||
public class TGAImageWriteParamTest {
|
||||
|
||||
private static final ImageTypeSpecifier TYPE_3BYTE_BGR = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||
|
||||
@Test
|
||||
public void testDefaultCopyFromMetadata() {
|
||||
TGAImageWriteParam param = new TGAImageWriteParam();
|
||||
@@ -115,8 +107,8 @@ public class TGAImageWriteParamTest {
|
||||
ImageWriteParam param = new ImageWriteParam(null);
|
||||
assumeFalse(param.canWriteCompressed());
|
||||
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, false), null)));
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false), null)));
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,7 +116,7 @@ public class TGAImageWriteParamTest {
|
||||
ImageWriteParam param = new TGAImageWriteParam();
|
||||
param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
|
||||
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,7 +124,7 @@ public class TGAImageWriteParamTest {
|
||||
ImageWriteParam param = new TGAImageWriteParam();
|
||||
param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
|
||||
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,12 +132,12 @@ public class TGAImageWriteParamTest {
|
||||
TGAImageWriteParam param = new TGAImageWriteParam();
|
||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, false), null)));
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false), null)));
|
||||
assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
|
||||
|
||||
param.setCompressionType("RLE");
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, false), null)));
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false), null)));
|
||||
assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
|
||||
}
|
||||
|
||||
}
|
||||
+67
-94
@@ -30,18 +30,14 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tga;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.util.Calendar;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -54,15 +50,10 @@ import static org.junit.Assert.*;
|
||||
* @version $Id: TGAMetadataTest.java,v 1.0 08/04/2021 haraldk Exp$
|
||||
*/
|
||||
public class TGAMetadataTest {
|
||||
|
||||
private static final ImageTypeSpecifier TYPE_BYTE_GRAY = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
|
||||
|
||||
private static final ImageTypeSpecifier TYPE_3BYTE_BGR = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||
|
||||
@Test
|
||||
public void testStandardFeatures() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
|
||||
final TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
|
||||
final TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
// Standard metadata format
|
||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||
@@ -92,10 +83,10 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardChromaGray() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, false);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), false);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
@@ -117,10 +108,10 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardChromaRGB() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
@@ -144,11 +135,10 @@ public class TGAMetadataTest {
|
||||
public void testStandardChromaPalette() {
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, -1);
|
||||
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
|
||||
TGAHeader header = TGAHeader.from(type, false);
|
||||
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), false);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(4, chroma.getLength());
|
||||
@@ -184,10 +174,10 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionRLE() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode compression = getStandardNode(metadata, "Compression");
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
assertEquals("Compression", compression.getNodeName());
|
||||
assertEquals(2, compression.getLength());
|
||||
@@ -205,18 +195,18 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionNone() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
assertNull(getStandardNode(metadata, "Compression")); // No compression, all default...
|
||||
assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataGray() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -238,10 +228,10 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardDataRGB() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -263,11 +253,10 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardDataRGBA() {
|
||||
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
|
||||
TGAHeader header = TGAHeader.from(type, true);
|
||||
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -291,11 +280,10 @@ public class TGAMetadataTest {
|
||||
public void testStandardDataPalette() {
|
||||
byte[] rgb = new byte[1 << 8]; // Colors doesn't really matter here
|
||||
IndexColorModel indexColorModel = new IndexColorModel(8, rgb.length, rgb, rgb, rgb, 0);
|
||||
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
|
||||
TGAHeader header = TGAHeader.from(type, true);
|
||||
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
@@ -317,56 +305,53 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionNormal() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(2, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(imageOrientation.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionFlipH() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
||||
header.origin = TGA.ORIGIN_LOWER_LEFT;
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(2, dimension.getLength());
|
||||
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("FlipH", imageOrientation.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(imageOrientation.getNextSibling()); // No more children
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDocument() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode document = getStandardNode(metadata, "Document");
|
||||
IIOMetadataNode document = metadata.getStandardDocumentNode();
|
||||
assertNotNull(document);
|
||||
assertEquals("Document", document.getNodeName());
|
||||
assertEquals(1, document.getLength());
|
||||
@@ -380,13 +365,13 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardDocumentExtensions() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
||||
TGAExtensions extensions = new TGAExtensions();
|
||||
extensions.creationDate = Calendar.getInstance();
|
||||
extensions.creationDate.set(2021, Calendar.APRIL, 8, 18, 55, 0);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, extensions);
|
||||
TGAMetadata metadata = new TGAMetadata(header, extensions);
|
||||
|
||||
IIOMetadataNode document = getStandardNode(metadata, "Document");
|
||||
IIOMetadataNode document = metadata.getStandardDocumentNode();
|
||||
assertNotNull(document);
|
||||
assertEquals("Document", document.getNodeName());
|
||||
assertEquals(2, document.getLength());
|
||||
@@ -409,7 +394,7 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardText() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
||||
header.identification = "MY_FILE.TGA";
|
||||
|
||||
TGAExtensions extensions = new TGAExtensions();
|
||||
@@ -417,9 +402,9 @@ public class TGAMetadataTest {
|
||||
extensions.authorName = "Harald K";
|
||||
extensions.authorComments = "Comments, comments... ";
|
||||
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, extensions);
|
||||
TGAMetadata metadata = new TGAMetadata(header, extensions);
|
||||
|
||||
IIOMetadataNode text = getStandardNode(metadata, "Text");
|
||||
IIOMetadataNode text = metadata.getStandardTextNode();
|
||||
assertNotNull(text);
|
||||
assertEquals("Text", text.getNodeName());
|
||||
assertEquals(4, text.getLength());
|
||||
@@ -447,10 +432,10 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyRGB() {
|
||||
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
|
||||
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
@@ -464,11 +449,10 @@ public class TGAMetadataTest {
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyRGBA() {
|
||||
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
|
||||
TGAHeader header = TGAHeader.from(type, true);
|
||||
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
@@ -484,30 +468,19 @@ public class TGAMetadataTest {
|
||||
public void testStandardTransparencyPalette() {
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, 1);
|
||||
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
|
||||
TGAHeader header = TGAHeader.from(type, true);
|
||||
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), true);
|
||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
||||
|
||||
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(2, transparency.getLength());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", alpha.getNodeName());
|
||||
assertEquals("nonpremultiplied", alpha.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode transparentIndex = (IIOMetadataNode) alpha.getNextSibling();
|
||||
assertEquals("TransparentIndex", transparentIndex.getNodeName());
|
||||
assertEquals("1", transparentIndex.getAttribute("value"));
|
||||
|
||||
assertNull(transparentIndex.getNextSibling()); // No more children
|
||||
assertNull(alpha.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
private IIOMetadataNode getStandardNode(IIOMetadata metadata, String nodeName) {
|
||||
IIOMetadataNode asTree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
NodeList nodes = asTree.getElementsByTagName(nodeName);
|
||||
|
||||
return nodes.getLength() > 0 ? (IIOMetadataNode) nodes.item(0) : null;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-thumbsdb</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff-jai-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JAI Metadata Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff-jdk-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.4</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user