This commit is contained in:
Harald Kuhr 2018-09-08 16:04:06 +02:00
parent 5a3945c411
commit 586359e7ab
11 changed files with 614 additions and 29 deletions

View File

@ -36,6 +36,8 @@ interface TGA {
/** Fixed header size: 18.*/
int HEADER_SIZE = 18;
int EXT_AREA_SIZE = 495;
/** No color map included. */
int COLORMAP_NONE = 0;
/** Color map included. */

View File

@ -36,6 +36,8 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import static com.twelvemonkeys.imageio.plugins.tga.TGA.EXT_AREA_SIZE;
/**
* TGAExtensions.
*
@ -44,7 +46,6 @@ import java.util.Calendar;
* @version $Id: TGAExtensions.java,v 1.0 27/07/15 harald.kuhr Exp$
*/
final class TGAExtensions {
public static final int EXT_AREA_SIZE = 495;
private String authorName;
private String authorComments;
@ -77,7 +78,7 @@ final class TGAExtensions {
}
TGAExtensions extensions = new TGAExtensions();
extensions.authorName = readString(stream, 41);;
extensions.authorName = readString(stream, 41);
extensions.authorComments = readString(stream, 324);
extensions.creationDate = readDate(stream);
extensions.jobId = readString(stream, 41);

View File

@ -31,11 +31,19 @@
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;
import java.awt.image.RenderedImage;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import static com.twelvemonkeys.lang.Validate.notNull;
import static java.awt.color.ColorSpace.TYPE_GRAY;
import static java.awt.color.ColorSpace.TYPE_RGB;
final class TGAHeader {
@ -55,43 +63,44 @@ final class TGAHeader {
private String identification;
private IndexColorModel colorMap;
public int getImageType() {
int getImageType() {
return imageType;
}
public int getWidth() {
int getWidth() {
return width;
}
public int getHeight() {
int getHeight() {
return height;
}
public int getPixelDepth() {
int getPixelDepth() {
return pixelDepth;
}
public int getAttributeBits() {
int getAttributeBits() {
return attributeBits;
}
public int getOrigin() {
int getOrigin() {
return origin;
}
public int getInterleave() {
int getInterleave() {
return interleave;
}
public String getIdentification() {
String getIdentification() {
return identification;
}
public IndexColorModel getColorMap() {
IndexColorModel getColorMap() {
return colorMap;
}
@Override public String toString() {
@Override
public String toString() {
return "TGAHeader{" +
"colorMapType=" + colorMapType +
", imageType=" + imageType +
@ -110,7 +119,101 @@ final class TGAHeader {
'}';
}
public static TGAHeader read(final ImageInputStream imageInput) throws IOException {
static TGAHeader from(final RenderedImage image, final ImageWriteParam param) {
notNull(image, "image");
ColorModel colorModel = image.getColorModel();
IndexColorModel colorMap = colorModel instanceof IndexColorModel ? (IndexColorModel) colorModel : null;
TGAHeader header = new TGAHeader();
header.colorMapType = colorMap != null ? 1 : 0;
header.imageType = getImageType(colorModel, param);
header.colorMapStart = 0;
header.colorMapSize = colorMap != null ? colorMap.getMapSize() : 0;
header.colorMapDepth = colorMap != null ? (colorMap.hasAlpha() ? 32 : 24) : 0;
header.x = 0;
header.y = 0;
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?
header.attributeBits = colorModel.hasAlpha() ? 8 : 0; // TODO: FixMe
header.identification = null;
header.colorMap = colorMap;
return header;
}
private static int getImageType(final ColorModel colorModel, final ImageWriteParam param) {
int uncompressedType;
if (colorModel instanceof IndexColorModel) {
uncompressedType = TGA.IMAGETYPE_COLORMAPPED;
}
else {
switch (colorModel.getColorSpace().getType()) {
case TYPE_RGB:
uncompressedType = TGA.IMAGETYPE_TRUECOLOR;
break;
case TYPE_GRAY:
uncompressedType = TGA.IMAGETYPE_MONOCHROME;
break;
default:
throw new IllegalArgumentException("Unsupported color space for TGA: " + colorModel.getColorSpace());
}
}
return uncompressedType | (TGAImageWriteParam.isRLE(param) ? 8 : 0);
}
void write(final DataOutput stream) throws IOException {
byte[] idBytes = identification != null ? identification.getBytes(StandardCharsets.US_ASCII) : new byte[0];
stream.writeByte(idBytes.length);
stream.writeByte(colorMapType);
stream.writeByte(imageType);
stream.writeShort(colorMapStart);
stream.writeShort(colorMapSize);
stream.writeByte(colorMapDepth);
stream.writeShort(x);
stream.writeShort(y);
stream.writeShort(width);
stream.writeShort(height);
stream.writeByte(pixelDepth);
stream.writeByte(attributeBits | origin << 4 | interleave << 6);
// Identification
stream.write(idBytes);
// Color map
if (colorMap != null) {
int[] rgb = new int[colorMap.getMapSize()];
colorMap.getRGBs(rgb);
int components = colorMap.hasAlpha() ? 4 : 3;
byte[] cmap = new byte[rgb.length * components];
for (int i = 0; i < rgb.length; i++) {
cmap[i * components ] = (byte) ((rgb[i] >> 16) & 0xff);
cmap[i * components + 1] = (byte) ((rgb[i] >> 8) & 0xff);
cmap[i * components + 2] = (byte) ((rgb[i] ) & 0xff);
if (components == 4) {
cmap[i * components + 3] = (byte) ((rgb[i] >>> 24) & 0xff);
}
}
stream.write(cmap);
}
}
static TGAHeader read(final ImageInputStream imageInput) throws IOException {
// typedef struct _TgaHeader
// {
// BYTE IDLength; /* 00h Size of Image ID field */
@ -154,7 +257,7 @@ final class TGAHeader {
byte[] idBytes = new byte[imageIdLength];
imageInput.readFully(idBytes);
header.identification = new String(idBytes, Charset.forName("US-ASCII"));
header.identification = new String(idBytes, StandardCharsets.US_ASCII);
}
// Color map, not *really* part of the header
@ -165,7 +268,7 @@ final class TGAHeader {
return header;
}
static IndexColorModel readColorMap(final DataInput stream, final TGAHeader header) throws IOException {
private static IndexColorModel readColorMap(final DataInput stream, final TGAHeader header) throws IOException {
int size = header.colorMapSize;
int depth = header.colorMapDepth;
int bytes = (depth + 7) / 8;
@ -177,7 +280,7 @@ final class TGAHeader {
switch (depth) {
case 16:
// Expand 16 bit to 24 bit RGB
// Expand 16 (15) bit to 24 bit RGB
byte[] temp = cmap;
cmap = new byte[size * 3];
@ -187,13 +290,9 @@ final class TGAHeader {
byte low = temp[i * 2];
byte high = temp[i * 2 + 1];
byte r = (byte) (8 * ((high & 0x7C) >> 2));
byte g = (byte) (8 * ((high & 0x03) << 3 | (low & 0xE0) >> 5));
byte b = (byte) (8 * ((low & 0x1F)));
cmap[i * 3 ] = r;
cmap[i * 3 + 1] = g;
cmap[i * 3 + 2] = b;
cmap[i * 3 ] = (byte) (((high & 0x7C) >> 2) << 3);
cmap[i * 3 + 1] = (byte) (((high & 0x03) << 3 | (low & 0xE0) >> 5) << 3);
cmap[i * 3 + 2] = (byte) (((low & 0x1F)) << 3);
}
hasAlpha = false;

View File

@ -58,14 +58,14 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public final class TGAImageReader extends ImageReaderBase {
final class TGAImageReader extends ImageReaderBase {
// http://www.fileformat.info/format/tga/egff.htm
// http://www.gamers.org/dEngine/quake3/TGA.txt
private TGAHeader header;
private TGAExtensions extensions;
protected TGAImageReader(final ImageReaderSpi provider) {
TGAImageReader(final ImageReaderSpi provider) {
super(provider);
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2017, 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 javax.imageio.ImageWriteParam;
import java.util.Locale;
/**
* TGAImageWriteParam
*/
public final class TGAImageWriteParam extends ImageWriteParam {
@SuppressWarnings("unused")
public TGAImageWriteParam() {
this(null);
}
@SuppressWarnings("WeakerAccess")
public TGAImageWriteParam(final Locale locale) {
super(locale);
compressionTypes = new String[]{"None", "RLE"};
}
static boolean isRLE(final ImageWriteParam param) {
return param != null && param.getCompressionMode() == MODE_EXPLICIT && "RLE".equals(param.getCompressionType());
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2017, 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.ImageWriterBase;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* TGAImageWriter
*/
final class TGAImageWriter extends ImageWriterBase {
TGAImageWriter(ImageWriterSpi provider) {
super(provider);
}
@Override
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
TGAHeader header = TGAHeader.from(imageType.createBufferedImage(1, 1), param);
return new TGAMetadata(header, null);
}
@Override
public IIOMetadata convertImageMetadata(final IIOMetadata inData, final ImageTypeSpecifier imageType, final ImageWriteParam param) {
return null;
}
@Override
public void setOutput(Object output) {
super.setOutput(output);
if (imageOutput != null) {
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
}
}
@Override
public void write(final IIOMetadata streamMetadata, final IIOImage image, final ImageWriteParam param) throws IOException {
assertOutput();
if (image.hasRaster()) {
throw new UnsupportedOperationException("Raster not supported");
}
RenderedImage renderedImage = image.getRenderedImage();
TGAHeader header = TGAHeader.from(renderedImage, param);
header.write(imageOutput);
processImageStarted(0);
WritableRaster rowRaster = header.getPixelDepth() == 32
? 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()
: ImageTypeSpecifier.createFromRenderedImage(renderedImage)
.createBufferedImage(renderedImage.getWidth(), 1)
.getRaster();
DataBuffer buffer = rowRaster.getDataBuffer();
for (int tileY = 0; tileY < renderedImage.getNumYTiles(); tileY++) {
for (int tileX = 0; tileX < renderedImage.getNumXTiles(); tileX++) {
if (abortRequested()) {
break;
}
// Wraps TYPE_INT rasters to TYPE_BYTE
Raster raster = asByteRaster(renderedImage.getTile(tileX, tileY), renderedImage.getColorModel());
for (int y = 0; y < raster.getHeight(); y++) {
if (abortRequested()) {
break;
}
switch (buffer.getDataType()) {
case DataBuffer.TYPE_BYTE:
rowRaster.setDataElements(0, 0, raster.createChild(0, y, raster.getWidth(), 1, 0, 0, null));
imageOutput.write(((DataBufferByte) buffer).getData());
break;
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);
break;
default:
throw new IIOException("Unsupported data");
}
processImageProgress(tileY * 100f / renderedImage.getNumYTiles());
}
}
}
// TODO: If we have thumbnails, we need to write extension too.
processImageComplete();
}
// Vi kan lage en DataBuffer wrapper-klasse,
// som gjør TYPE_INT_RGB/INT_ARGB/INT_ARGB_PRE/INT_BGR til tilsvarende TYPE_xBYTE-klasser.
// Ytelse er ikke viktig her, siden vi uansett konvertere når vi skal skrive/lese.
// TODO: Refactore dette til felles lag?
// TODO: Implementere writable også, slik at vi kan bruke i lesing?
private Raster asByteRaster(final Raster raster, ColorModel colorModel) {
switch (raster.getTransferType()) {
case DataBuffer.TYPE_BYTE:
return raster;
case DataBuffer.TYPE_USHORT:
return raster; // TODO: we handle ushort especially for now..
case DataBuffer.TYPE_INT:
final int bands = colorModel.getNumComponents();
final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
int w = raster.getWidth();
int h = raster.getHeight();
int size = buffer.getSize();
return new Raster(
new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, w, h, bands, w * bands, createBandOffsets(colorModel)),
new DataBuffer(DataBuffer.TYPE_BYTE, size * bands) {
@Override
public int getElem(int bank, int i) {
int index = i / bands;
int shift = (i % bands) * 8;
return (buffer.getElem(index) >>> shift) & 0xFF;
}
@Override
public void setElem(int bank, int i, int val) {
throw new UnsupportedOperationException("Wrapped buffer is read-only");
}
}, new Point()) {};
default:
throw new IllegalArgumentException(String.format("Raster type %d not supported", raster.getTransferType()));
}
}
private int[] createBandOffsets(final ColorModel colorModel) {
notNull(colorModel, "colorModel");
if (colorModel instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel) colorModel;
int[] masks = dcm.getMasks();
int[] offs = new int[masks.length];
for (int i = 0; i < masks.length; i++) {
int mask = masks[i];
int off = 0;
// TODO: FixMe! This only works for standard 8 bit masks (0xFF)
if (mask != 0) {
while ((mask & 0xFF) == 0) {
mask >>>= 8;
off++;
}
}
offs[i] = off;
}
return offs;
}
throw new IllegalArgumentException(String.format("%s not supported", colorModel.getClass().getSimpleName()));
}
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
ImageIO.write(image, "TGA", new File("foo.tga"));
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2017, 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.spi.ImageWriterSpiBase;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.util.Locale;
/**
* TGAImageWriterSpi
*/
public final class TGAImageWriterSpi extends ImageWriterSpiBase {
@SuppressWarnings("WeakerAccess")
public TGAImageWriterSpi() {
super(new TGAProviderInfo());
}
@Override
public boolean canEncodeImage(final ImageTypeSpecifier type) {
// Fast case, it's a known, supported type
switch (type.getBufferedImageType()) {
case BufferedImage.TYPE_INT_RGB:
case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
case BufferedImage.TYPE_3BYTE_BGR:
case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
case BufferedImage.TYPE_USHORT_555_RGB:
case BufferedImage.TYPE_BYTE_GRAY:
case BufferedImage.TYPE_BYTE_INDEXED:
return true;
case BufferedImage.TYPE_BYTE_BINARY: // Could be supported? Uncertain if the format allows < 8 bit/sample for color map entries
case BufferedImage.TYPE_INT_BGR: // TODO: Should be supported, just needs to invert band indices/offsets
case BufferedImage.TYPE_USHORT_565_RGB:
case BufferedImage.TYPE_USHORT_GRAY:
return false;
default:
// Fall through
}
// Inspect color model etc.
ColorSpace colorSpace = type.getColorModel().getColorSpace();
if (!(colorSpace.getType() == ColorSpace.TYPE_RGB || colorSpace.getType() == ColorSpace.TYPE_GRAY)) {
return false;
}
// TODO!
return true;
}
@Override
public ImageWriter createWriterInstance(Object extension) {
return new TGAImageWriter(this);
}
@Override
public String getDescription(Locale locale) {
return "TrueVision TGA image writer";
}
}

View File

@ -37,12 +37,14 @@ import java.awt.*;
import java.awt.image.IndexColorModel;
import java.util.Calendar;
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 = header;
this.header = notNull(header, "header");
this.extensions = extensions;
}

View File

@ -55,8 +55,8 @@ final class TGAProviderInfo extends ReaderWriterProviderInfo {
},
"com.twelvemonkeys.imageio.plugins.tga.TGAImageReader",
new String[] {"com.twelvemonkeys.imageio.plugins.tga.TGAImageReaderSpi"},
null,
null,
"com.twelvemonkeys.imageio.plugins.tga.TGAImageWriter",
new String[] {"com.twelvemonkeys.imageio.plugins.tga.TGAImageWriterSpi"},
false, null, null, null, null,
true, null, null, null, null
);

View File

@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.tga.TGAImageWriterSpi

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2017, 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.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
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.Assume.assumeNotNull;
/**
* TGAImageWriterTest
*/
public class TGAImageWriterTest extends ImageWriterAbstractTest {
private static final TGAImageWriterSpi PROVIDER = new TGAImageWriterSpi();
private static final ImageTypeSpecifier TYPE_USHORT_1555_ARGB = ImageTypeSpecifiers.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB), 0x7C00, 0x03E0, 0x001F, 0x8000, DataBuffer.TYPE_USHORT, false);
@Override
protected ImageWriter createImageWriter() {
return new TGAImageWriter(PROVIDER);
}
@Override
protected List<? extends RenderedImage> getTestData() {
return Arrays.asList(
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB_PRE)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_INT_BGR)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_3BYTE_BGR)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR_PRE)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_USHORT_555_RGB)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_BYTE_GRAY)),
drawSomething(new BufferedImage(10, 10, BufferedImage.TYPE_BYTE_INDEXED)),
drawSomething(TYPE_USHORT_1555_ARGB.createBufferedImage(10, 10))
);
}
@Test
public void testWriteRead() throws IOException {
ImageWriter writer = createImageWriter();
ImageReader reader = ImageIO.getImageReader(writer);
assumeNotNull(reader);
for (RenderedImage testData : getTestData()) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(buffer)) {
writer.setOutput(stream);
writer.write(drawSomething((BufferedImage) testData));
}
try (ImageInputStream stream = ImageIO.createImageInputStream(new ByteArrayInputStream(buffer.toByteArray()))) {
reader.setInput(stream);
BufferedImage image = reader.read(0);
assertNotNull(image);
assertImageDataEquals("Images differ for " + testData, (BufferedImage) testData, image);
}
}
}
}