mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 12:35:29 -04:00
#379 TGA write
This commit is contained in:
parent
5a3945c411
commit
586359e7ab
@ -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. */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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 må 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"));
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.tga.TGAImageWriterSpi
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user