diff --git a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTest.java b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTest.java
index 23b4e057..863f04b7 100644
--- a/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTest.java
+++ b/common/common-io/src/test/java/com/twelvemonkeys/io/enc/EncoderAbstractTest.java
@@ -32,13 +32,13 @@ package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.ObjectAbstractTest;
+
import org.junit.Test;
import java.io.*;
-import java.util.Arrays;
import java.util.Random;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.fail;
/**
@@ -73,7 +73,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
}
}
- private byte[] createData(final int pLength) throws Exception {
+ private byte[] createData(final int pLength) {
byte[] bytes = new byte[pLength];
RANDOM.nextBytes(bytes);
return bytes;
@@ -82,9 +82,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
private void runStreamTest(final int pLength) throws Exception {
byte[] data = createData(pLength);
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
- OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
- try {
+ try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
// Provoke failure for encoders that doesn't take array offset properly into account
int off = (data.length + 1) / 2;
out.write(data, 0, off);
@@ -92,9 +91,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
out.write(data, off, data.length - off);
}
}
- finally {
- out.close();
- }
byte[] encoded = outBytes.toByteArray();
@@ -102,7 +98,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
// System.err.println("encoded: " + Arrays.toString(encoded));
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
- assertTrue(Arrays.equals(data, decoded));
+ assertArrayEquals(data, decoded);
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
outBytes = new ByteArrayOutputStream();
@@ -116,7 +112,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
}
decoded = outBytes.toByteArray();
- assertTrue(Arrays.equals(data, decoded));
+ assertArrayEquals(data, decoded);
}
@Test
@@ -129,10 +125,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
- catch (Exception e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
}
for (int i = 100; i < 2000; i += 250) {
@@ -143,10 +135,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
- catch (Exception e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
}
for (int i = 2000; i < 80000; i += 1000) {
@@ -157,14 +145,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
- catch (Exception e) {
- e.printStackTrace();
- fail(e.getMessage() + ": " + i);
- }
}
}
- // TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
-
-
+ // TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
}
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/RLEDecoder.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/RLEDecoder.java
index 6a1ee98e..dbfbda11 100755
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/RLEDecoder.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/RLEDecoder.java
@@ -63,7 +63,8 @@ final class RLEDecoder implements Decoder {
buffer.put((byte) data);
}
- } else {
+ }
+ else {
for (int b = 0; b < pixel.length; b++) {
int data = stream.read();
if (data < 0) {
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/RLEEncoder.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/RLEEncoder.java
new file mode 100755
index 00000000..6cc33585
--- /dev/null
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/RLEEncoder.java
@@ -0,0 +1,117 @@
+/*
+ * 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.tga;
+
+import com.twelvemonkeys.io.enc.Encoder;
+import com.twelvemonkeys.lang.Validate;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+final class RLEEncoder implements Encoder {
+
+ private final int pixelSize;
+
+ RLEEncoder(final int pixelDepth) {
+ Validate.isTrue(pixelDepth % Byte.SIZE == 0, "Depth must be a multiple of bytes (8 bits)");
+ pixelSize = pixelDepth / Byte.SIZE;
+ }
+
+ public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
+ encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
+ buffer.position(buffer.remaining());
+ }
+
+ private void encode(final OutputStream stream, final byte[] buffer, final int pOffset, final int length) throws IOException {
+ // NOTE: It's best to encode a 2 byte repeat
+ // run as a replicate run except when preceded and followed by a
+ // literal run, in which case it's best to merge the three into one
+ // literal run. Always encode 3 byte repeats as replicate runs.
+ // Worst case: output = input + (input + 127) / 128
+
+ int offset = pOffset;
+ final int max = pOffset + length - pixelSize;
+ final int maxMinus1 = max - pixelSize;
+
+ while (offset <= max) {
+ // Compressed run
+ int run = 1;
+ while (run < 127 && offset < max && equalPixel(buffer, offset, offset + pixelSize)) {
+ offset += pixelSize;
+ run++;
+ }
+
+ if (run > 1) {
+ stream.write(0x80 | (run - 1));
+ stream.write(buffer, offset, pixelSize);
+ offset += pixelSize;
+ }
+
+ // Literal run
+ int runStart = offset;
+ run = 0;
+ while ((run < 127 && ((offset < max && !(equalPixel(buffer, offset, offset + pixelSize)))
+ || (offset < maxMinus1 && !(equalPixel(buffer, offset, offset + 2 * pixelSize)))))) {
+ offset += pixelSize;
+ run++;
+ }
+
+ // If last pixel, include it in literal run, if space
+ if (offset == max && run > 0 && run < 127) {
+ offset += pixelSize;
+ run++;
+ }
+
+ if (run > 0) {
+ stream.write(run - 1);
+ stream.write(buffer, runStart, run * pixelSize);
+ }
+
+ // If last pixel, and not space, start new literal run
+ if (offset == max && (run <= 0 || run >= 127)) {
+ stream.write(0);
+ stream.write(buffer, offset, pixelSize);
+ offset += pixelSize;
+ }
+ }
+ }
+
+ private boolean equalPixel(final byte[] buffer, final int offset, int compareOffset) {
+ for (int i = 0; i < pixelSize; i++) {
+ if (buffer[offset + i] != buffer[compareOffset + i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAExtensions.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAExtensions.java
index 39ee721c..0b3048c9 100644
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAExtensions.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAExtensions.java
@@ -47,26 +47,26 @@ import static com.twelvemonkeys.imageio.plugins.tga.TGA.EXT_AREA_SIZE;
*/
final class TGAExtensions {
- private String authorName;
- private String authorComments;
+ String authorName;
+ String authorComments;
- private Calendar creationDate;
- private String jobId;
+ Calendar creationDate;
+ String jobId;
- private String softwareId;
- private String softwareVersion;
+ String softwareId;
+ String softwareVersion;
- private int backgroundColor;
- private double pixelAspectRatio;
- private double gamma;
+ int backgroundColor;
+ double pixelAspectRatio;
+ double gamma;
- private long colorCorrectionOffset;
- private long postageStampOffset;
- private long scanLineOffset;
+ long colorCorrectionOffset;
+ long postageStampOffset;
+ long scanLineOffset;
- private int attributeType;
+ int attributeType;
- private TGAExtensions() {
+ TGAExtensions() {
}
static TGAExtensions read(final ImageInputStream stream) throws IOException {
@@ -142,6 +142,7 @@ final class TGAExtensions {
return null;
}
+ //noinspection MagicConstant
calendar.set(year, month - 1, date, hourOfDay, minute, second);
return calendar;
@@ -176,6 +177,7 @@ final class TGAExtensions {
}
}
+ @SuppressWarnings("SwitchStatementWithTooFewBranches")
public boolean isAlphaPremultiplied() {
switch (attributeType) {
case 4:
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java
index 8b58fb23..e40e6fc6 100755
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java
@@ -31,7 +31,6 @@
package com.twelvemonkeys.imageio.plugins.tga;
import javax.imageio.IIOException;
-import javax.imageio.ImageWriteParam;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
@@ -58,9 +57,9 @@ final class TGAHeader {
private int height;
private int pixelDepth;
private int attributeBits;
- private int origin;
+ int origin;
private int interleave;
- private String identification;
+ String identification;
private IndexColorModel colorMap;
int getImageType() {
@@ -119,7 +118,7 @@ final class TGAHeader {
'}';
}
- static TGAHeader from(final RenderedImage image, final ImageWriteParam param) {
+ static TGAHeader from(final RenderedImage image, final boolean compressed) {
notNull(image, "image");
ColorModel colorModel = image.getColorModel();
@@ -128,7 +127,7 @@ final class TGAHeader {
TGAHeader header = new TGAHeader();
header.colorMapType = colorMap != null ? 1 : 0;
- header.imageType = getImageType(colorModel, param);
+ header.imageType = getImageType(colorModel, compressed);
header.colorMapStart = 0;
header.colorMapSize = colorMap != null ? colorMap.getMapSize() : 0;
header.colorMapDepth = colorMap != null ? (colorMap.hasAlpha() ? 32 : 24) : 0;
@@ -149,7 +148,7 @@ final class TGAHeader {
return header;
}
- private static int getImageType(final ColorModel colorModel, final ImageWriteParam param) {
+ private static int getImageType(final ColorModel colorModel, final boolean compressed) {
int uncompressedType;
if (colorModel instanceof IndexColorModel) {
@@ -169,7 +168,7 @@ final class TGAHeader {
}
}
- return uncompressedType | (TGAImageWriteParam.isRLE(param) ? 8 : 0);
+ return uncompressedType | (compressed ? 8 : 0);
}
void write(final DataOutput stream) throws IOException {
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParam.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParam.java
index 8e4e528e..7c9bb0be 100644
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParam.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParam.java
@@ -30,7 +30,13 @@
package com.twelvemonkeys.imageio.plugins.tga;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
import javax.imageio.ImageWriteParam;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.metadata.IIOMetadataNode;
import java.util.Locale;
/**
@@ -42,14 +48,29 @@ public final class TGAImageWriteParam extends ImageWriteParam {
this(null);
}
- @SuppressWarnings("WeakerAccess")
public TGAImageWriteParam(final Locale locale) {
super(locale);
+ canWriteCompressed = true;
compressionTypes = new String[]{"None", "RLE"};
}
- static boolean isRLE(final ImageWriteParam param) {
- return param != null && param.getCompressionMode() == MODE_EXPLICIT && "RLE".equals(param.getCompressionType());
+ static boolean isRLE(final ImageWriteParam param, final IIOMetadata metadata) {
+ return (param == null || param.canWriteCompressed() && param.getCompressionMode() == MODE_COPY_FROM_METADATA) && "RLE".equals(compressionTypeFromMetadata(metadata))
+ || param != null && param.canWriteCompressed() && param.getCompressionMode() == MODE_EXPLICIT && "RLE".equals(param.getCompressionType());
+ }
+
+ private static String compressionTypeFromMetadata(final IIOMetadata metadata) {
+ if (metadata != null) {
+ IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
+ NodeList compressionTypeName = root.getElementsByTagName("CompressionTypeName");
+
+ if (compressionTypeName.getLength() > 0) {
+ Node value = compressionTypeName.item(0).getAttributes().getNamedItem("value");
+ return value != null ? value.getNodeValue() : null;
+ }
+ }
+
+ return null;
}
}
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java
index b5696876..9dbedc87 100644
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java
@@ -31,18 +31,26 @@
package com.twelvemonkeys.imageio.plugins.tga;
import com.twelvemonkeys.imageio.ImageWriterBase;
+import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+import com.twelvemonkeys.io.LittleEndianDataOutputStream;
+import com.twelvemonkeys.io.enc.EncoderStream;
+import com.twelvemonkeys.lang.Validate;
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.ColorSpace;
import java.awt.image.*;
+import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import static com.twelvemonkeys.imageio.plugins.tga.TGAImageWriteParam.isRLE;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
@@ -55,13 +63,23 @@ final class TGAImageWriter extends ImageWriterBase {
@Override
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
- TGAHeader header = TGAHeader.from(imageType.createBufferedImage(1, 1), param);
+ Validate.notNull(imageType, "imageType");
+
+ TGAHeader header = TGAHeader.from(imageType.createBufferedImage(1, 1), isRLE(param, null));
return new TGAMetadata(header, null);
}
@Override
public IIOMetadata convertImageMetadata(final IIOMetadata inData, final ImageTypeSpecifier imageType, final ImageWriteParam param) {
- return null;
+ Validate.notNull(inData, "inData");
+ Validate.notNull(imageType, "imageType");
+
+ if (inData instanceof TGAMetadata) {
+ return inData;
+ }
+
+ // TODO: Make metadata mutable, and do actual merge
+ return getDefaultImageMetadata(imageType, param);
}
@Override
@@ -73,16 +91,23 @@ final class TGAImageWriter extends ImageWriterBase {
}
}
+ @Override
+ public ImageWriteParam getDefaultWriteParam() {
+ return new TGAImageWriteParam(getLocale());
+ }
+
@Override
public void write(final IIOMetadata streamMetadata, final IIOImage image, final ImageWriteParam param) throws IOException {
assertOutput();
+ Validate.notNull(image, "image");
if (image.hasRaster()) {
throw new UnsupportedOperationException("Raster not supported");
}
+ final boolean compressed = isRLE(param, image.getMetadata());
RenderedImage renderedImage = image.getRenderedImage();
- TGAHeader header = TGAHeader.from(renderedImage, param);
+ TGAHeader header = TGAHeader.from(renderedImage, compressed);
header.write(imageOutput);
@@ -94,7 +119,7 @@ final class TGAImageWriter extends ImageWriterBase {
? ImageTypeSpecifiers.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, false, false).createBufferedImage(renderedImage.getWidth(), 1).getRaster()
: ImageTypeSpecifier.createFromRenderedImage(renderedImage).createBufferedImage(renderedImage.getWidth(), 1).getRaster();
- DataBuffer buffer = rowRaster.getDataBuffer();
+ final DataBuffer buffer = rowRaster.getDataBuffer();
for (int tileY = 0; tileY < renderedImage.getNumYTiles(); tileY++) {
for (int tileX = 0; tileX < renderedImage.getNumXTiles(); tileX++) {
@@ -110,6 +135,8 @@ final class TGAImageWriter extends ImageWriterBase {
break;
}
+ DataOutput imageOutput = compressed ? createRLEStream(header, this.imageOutput) : this.imageOutput;
+
switch (buffer.getDataType()) {
case DataBuffer.TYPE_BYTE:
rowRaster.setDataElements(0, 0, raster.createChild(0, y, raster.getWidth(), 1, 0, 0, null));
@@ -118,22 +145,37 @@ final class TGAImageWriter extends ImageWriterBase {
case DataBuffer.TYPE_USHORT:
rowRaster.setDataElements(0, 0, raster.createChild(0, y, raster.getWidth(), 1, 0, 0, null));
short[] shorts = ((DataBufferUShort) buffer).getData();
- imageOutput.writeShorts(shorts, 0, shorts.length);
+
+ // TODO: Get rid of this, due to stupid design in EncoderStream...
+ ByteBuffer bb = ByteBuffer.allocate(shorts.length * 2);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ bb.asShortBuffer().put(shorts);
+ imageOutput.write(bb.array());
+ // TODO: The below should work just as good
+// for (short value : shorts) {
+// imageOutput.writeShort(value);
+// }
break;
default:
- throw new IIOException("Unsupported data");
+ throw new IIOException("Unsupported data type");
}
- processImageProgress(tileY * 100f / renderedImage.getNumYTiles());
+ if (compressed) {
+ ((LittleEndianDataOutputStream) imageOutput).close();
+ }
}
+ processImageProgress(tileY * 100f / renderedImage.getNumYTiles());
}
}
// TODO: If we have thumbnails, we need to write extension too.
processImageComplete();
+ }
+ 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
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java
index 241a800a..f3755c9c 100755
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java
@@ -78,7 +78,12 @@ final class TGAMetadata extends AbstractMetadata {
chroma.appendChild(numChannels);
switch (header.getPixelDepth()) {
case 8:
- numChannels.setAttribute("value", Integer.toString(1));
+ 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()) {
@@ -146,7 +151,7 @@ final class TGAMetadata extends AbstractMetadata {
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
node.appendChild(compressionTypeName);
String value = header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN || header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE
- ? "Uknown" : "RLE";
+ ? "Unknown" : "RLE";
compressionTypeName.setAttribute("value", value);
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
@@ -155,7 +160,7 @@ final class TGAMetadata extends AbstractMetadata {
return node;
default:
- // No compreesion
+ // No compression
return null;
}
}
@@ -199,10 +204,10 @@ final class TGAMetadata extends AbstractMetadata {
}
break;
case 24:
- bitsPerSample.setAttribute("value", createListValue(3, Integer.toString(8)));
+ bitsPerSample.setAttribute("value", createListValue(3, "8"));
break;
case 32:
- bitsPerSample.setAttribute("value", createListValue(4, Integer.toString(8)));
+ bitsPerSample.setAttribute("value", createListValue(4, "8"));
break;
}
diff --git a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/RLEDecoderTest.java b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/RLEDecoderTest.java
new file mode 100644
index 00000000..8ce2e9b1
--- /dev/null
+++ b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/RLEDecoderTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.tga;
+
+import com.twelvemonkeys.io.enc.Decoder;
+import com.twelvemonkeys.io.enc.DecoderAbstractTest;
+import com.twelvemonkeys.io.enc.Encoder;
+
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * RLEDecoderTest.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: RLEDecoderTest.java,v 1.0 07/04/2021 haraldk Exp$
+ */
+public class RLEDecoderTest extends DecoderAbstractTest {
+ public Decoder createDecoder() {
+ return new RLEDecoder(8);
+ }
+
+ public Encoder createCompatibleEncoder() {
+ return new RLEEncoder(8);
+ }
+
+ @Test
+ public void testRLE8() throws IOException {
+ RLEDecoder decoder = new RLEDecoder(8);
+
+ ByteBuffer buffer = ByteBuffer.allocate(256);
+ // Literal run, 2 bytes, compressed run, 8 bytes
+ ByteArrayInputStream stream = new ByteArrayInputStream(new byte[] {1, (byte) 0xFF, (byte) 0xF1, (byte) 0x87, (byte) 0xFE});
+
+ int decoded = decoder.decode(stream, buffer);
+
+ assertEquals(10, decoded);
+ assertArrayEquals(new byte[] {(byte) 0xFF, (byte) 0xF1, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE}, Arrays.copyOf(buffer.array(), 10));
+ }
+
+ @Test
+ public void testRLE16() throws IOException {
+ RLEDecoder decoder = new RLEDecoder(16);
+
+ ByteBuffer buffer = ByteBuffer.allocate(512);
+ // Literal run, 2 * 2 bytes, compressed run, 8 * 2 bytes
+ ByteArrayInputStream stream = new ByteArrayInputStream(new byte[] {1, (byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0x87, (byte) 0x00, (byte) 0xFE});
+
+ int decoded = decoder.decode(stream, buffer);
+
+ assertEquals(20, decoded);
+ assertArrayEquals(new byte[] {
+ (byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0xF1,
+ (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE
+ },
+ Arrays.copyOf(buffer.array(), 20));
+ }
+
+ @Test
+ public void testRLE24() throws IOException {
+ RLEDecoder decoder = new RLEDecoder(24);
+
+ ByteBuffer buffer = ByteBuffer.allocate(1024);
+ // Literal run, 2 * 3 bytes, compressed run, 8 * 3 bytes
+ ByteArrayInputStream stream = new ByteArrayInputStream(new byte[] {1, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1, (byte) 0x87, (byte) 0x00, (byte) 0xFE, (byte) 0xFE});
+
+ int decoded = decoder.decode(stream, buffer);
+
+ assertEquals(30, decoded);
+ assertArrayEquals(new byte[] {
+ (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE
+ },
+ Arrays.copyOf(buffer.array(), 30));
+ }
+
+ @Test
+ public void testRLE32() throws IOException {
+ RLEDecoder decoder = new RLEDecoder(32);
+
+ ByteBuffer buffer = ByteBuffer.allocate(1024);
+ // Literal run, 2 * 4 bytes, compressed run, 8 * 4 bytes
+ ByteArrayInputStream stream = new ByteArrayInputStream(new byte[] {1, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1, (byte) 0xF1, (byte) 0x87, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE});
+
+ int decoded = decoder.decode(stream, buffer);
+
+ assertEquals(40, decoded);
+ assertArrayEquals(new byte[] {
+ (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1, (byte) 0xF1,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE
+ },
+ Arrays.copyOf(buffer.array(), 40));
+ }
+
+}
\ No newline at end of file
diff --git a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/RLEEncoderTest.java b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/RLEEncoderTest.java
new file mode 100644
index 00000000..be8a4d8c
--- /dev/null
+++ b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/RLEEncoderTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.tga;
+
+import com.twelvemonkeys.io.enc.Decoder;
+import com.twelvemonkeys.io.enc.Encoder;
+import com.twelvemonkeys.io.enc.EncoderAbstractTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertArrayEquals;
+
+/**
+ * RLEEncoderTest.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: RLEEncoderTest.java,v 1.0 07/04/2021 haraldk Exp$
+ */
+
+public class RLEEncoderTest extends EncoderAbstractTest {
+ @Override
+ protected Encoder createEncoder() {
+ return new RLEEncoder(8);
+ }
+
+ @Override
+ protected Decoder createCompatibleDecoder() {
+ return new RLEDecoder(8);
+ }
+
+ @Test
+ public void testRLE8() throws IOException {
+ RLEEncoder encoder = new RLEEncoder(8);
+
+ // Literal run, 2 bytes, compressed run, 8 bytes
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[] {(byte) 0xFF, (byte) 0xF1, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE});
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(10);
+
+ encoder.encode(stream, buffer);
+
+ assertArrayEquals(new byte[] {1, (byte) 0xFF, (byte) 0xF1, (byte) 0x87, (byte) 0xFE}, stream.toByteArray());
+ }
+
+ @Test
+ public void testRLE16() throws IOException {
+ RLEEncoder encoder = new RLEEncoder(16);
+
+ // Literal run, 2 * 2 bytes, compressed run, 8 * 2 bytes
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[] {(byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0xF1,
+ (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0x00, (byte) 0xFE});
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(20);
+
+ encoder.encode(stream, buffer);
+
+ assertArrayEquals(new byte[] {1, (byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0x87, (byte) 0x00, (byte) 0xFE}, stream.toByteArray());
+ }
+
+ @Test
+ public void testRLE24() throws IOException {
+ RLEEncoder encoder = new RLEEncoder(24);
+
+ // Literal run, 2 * 3 bytes, compressed run, 8 * 3 bytes
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[] {
+ (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE
+ });
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(30);
+
+ encoder.encode(stream, buffer);
+
+ assertArrayEquals(new byte[] {
+ 1, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1,
+ (byte) 0x87, (byte) 0x00, (byte) 0xFE, (byte) 0xFE
+ }, stream.toByteArray());
+ }
+
+ @Test
+ public void testRLE32() throws IOException {
+ RLEEncoder encoder = new RLEEncoder(32);
+
+ // Literal run, 2 * 4 bytes, compressed run, 8 * 4 bytes
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[] {
+ (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1, (byte) 0xF1,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE,
+ (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE
+ });
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(40);
+
+ encoder.encode(stream, buffer);
+
+ assertArrayEquals(new byte[] {
+ 1, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0xF1, (byte) 0xF1, (byte) 0xF1,
+ (byte) 0x87, (byte) 0x00, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE
+ }, stream.toByteArray());
+ }
+}
\ No newline at end of file
diff --git a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParamTest.java b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParamTest.java
new file mode 100644
index 00000000..d8ff3059
--- /dev/null
+++ b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParamTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.tga;
+
+import org.junit.Test;
+
+import javax.imageio.ImageWriteParam;
+import java.awt.image.BufferedImage;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.assumeFalse;
+
+/**
+ * TGAImageWriteParamTest.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: TGAImageWriteParamTest.java,v 1.0 08/04/2021 haraldk Exp$
+ */
+public class TGAImageWriteParamTest {
+ @Test
+ public void testDefaultCopyFromMetadata() {
+ TGAImageWriteParam param = new TGAImageWriteParam();
+ assertTrue(param.canWriteCompressed());
+ assertEquals(ImageWriteParam.MODE_COPY_FROM_METADATA, param.getCompressionMode());
+ }
+
+ @Test
+ public void testIsRLENoParamNoMetadata() {
+ assertFalse(TGAImageWriteParam.isRLE(null, null));
+ }
+
+ @Test
+ public void testIsRLEParamCantWriteCompressedNoMetadata() {
+ // Base class has canWriteCompressed == false, need to test
+ ImageWriteParam param = new ImageWriteParam(null);
+ assumeFalse(param.canWriteCompressed());
+
+ assertFalse(TGAImageWriteParam.isRLE(param, null));
+ }
+
+ @Test
+ public void testIsRLEParamDefaultNoMetadata() {
+ TGAImageWriteParam param = new TGAImageWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_DEFAULT);
+ assertFalse(TGAImageWriteParam.isRLE(param, null));
+ }
+
+ @Test
+ public void testIsRLEParamExplicitNoMetadata() {
+ TGAImageWriteParam param = new TGAImageWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+
+ assertFalse(TGAImageWriteParam.isRLE(param, null));
+
+ param.setCompressionType("RLE");
+ assertTrue(TGAImageWriteParam.isRLE(param, null));
+ }
+
+ @Test
+ public void testIsRLEParamDisabledNoMetadata() {
+ TGAImageWriteParam param = new TGAImageWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_DISABLED);
+
+ assertFalse(TGAImageWriteParam.isRLE(param, null));
+ }
+
+ @Test
+ public void testIsRLEParamCopyNoMetadata() {
+ ImageWriteParam param = new TGAImageWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
+
+ assertFalse(TGAImageWriteParam.isRLE(param, null));
+ }
+
+ @Test
+ public void testIsRLEParamCantWriteCompressedAndMetadata() {
+ // Base class has canWriteCompressed == false, need to test
+ ImageWriteParam param = new ImageWriteParam(null);
+ assumeFalse(param.canWriteCompressed());
+
+ 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
+ public void testIsRLEParamCopyAndMetadataNoCompression() {
+ ImageWriteParam param = new TGAImageWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
+
+ assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
+ }
+
+ @Test
+ public void testIsRLEParamCopyAndMetadataRLE() {
+ ImageWriteParam param = new TGAImageWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
+
+ assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
+ }
+
+ @Test
+ public void testIsRLEParamExplicitAndMetadata() {
+ TGAImageWriteParam param = new TGAImageWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+
+ 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(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)));
+ }
+
+}
\ No newline at end of file
diff --git a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriterTest.java b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriterTest.java
index 8d5e5b45..8d7e4f37 100644
--- a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriterTest.java
+++ b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriterTest.java
@@ -30,15 +30,18 @@
package com.twelvemonkeys.imageio.plugins.tga;
+import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
+import com.twelvemonkeys.io.FastByteArrayOutputStream;
import org.junit.Test;
+import org.w3c.dom.NodeList;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReader;
-import javax.imageio.ImageTypeSpecifier;
-import javax.imageio.ImageWriter;
+import javax.imageio.*;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
@@ -53,7 +56,7 @@ import java.util.Arrays;
import java.util.List;
import static com.twelvemonkeys.imageio.util.ImageReaderAbstractTest.assertImageDataEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.*;
import static org.junit.Assume.assumeNotNull;
/**
@@ -84,6 +87,13 @@ public class TGAImageWriterTest extends ImageWriterAbstractTest
);
}
+ @Test
+ public void testDefaultParamIsTGA() throws IOException {
+ ImageWriter writer = createWriter();
+ assertEquals(writer.getDefaultWriteParam().getClass(), TGAImageWriteParam.class);
+ writer.dispose();
+ }
+
@Test
public void testWriteRead() throws IOException {
ImageWriter writer = createWriter();
@@ -108,5 +118,120 @@ public class TGAImageWriterTest extends ImageWriterAbstractTest
assertImageDataEquals("Images differ for " + testData, (BufferedImage) testData, image);
}
}
+
+ writer.dispose();
+ reader.dispose();
+ }
+
+ @Test
+ public void testWriteReadRLE() throws IOException {
+ ImageWriter writer = createWriter();
+ ImageReader reader = ImageIO.getImageReader(writer);
+
+ assumeNotNull(reader);
+
+ ImageWriteParam param = writer.getDefaultWriteParam();
+ param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+ param.setCompressionType("RLE");
+
+ for (RenderedImage testData : getTestData()) {
+ FastByteArrayOutputStream buffer = new FastByteArrayOutputStream(4096);
+
+ try (ImageOutputStream stream = ImageIO.createImageOutputStream(buffer)) {
+ writer.setOutput(stream);
+ writer.write(null, new IIOImage(drawSomething((BufferedImage) testData), null, null), param);
+ }
+
+ try (ImageInputStream stream = new ByteArrayImageInputStream(buffer.toByteArray())) {
+ reader.setInput(stream);
+
+ BufferedImage image = reader.read(0);
+
+ assertNotNull(image);
+ assertImageDataEquals("Images differ for " + testData, (BufferedImage) testData, image);
+ }
+ }
+
+ writer.dispose();
+ reader.dispose();
+ }
+
+ @Test
+ public void testRewriteCompressionCopyFromMetadataUncompressed() throws IOException {
+ ImageWriter writer = createWriter();
+ ImageReader reader = ImageIO.getImageReader(writer);
+
+ assumeNotNull(reader);
+
+ try (ImageInputStream input = ImageIO.createImageInputStream(getClassLoaderResource("/tga/UTC24.TGA"))) {
+ reader.setInput(input);
+ IIOImage image = reader.readAll(0, null);
+ assertNull(findCompressionType(image.getMetadata())); // Sanity
+
+ FastByteArrayOutputStream buffer = new FastByteArrayOutputStream(65536);
+
+ try (ImageOutputStream output = ImageIO.createImageOutputStream(buffer)) {
+ writer.setOutput(output);
+
+ // Copy from metadata should be default, we'll validate here
+ ImageWriteParam param = writer.getDefaultWriteParam();
+ assertEquals(ImageWriteParam.MODE_COPY_FROM_METADATA, param.getCompressionMode());
+
+ writer.write(null, image, param);
+ }
+
+ try (ImageInputStream stream = new ByteArrayImageInputStream(buffer.toByteArray())) {
+ reader.setInput(stream);
+ IIOMetadata metadata = reader.getImageMetadata(0);
+
+ assertNull(findCompressionType(metadata));
+ }
+ }
+
+ writer.dispose();
+ reader.dispose();
+ }
+
+ @Test
+ public void testRewriteCompressionCopyFromMetadataRLE() throws IOException {
+ ImageWriter writer = createWriter();
+ ImageReader reader = ImageIO.getImageReader(writer);
+
+ assumeNotNull(reader);
+
+ try (ImageInputStream input = ImageIO.createImageInputStream(getClassLoaderResource("/tga/CTC24.TGA"))) {
+ reader.setInput(input);
+ IIOImage image = reader.readAll(0, null);
+ assertEquals("RLE", findCompressionType(image.getMetadata())); // Sanity
+
+ FastByteArrayOutputStream buffer = new FastByteArrayOutputStream(32768);
+
+ try (ImageOutputStream output = ImageIO.createImageOutputStream(buffer)) {
+ writer.setOutput(output);
+
+ // Copy from metadata should be default, we'll just go with no param here
+ writer.write(null, image, null);
+ }
+
+ try (ImageInputStream inputStream = new ByteArrayImageInputStream(buffer.toByteArray())) {
+ reader.setInput(inputStream);
+ IIOMetadata metadata = reader.getImageMetadata(0);
+
+ assertEquals("RLE", findCompressionType(metadata));
+ }
+ }
+
+ writer.dispose();
+ reader.dispose();
+ }
+
+ private String findCompressionType(IIOMetadata metadata) {
+ IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
+ NodeList compressionTypeName = root.getElementsByTagName("CompressionTypeName");
+ if (compressionTypeName.getLength() > 0) {
+ return compressionTypeName.item(0).getAttributes().getNamedItem("value").getNodeValue();
+ }
+
+ return null;
}
}
\ No newline at end of file
diff --git a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadataTest.java b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadataTest.java
new file mode 100644
index 00000000..c1bd18e1
--- /dev/null
+++ b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadataTest.java
@@ -0,0 +1,486 @@
+/*
+ * 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.tga;
+
+import org.junit.Test;
+import org.junit.function.ThrowingRunnable;
+import org.w3c.dom.Node;
+
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.metadata.IIOMetadataNode;
+import java.awt.image.BufferedImage;
+import java.awt.image.IndexColorModel;
+import java.util.Calendar;
+
+import static org.junit.Assert.*;
+
+/**
+ * TGAMetadataTest.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: TGAMetadataTest.java,v 1.0 08/04/2021 haraldk Exp$
+ */
+public class TGAMetadataTest {
+ @Test
+ public void testStandardFeatures() {
+ 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());
+ Node root = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
+ assertNotNull(root);
+ assertTrue(root instanceof IIOMetadataNode);
+
+ // Other formats
+ assertNull(metadata.getNativeMetadataFormatName());
+ assertNull(metadata.getExtraMetadataFormatNames());
+ assertThrows(IllegalArgumentException.class, new ThrowingRunnable() {
+ @Override
+ public void run() {
+ metadata.getAsTree("com_foo_bar_1.0");
+ }
+ });
+
+ // Read-only
+ assertTrue(metadata.isReadOnly());
+ assertThrows(IllegalStateException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ metadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName));
+ }
+ });
+ }
+
+ @Test
+ public void testStandardChromaGray() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), false);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ assertNotNull(chroma);
+ assertEquals("Chroma", chroma.getNodeName());
+ assertEquals(3, chroma.getLength());
+
+ IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
+ assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
+ assertEquals("GRAY", colorSpaceType.getAttribute("name"));
+
+ IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
+ assertEquals("NumChannels", numChannels.getNodeName());
+ assertEquals("1", numChannels.getAttribute("value"));
+
+ IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
+ assertEquals("BlackIsZero", blackIsZero.getNodeName());
+ assertEquals("TRUE", blackIsZero.getAttribute("value"));
+
+ assertNull(blackIsZero.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardChromaRGB() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ assertNotNull(chroma);
+ assertEquals("Chroma", chroma.getNodeName());
+ assertEquals(3, chroma.getLength());
+
+ IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
+ assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
+ assertEquals("RGB", colorSpaceType.getAttribute("name"));
+
+ IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
+ assertEquals("NumChannels", numChannels.getNodeName());
+ assertEquals("3", numChannels.getAttribute("value"));
+
+ IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
+ assertEquals("BlackIsZero", blackIsZero.getNodeName());
+ assertEquals("TRUE", blackIsZero.getAttribute("value"));
+
+ assertNull(blackIsZero.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardChromaPalette() {
+ byte[] bw = {0, (byte) 0xff};
+ IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, -1);
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), false);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ assertNotNull(chroma);
+ assertEquals("Chroma", chroma.getNodeName());
+ assertEquals(4, chroma.getLength());
+
+ IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
+ assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
+ assertEquals("RGB", colorSpaceType.getAttribute("name"));
+
+ IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
+ assertEquals("NumChannels", numChannels.getNodeName());
+ assertEquals("3", numChannels.getAttribute("value"));
+
+ IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
+ assertEquals("BlackIsZero", blackIsZero.getNodeName());
+ assertEquals("TRUE", blackIsZero.getAttribute("value"));
+
+ IIOMetadataNode palette = (IIOMetadataNode) blackIsZero.getNextSibling();
+ assertEquals("Palette", palette.getNodeName());
+ assertEquals(bw.length, palette.getLength());
+
+ for (int i = 0; i < palette.getLength(); i++) {
+ IIOMetadataNode item0 = (IIOMetadataNode) palette.item(i);
+ assertEquals("PaletteEntry", item0.getNodeName());
+ assertEquals(String.valueOf(i), item0.getAttribute("index"));
+ String rgb = String.valueOf(bw[i] & 0xff);
+ assertEquals(rgb, item0.getAttribute("red"));
+ assertEquals(rgb, item0.getAttribute("green"));
+ assertEquals(rgb, item0.getAttribute("blue"));
+ }
+
+ // TODO: BackgroundIndex == 1??
+ }
+
+ @Test
+ public void testStandardCompressionRLE() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode compression = metadata.getStandardCompressionNode();
+ assertNotNull(compression);
+ assertEquals("Compression", compression.getNodeName());
+ assertEquals(2, compression.getLength());
+
+ IIOMetadataNode compressionTypeName = (IIOMetadataNode) compression.getFirstChild();
+ assertEquals("CompressionTypeName", compressionTypeName.getNodeName());
+ assertEquals("RLE", compressionTypeName.getAttribute("value"));
+
+ IIOMetadataNode lossless = (IIOMetadataNode) compressionTypeName.getNextSibling();
+ assertEquals("Lossless", lossless.getNodeName());
+ assertEquals("TRUE", lossless.getAttribute("value"));
+
+ assertNull(lossless.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardCompressionNone() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
+ }
+
+ @Test
+ public void testStandardDataGray() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode data = metadata.getStandardDataNode();
+ assertNotNull(data);
+ assertEquals("Data", data.getNodeName());
+ assertEquals(3, data.getLength());
+
+ IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
+ assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
+ assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
+
+ IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
+ assertEquals("SampleFormat", sampleFomat.getNodeName());
+ assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
+
+ IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
+ assertEquals("BitsPerSample", bitsPerSample.getNodeName());
+ assertEquals("8", bitsPerSample.getAttribute("value"));
+
+ assertNull(bitsPerSample.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardDataRGB() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode data = metadata.getStandardDataNode();
+ assertNotNull(data);
+ assertEquals("Data", data.getNodeName());
+ assertEquals(3, data.getLength());
+
+ IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
+ assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
+ assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
+
+ IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
+ assertEquals("SampleFormat", sampleFomat.getNodeName());
+ assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
+
+ IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
+ assertEquals("BitsPerSample", bitsPerSample.getNodeName());
+ assertEquals("8 8 8", bitsPerSample.getAttribute("value"));
+
+ assertNull(bitsPerSample.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardDataRGBA() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode data = metadata.getStandardDataNode();
+ assertNotNull(data);
+ assertEquals("Data", data.getNodeName());
+ assertEquals(3, data.getLength());
+
+ IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
+ assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
+ assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
+
+ IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
+ assertEquals("SampleFormat", sampleFomat.getNodeName());
+ assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
+
+ IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
+ assertEquals("BitsPerSample", bitsPerSample.getNodeName());
+ assertEquals("8 8 8 8", bitsPerSample.getAttribute("value"));
+
+ assertNull(bitsPerSample.getNextSibling()); // No more children
+ }
+
+ @Test
+ 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);
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode data = metadata.getStandardDataNode();
+ assertNotNull(data);
+ assertEquals("Data", data.getNodeName());
+ assertEquals(3, data.getLength());
+
+ IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
+ assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
+ assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
+
+ IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
+ assertEquals("SampleFormat", sampleFomat.getNodeName());
+ assertEquals("Index", sampleFomat.getAttribute("value"));
+
+ IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
+ assertEquals("BitsPerSample", bitsPerSample.getNodeName());
+ assertEquals("8", bitsPerSample.getAttribute("value"));
+
+ assertNull(bitsPerSample.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardDimensionNormal() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ assertNotNull(dimension);
+ assertEquals("Dimension", dimension.getNodeName());
+ assertEquals(2, dimension.getLength());
+
+ 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(pixelAspectRatio.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardDimensionFlipH() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ header.origin = TGA.ORIGIN_LOWER_LEFT;
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ assertNotNull(dimension);
+ assertEquals("Dimension", dimension.getNodeName());
+ assertEquals(2, dimension.getLength());
+
+ 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(pixelAspectRatio.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardDocument() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ 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"));
+
+ assertNull(formatVersion.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardDocumentExtensions() {
+ 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(header, extensions);
+
+ IIOMetadataNode document = metadata.getStandardDocumentNode();
+ assertNotNull(document);
+ assertEquals("Document", document.getNodeName());
+ assertEquals(2, document.getLength());
+
+ IIOMetadataNode formatVersion = (IIOMetadataNode) document.getFirstChild();
+ assertEquals("FormatVersion", formatVersion.getNodeName());
+ assertEquals("2.0", formatVersion.getAttribute("value"));
+
+ IIOMetadataNode imageCreationTime = (IIOMetadataNode) formatVersion.getNextSibling();
+ assertEquals("ImageCreationTime", imageCreationTime.getNodeName());
+ assertEquals("2021", imageCreationTime.getAttribute("year"));
+ assertEquals("4", imageCreationTime.getAttribute("month"));
+ assertEquals("8", imageCreationTime.getAttribute("day"));
+ assertEquals("18", imageCreationTime.getAttribute("hour"));
+ assertEquals("55", imageCreationTime.getAttribute("minute"));
+ assertEquals("0", imageCreationTime.getAttribute("second"));
+
+ assertNull(imageCreationTime.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardText() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ header.identification = "MY_FILE.TGA";
+
+ TGAExtensions extensions = new TGAExtensions();
+ extensions.softwareId = "TwelveMonkeys";
+ extensions.authorName = "Harald K";
+ extensions.authorComments = "Comments, comments... ";
+
+ TGAMetadata metadata = new TGAMetadata(header, extensions);
+
+ IIOMetadataNode text = metadata.getStandardTextNode();
+ assertNotNull(text);
+ assertEquals("Text", text.getNodeName());
+ assertEquals(4, text.getLength());
+
+ IIOMetadataNode textEntry = (IIOMetadataNode) text.item(0);
+ assertEquals("TextEntry", textEntry.getNodeName());
+ assertEquals("DocumentName", textEntry.getAttribute("keyword"));
+ assertEquals(header.getIdentification(), textEntry.getAttribute("value"));
+
+ textEntry = (IIOMetadataNode) text.item(1);
+ assertEquals("TextEntry", textEntry.getNodeName());
+ assertEquals("Software", textEntry.getAttribute("keyword"));
+ assertEquals(extensions.getSoftware(), textEntry.getAttribute("value"));
+
+ textEntry = (IIOMetadataNode) text.item(2);
+ assertEquals("TextEntry", textEntry.getNodeName());
+ assertEquals("Artist", textEntry.getAttribute("keyword"));
+ assertEquals(extensions.getAuthorName(), textEntry.getAttribute("value"));
+
+ textEntry = (IIOMetadataNode) text.item(3);
+ assertEquals("TextEntry", textEntry.getNodeName());
+ assertEquals("UserComment", textEntry.getAttribute("keyword"));
+ assertEquals(extensions.getAuthorComments(), textEntry.getAttribute("value"));
+ }
+
+ @Test
+ public void testStandardTransparencyRGB() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ assertNotNull(transparency);
+ 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
+ }
+
+ @Test
+ public void testStandardTransparencyRGBA() {
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ assertNotNull(transparency);
+ assertEquals("Transparency", transparency.getNodeName());
+ assertEquals(1, transparency.getLength());
+
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("nonpremultiplied", alpha.getAttribute("value"));
+
+ assertNull(alpha.getNextSibling()); // No more children
+ }
+
+ @Test
+ public void testStandardTransparencyPalette() {
+ byte[] bw = {0, (byte) 0xff};
+ IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, 1);
+ TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), true);
+ TGAMetadata metadata = new TGAMetadata(header, null);
+
+ IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ assertNotNull(transparency);
+ assertEquals("Transparency", transparency.getNodeName());
+ assertEquals(1, transparency.getLength());
+
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("nonpremultiplied", alpha.getAttribute("value"));
+
+ assertNull(alpha.getNextSibling()); // No more children
+ }
+
+}
\ No newline at end of file