mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-28 00:00:03 -04:00
#714 PNM: Add support for writing TYPE_INT_* images + implementation of WriterSpi.canEncode
(cherry picked from commit 26981513d8)
This commit is contained in:
+25
-21
@@ -30,8 +30,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
@@ -40,7 +38,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -53,11 +51,7 @@ abstract class HeaderWriter {
|
||||
this.imageOutput = imageOutput;
|
||||
}
|
||||
|
||||
public static void write(final IIOImage image, final ImageWriterBase writer, final ImageOutputStream imageOutput) throws IOException {
|
||||
createHeaderWriter(writer.getFormatName(), imageOutput).writeHeader(image, writer.getOriginatingProvider());
|
||||
}
|
||||
|
||||
private static HeaderWriter createHeaderWriter(final String formatName, final ImageOutputStream imageOutput) {
|
||||
static HeaderWriter createHeaderWriter(final String formatName, final ImageOutputStream imageOutput) {
|
||||
if (formatName.equals("pam")) {
|
||||
return new PAMHeaderWriter(imageOutput);
|
||||
}
|
||||
@@ -83,25 +77,36 @@ abstract class HeaderWriter {
|
||||
return image.hasRaster() ? image.getRaster().getNumBands() : image.getRenderedImage().getSampleModel().getNumBands();
|
||||
}
|
||||
|
||||
protected final SampleModel getSampleModel(final IIOImage image) {
|
||||
return image.hasRaster() ? image.getRaster().getSampleModel() : image.getRenderedImage().getSampleModel();
|
||||
}
|
||||
|
||||
protected int getMaxVal(final IIOImage image) {
|
||||
int transferType = getTransferType(image);
|
||||
|
||||
if (transferType == DataBuffer.TYPE_BYTE) {
|
||||
return PNM.MAX_VAL_8BIT;
|
||||
}
|
||||
else if (transferType == DataBuffer.TYPE_USHORT) {
|
||||
return PNM.MAX_VAL_16BIT;
|
||||
}
|
||||
// else if (transferType == DataBuffer.TYPE_INT) {
|
||||
// TODO: Support TYPE_INT through conversion, if number of channels is 3 or 4 (TYPE_INT_RGB, TYPE_INT_ARGB)
|
||||
// }
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported data type: " + transferType);
|
||||
switch (transferType) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return PNM.MAX_VAL_8BIT;
|
||||
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
return PNM.MAX_VAL_16BIT;
|
||||
|
||||
case DataBuffer.TYPE_INT:
|
||||
// We support TYPE_INT through conversion, if number of channels is 3 or 4 (TYPE_INT_RGB, TYPE_INT_ARGB)
|
||||
SampleModel sampleModel = getSampleModel(image);
|
||||
int numBands = sampleModel.getNumBands();
|
||||
|
||||
if (sampleModel instanceof SinglePixelPackedSampleModel && (numBands == 3 || numBands == 4)) {
|
||||
return PNM.MAX_VAL_8BIT;
|
||||
}
|
||||
// ...else fall through
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported data type: " + transferType);
|
||||
}
|
||||
}
|
||||
|
||||
protected final int getTransferType(final IIOImage image) {
|
||||
return image.hasRaster() ? image.getRaster().getTransferType() : image.getRenderedImage().getSampleModel().getTransferType();
|
||||
return image.hasRaster() ? image.getRaster().getTransferType() : image.getRenderedImage().getSampleModel().getTransferType();
|
||||
}
|
||||
|
||||
protected final void writeComments(final IIOMetadata metadata, final ImageWriterSpi provider) throws IOException {
|
||||
@@ -120,5 +125,4 @@ abstract class HeaderWriter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-5
@@ -30,6 +30,8 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
@@ -44,6 +46,8 @@ final class PAMHeaderWriter extends HeaderWriter {
|
||||
|
||||
@Override
|
||||
public void writeHeader(final IIOImage image, final ImageWriterSpi provider) throws IOException {
|
||||
TupleType tupleType = tupleType(image);
|
||||
|
||||
// Write PAM magic
|
||||
imageOutput.writeShort(PNM.PAM);
|
||||
imageOutput.write('\n');
|
||||
@@ -52,12 +56,19 @@ final class PAMHeaderWriter extends HeaderWriter {
|
||||
// Write width/height and number of channels
|
||||
imageOutput.write(String.format("WIDTH %s\nHEIGHT %s\n", getWidth(image), getHeight(image)).getBytes(UTF_8));
|
||||
imageOutput.write(String.format("DEPTH %s\n", getNumBands(image)).getBytes(UTF_8));
|
||||
|
||||
// TODO: maxSample (8 or16 bit)
|
||||
imageOutput.write(String.format("MAXVAL %s\n", getMaxVal(image)).getBytes(UTF_8));
|
||||
|
||||
// TODO: Determine tuple type based on input color model and image data
|
||||
TupleType tupleType = getNumBands(image) > 3 ? TupleType.RGB_ALPHA : TupleType.RGB;
|
||||
imageOutput.write(String.format("TUPLTYPE %s\nENDHDR\n", tupleType).getBytes(UTF_8));
|
||||
}
|
||||
|
||||
private static TupleType tupleType(IIOImage image) {
|
||||
TupleType tupleType = image.hasRaster()
|
||||
? TupleType.forPAM(image.getRaster())
|
||||
: TupleType.forPAM(ImageTypeSpecifiers.createFromRenderedImage(image.getRenderedImage()));
|
||||
|
||||
if (tupleType == null) {
|
||||
throw new IllegalArgumentException("Unknown TupleType for " + (image.hasRaster() ? image.getRaster() : image.getRenderedImage()));
|
||||
}
|
||||
|
||||
return tupleType;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -45,12 +45,11 @@ public final class PAMImageWriterSpi extends ImageWriterSpiBase {
|
||||
super(new PAMProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier pType) {
|
||||
// TODO: FixMe
|
||||
return true;
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||
return TupleType.forPAM(type) != null;
|
||||
}
|
||||
|
||||
public ImageWriter createWriterInstance(final Object pExtension) {
|
||||
public ImageWriter createWriterInstance(final Object extension) {
|
||||
return new PNMImageWriter(this);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ final class PFMHeaderParser extends HeaderParser {
|
||||
public PNMHeader parse() throws IOException {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
float maxSample = tupleType == TupleType.BLACKANDWHITE_WHITE_IS_ZERO ? 1 : 0; // PBM has no maxSample line
|
||||
float maxSample = 0;
|
||||
|
||||
List<String> comments = new ArrayList<>();
|
||||
|
||||
@@ -77,7 +77,7 @@ final class PFMHeaderParser extends HeaderParser {
|
||||
String line = input.readLine();
|
||||
|
||||
if (line == null) {
|
||||
throw new IIOException("Unexpeced end of stream");
|
||||
throw new IIOException("Unexpected end of stream");
|
||||
}
|
||||
|
||||
int commentStart = line.indexOf('#');
|
||||
|
||||
+34
-2
@@ -30,6 +30,8 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
@@ -45,8 +47,7 @@ final class PNMHeaderWriter extends HeaderWriter {
|
||||
@Override
|
||||
public void writeHeader(final IIOImage image, final ImageWriterSpi provider) throws IOException {
|
||||
// Write P4/P5/P6 magic (Support only RAW formats for now; if we are to support PLAIN formats, pass parameter)
|
||||
// TODO: Determine PBM, PBM or PPM based on input color model and image data?
|
||||
short type = PNM.PPM;
|
||||
short type = type(image);
|
||||
imageOutput.writeShort(type);
|
||||
imageOutput.write('\n');
|
||||
|
||||
@@ -61,4 +62,35 @@ final class PNMHeaderWriter extends HeaderWriter {
|
||||
imageOutput.write(String.format("%s\n", getMaxVal(image)).getBytes(UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private short type(IIOImage image) {
|
||||
TupleType type = tupleType(image);
|
||||
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case BLACKANDWHITE_WHITE_IS_ZERO:
|
||||
return PNM.PBM;
|
||||
case GRAYSCALE:
|
||||
return PNM.PGM;
|
||||
case RGB:
|
||||
return PNM.PPM;
|
||||
default:
|
||||
// fall through...
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported tupleType: " + type);
|
||||
}
|
||||
|
||||
private static TupleType tupleType(IIOImage image) {
|
||||
TupleType tupleType = image.hasRaster()
|
||||
? TupleType.forPNM(image.getRaster())
|
||||
: TupleType.forPNM(ImageTypeSpecifiers.createFromRenderedImage(image.getRenderedImage()));
|
||||
|
||||
if (tupleType == null) {
|
||||
throw new IllegalArgumentException("Unknown TupleType for " + (image.hasRaster() ? image.getRaster() : image.getRenderedImage()));
|
||||
}
|
||||
|
||||
return tupleType;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-8
@@ -31,14 +31,17 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.util.RasterUtils;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.SampleModel;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -69,7 +72,7 @@ public final class PNMImageWriter extends ImageWriterBase {
|
||||
// TODO: Issue warning if streamMetadata is non-null?
|
||||
// TODO: Issue warning if IIOImage contains thumbnails or other data we can't store?
|
||||
|
||||
HeaderWriter.write(image, this, imageOutput);
|
||||
writeHeader(image, this, imageOutput);
|
||||
|
||||
// TODO: Sub region
|
||||
// TODO: Subsampling
|
||||
@@ -80,16 +83,20 @@ public final class PNMImageWriter extends ImageWriterBase {
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
private void writeHeader(final IIOImage image, final ImageWriterBase writer, final ImageOutputStream imageOutput) throws IOException {
|
||||
HeaderWriter.createHeaderWriter(writer.getFormatName(), imageOutput).writeHeader(image, writer.getOriginatingProvider());
|
||||
}
|
||||
|
||||
private void writeImageData(final IIOImage image) throws IOException {
|
||||
// - dump data as is (or convert, if TYPE_INT_xxx)
|
||||
// - dump data as is (or convert, if TYPE_INT_xxx
|
||||
// Enforce RGB/CMYK order for such data!
|
||||
|
||||
// TODO: Loop over x/y tiles, using 0,0 is only valid for BufferedImage
|
||||
// TODO: PNM/PAM does not support tiling, we must iterate all tiles along the x-axis for each row we write
|
||||
Raster tile = image.hasRaster() ? image.getRaster() : image.getRenderedImage().getTile(0, 0);
|
||||
tile = tile.getTransferType() == DataBuffer.TYPE_INT ? RasterUtils.asByteRaster(tile) : tile;
|
||||
|
||||
SampleModel sampleModel = tile.getSampleModel();
|
||||
|
||||
DataBuffer dataBuffer = tile.getDataBuffer();
|
||||
|
||||
int tileWidth = tile.getWidth();
|
||||
|
||||
+3
-4
@@ -48,12 +48,11 @@ public final class PNMImageWriterSpi extends ImageWriterSpiBase {
|
||||
super(new PNMProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier pType) {
|
||||
// TODO: FixMe: Support only 1 bit b/w, 8-16 bit gray and 8-16 bit/sample RGB
|
||||
return true;
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||
return TupleType.forPNM(type) != null;
|
||||
}
|
||||
|
||||
public ImageWriter createWriterInstance(final Object pExtension) {
|
||||
public ImageWriter createWriterInstance(final Object extension) {
|
||||
return new PNMImageWriter(this);
|
||||
}
|
||||
|
||||
|
||||
+149
@@ -30,7 +30,10 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
enum TupleType {
|
||||
// Official:
|
||||
@@ -80,4 +83,150 @@ enum TupleType {
|
||||
public boolean isValidMaxSample(int maxSample) {
|
||||
return maxSample >= minMaxSample && maxSample <= maxMaxSample;
|
||||
}
|
||||
|
||||
|
||||
static TupleType forPNM(Raster raster) {
|
||||
return filterPNM(forPAM(raster));
|
||||
}
|
||||
|
||||
static TupleType forPNM(ImageTypeSpecifier type) {
|
||||
return filterPNM(forPAM(type));
|
||||
}
|
||||
|
||||
private static TupleType filterPNM(TupleType tupleType) {
|
||||
if (tupleType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (tupleType) {
|
||||
case BLACKANDWHITE:
|
||||
return BLACKANDWHITE_WHITE_IS_ZERO;
|
||||
case GRAYSCALE:
|
||||
case RGB:
|
||||
return tupleType;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static TupleType forPAM(Raster raster) {
|
||||
SampleModel sampleModel = raster.getSampleModel();
|
||||
switch (sampleModel.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
case DataBuffer.TYPE_INT:
|
||||
// B/W, Gray or RGB
|
||||
int bands = sampleModel.getNumBands();
|
||||
|
||||
if (bands == 1 && sampleModel.getSampleSize(0) == 1) {
|
||||
return TupleType.BLACKANDWHITE;
|
||||
}
|
||||
else if (bands == 2 && sampleModel.getSampleSize(0) == 1 && sampleModel.getSampleSize(1) == 1) {
|
||||
return TupleType.BLACKANDWHITE_ALPHA;
|
||||
}
|
||||
|
||||
// We can only write 8 or 16 bits/band
|
||||
if (!(sampleModel.getSampleSize(0) == 8 || sampleModel.getSampleSize(0) == 16)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 1; i < bands; i++) {
|
||||
if (sampleModel.getSampleSize(0) != sampleModel.getSampleSize(i)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (bands == 1) {
|
||||
return TupleType.GRAYSCALE;
|
||||
}
|
||||
else if (bands == 2) {
|
||||
return TupleType.GRAYSCALE_ALPHA;
|
||||
}
|
||||
else if (bands == 3) {
|
||||
return TupleType.RGB;
|
||||
}
|
||||
else if (bands == 4) {
|
||||
return TupleType.RGB_ALPHA;
|
||||
}
|
||||
// ...else fall through...
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static TupleType forPAM(ImageTypeSpecifier type) {
|
||||
// Support only 1 bit b/w, 8-16 bit gray and 8-16 bit/sample RGB
|
||||
switch (type.getBufferedImageType()) {
|
||||
// 1 bit b/w or b/w + a
|
||||
case BufferedImage.TYPE_BYTE_BINARY:
|
||||
switch (type.getNumBands()) {
|
||||
case 1:
|
||||
return type.getBitsPerBand(0) == 1 ? TupleType.BLACKANDWHITE : null;
|
||||
case 2:
|
||||
return type.getBitsPerBand(0) == 2 || type.getBitsPerBand(0) == 1 && type.getBitsPerBand(1) == 1 ? TupleType.BLACKANDWHITE_ALPHA : null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
// Gray
|
||||
case BufferedImage.TYPE_BYTE_GRAY:
|
||||
case BufferedImage.TYPE_USHORT_GRAY:
|
||||
return TupleType.GRAYSCALE;
|
||||
// RGB
|
||||
case BufferedImage.TYPE_3BYTE_BGR:
|
||||
case BufferedImage.TYPE_INT_RGB:
|
||||
case BufferedImage.TYPE_INT_BGR:
|
||||
return TupleType.RGB;
|
||||
// RGBA
|
||||
case BufferedImage.TYPE_4BYTE_ABGR:
|
||||
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
|
||||
case BufferedImage.TYPE_INT_ARGB:
|
||||
case BufferedImage.TYPE_INT_ARGB_PRE:
|
||||
return TupleType.RGB_ALPHA;
|
||||
default:
|
||||
// BYTE, USHORT or INT (packed)
|
||||
switch (type.getSampleModel().getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
case DataBuffer.TYPE_INT:
|
||||
// Gray or RGB
|
||||
ColorModel colorModel = type.getColorModel();
|
||||
|
||||
if (!(colorModel instanceof IndexColorModel)) {
|
||||
ColorSpace cs = colorModel.getColorSpace();
|
||||
|
||||
// We can only write 8 or 16 bits/band
|
||||
int bands = type.getNumBands();
|
||||
if (!(type.getBitsPerBand(0) == 8 || type.getBitsPerBand(0) == 16)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 1; i < bands; i++) {
|
||||
if (type.getBitsPerBand(0) != type.getBitsPerBand(i)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cs.getType() == ColorSpace.TYPE_GRAY && bands == 1) {
|
||||
return TupleType.GRAYSCALE;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_GRAY && colorModel.hasAlpha() && bands == 2) {
|
||||
return TupleType.GRAYSCALE_ALPHA;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_RGB && bands == 3) {
|
||||
return TupleType.RGB;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_RGB && colorModel.hasAlpha() && bands == 4) {
|
||||
return TupleType.RGB_ALPHA;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_CMYK && bands == 4) {
|
||||
return TupleType.CMYK;
|
||||
}
|
||||
else if (cs.getType() == ColorSpace.TYPE_CMYK && colorModel.hasAlpha() && bands == 5) {
|
||||
return TupleType.CMYK_ALPHA;
|
||||
}
|
||||
// ...else fall through...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user