mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 12:05:29 -04:00
Better PFM support.
(cherry picked from commit 623d13a517b7b727076769976a2a9f4afe2a552f)
This commit is contained in:
parent
15c7cfe9a6
commit
75ff0f265f
@ -291,4 +291,21 @@ public final class IIOUtil {
|
||||
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||
}
|
||||
}
|
||||
|
||||
public static void subsampleRow(float[] srcRow, int srcPos, int srcWidth,
|
||||
float[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 32 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 32 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
Validate.isTrue(samplesPerPixel * bitsPerSample <= 32 || samplesPerPixel * bitsPerSample % 32 == 0,
|
||||
"samplesPerPixel * bitsPerSample must be < 32 or a multiple of 32 ");
|
||||
|
||||
int pixelStride = bitsPerSample * samplesPerPixel / 32;
|
||||
for (int x = 0; x < srcWidth * pixelStride; x += samplePeriod * pixelStride) {
|
||||
// System.arraycopy should be intrinsic, but consider using direct array access for pixelStride == 1
|
||||
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||
}
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ final class PNMHeader {
|
||||
private final TupleType tupleType;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final float maxSampleFloat;
|
||||
private final int maxSample;
|
||||
|
||||
private final List<String> comments;
|
||||
@ -57,6 +58,7 @@ final class PNMHeader {
|
||||
this.height = isTrue(height > 0, height, "height must be greater than: %d");
|
||||
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
|
||||
this.maxSample = isTrue(tupleType.isValidMaxSample(maxSample), maxSample, "maxSample out of range: %d");
|
||||
this.maxSampleFloat = this.maxSample;
|
||||
|
||||
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments));
|
||||
|
||||
@ -70,7 +72,8 @@ final class PNMHeader {
|
||||
this.height = isTrue(height > 0, height, "height must be greater than: %d");
|
||||
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
|
||||
|
||||
this.maxSample = -1;
|
||||
this.maxSample = 1;
|
||||
this.maxSampleFloat = maxSample;
|
||||
this.byteOrder = byteOrder;
|
||||
|
||||
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments));
|
||||
@ -118,11 +121,8 @@ final class PNMHeader {
|
||||
if (maxSample <= PNM.MAX_VAL_16BIT) {
|
||||
return 16;
|
||||
}
|
||||
if ((maxSample & 0xffffffffL) <= PNM.MAX_VAL_32BIT) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
throw new AssertionError("maxSample exceeds 32 bit");
|
||||
return 32;
|
||||
}
|
||||
|
||||
public int getTransferType() {
|
||||
@ -135,11 +135,8 @@ final class PNMHeader {
|
||||
if (maxSample <= PNM.MAX_VAL_16BIT) {
|
||||
return DataBuffer.TYPE_USHORT;
|
||||
}
|
||||
if ((maxSample & 0xffffffffL) <= PNM.MAX_VAL_32BIT) {
|
||||
return DataBuffer.TYPE_INT;
|
||||
}
|
||||
|
||||
throw new AssertionError("maxSample exceeds 32 bit");
|
||||
return DataBuffer.TYPE_INT;
|
||||
}
|
||||
|
||||
public List<String> getComments() {
|
||||
|
@ -49,11 +49,13 @@ import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
|
||||
|
||||
public final class PNMImageReader extends ImageReaderBase {
|
||||
// TODO: Allow reading unknown tuple types as Raster!
|
||||
// TODO: readAsRenderedImage?
|
||||
@ -83,7 +85,7 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
|
||||
static String asASCII(final short type) {
|
||||
byte[] asciiBytes = {(byte) ((type >> 8) & 0xff), (byte) (type & 0xff)};
|
||||
return new String(asciiBytes, Charset.forName("ASCII"));
|
||||
return new String(asciiBytes, StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,7 +152,6 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
|
||||
default:
|
||||
// TODO: Allow reading unknown tuple types as Raster!
|
||||
|
||||
throw new AssertionError("Unknown PNM tuple type: " + header.getTupleType());
|
||||
}
|
||||
}
|
||||
@ -261,7 +262,7 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (transferType) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
readRowByte(destRaster, clippedRow, colorConvert, rowDataByte, samplesPerPixel, input, y, srcRegion, xSub, ySub);
|
||||
readRowByte(destRaster, clippedRow, colorConvert, rowDataByte, header.getBitsPerSample(), samplesPerPixel, input, y, srcRegion, xSub, ySub);
|
||||
break;
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
readRowUShort(destRaster, clippedRow, rowDataUShort, samplesPerPixel, input, y, srcRegion, xSub, ySub);
|
||||
@ -339,7 +340,7 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
Raster rowRaster,
|
||||
final ColorConvertOp colorConvert,
|
||||
final byte[] rowDataByte,
|
||||
final int samplesPerPixel,
|
||||
final int bitsPerSample, final int samplesPerPixel,
|
||||
final DataInput input, final int y,
|
||||
final Rectangle srcRegion,
|
||||
final int xSub, final int ySub) throws IOException {
|
||||
@ -352,7 +353,9 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
input.readFully(rowDataByte);
|
||||
|
||||
// Subsample (horizontal)
|
||||
subsampleHorizontal(rowDataByte, rowDataByte.length, samplesPerPixel, xSub);
|
||||
if (xSub > 1) {
|
||||
subsampleRow(rowDataByte, srcRegion.x, srcRegion.width, rowDataByte, 0, samplesPerPixel, bitsPerSample, xSub);
|
||||
}
|
||||
|
||||
normalize(rowDataByte, 0, rowDataByte.length / xSub);
|
||||
|
||||
@ -379,7 +382,9 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
readFully(input, rowDataUShort);
|
||||
|
||||
// Subsample (horizontal)
|
||||
subsampleHorizontal(rowDataUShort, rowDataUShort.length, samplesPerPixel, xSub);
|
||||
if (xSub > 1) {
|
||||
subsampleRow(rowDataUShort, srcRegion.x, srcRegion.width, rowDataUShort, 0, samplesPerPixel, 16, xSub);
|
||||
}
|
||||
|
||||
normalize(rowDataUShort);
|
||||
|
||||
@ -402,11 +407,14 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
readFully(input, rowDataFloat);
|
||||
|
||||
// Subsample (horizontal)
|
||||
subsampleHorizontal(rowDataFloat, rowDataFloat.length, samplesPerPixel, xSub);
|
||||
if (xSub > 1) {
|
||||
subsampleRow(rowDataFloat, srcRegion.x, srcRegion.width, rowDataFloat, 0, samplesPerPixel, 32, xSub);
|
||||
}
|
||||
|
||||
normalize(rowDataFloat);
|
||||
|
||||
int destY = (y - srcRegion.y) / ySub;
|
||||
// Note: PFM is stored bottom to top!
|
||||
int destY = destRaster.getHeight() - 1 - (y - srcRegion.y) / ySub;
|
||||
// TODO: ColorConvertOp if needed
|
||||
destRaster.setDataElements(0, destY, rowRaster);
|
||||
}
|
||||
@ -437,19 +445,6 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SuspiciousSystemArraycopy")
|
||||
private void subsampleHorizontal(final Object data, final int length, final int samplesPerPixel, final int xSub) {
|
||||
if (xSub == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Super-special 1 bit subsampling handling for PBM
|
||||
|
||||
for (int x = 0; x < length / xSub; x += samplesPerPixel) {
|
||||
System.arraycopy(data, x * xSub, data, x, samplesPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
private void normalize(final byte[] rowData, final int start, final int length) {
|
||||
switch (header.getTupleType()) {
|
||||
case BLACKANDWHITE:
|
||||
@ -484,13 +479,9 @@ public final class PNMImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void normalize(final float[] rowData) {
|
||||
// TODO: Do the real thing, find min/max and normalize to range 0...255? But only if not reading raster..? Only support reading as raster?
|
||||
// Normalize
|
||||
for (int i = 0; i < rowData.length; i++) {
|
||||
// if (rowData[i] > 275f /*header.getMaxSampleFloat()*/) {
|
||||
// System.out.println("rowData[" + i + "]: " + rowData[i]);
|
||||
// }
|
||||
// rowData[i] = rowData[i] / 275f /*header.getMaxSampleFloat()*/;
|
||||
rowData[i] = Math.min(1, rowData[i]); // Clamp
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,10 @@ final class PNMMetadata extends AbstractMetadata {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientation.setAttribute("value", "Normal");
|
||||
imageOrientation.setAttribute("value",
|
||||
header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
|
||||
? "FlipH"
|
||||
: "Normal");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
|
Loading…
x
Reference in New Issue
Block a user