mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 03:55:28 -04:00
parent
a5def1ba3d
commit
0c2433dc9f
85
imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java
Executable file → Normal file
85
imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java
Executable file → Normal file
@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.IIOParam;
|
||||
import javax.imageio.ImageIO;
|
||||
@ -148,11 +149,10 @@ public final class IIOUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pSourceRegion != null) {
|
||||
if (pSourceRegion.x != 0 || pSourceRegion.y != 0 || pSourceRegion.width != pImage.getWidth() || pSourceRegion.height != pImage.getHeight()) {
|
||||
if (pSourceRegion != null
|
||||
&& (pSourceRegion.x != 0 || pSourceRegion.y != 0 || pSourceRegion.width != pImage.getWidth() || pSourceRegion.height != pImage.getHeight())) {
|
||||
return pImage.getSubimage(pSourceRegion.x, pSourceRegion.y, pSourceRegion.width, pSourceRegion.height);
|
||||
}
|
||||
}
|
||||
|
||||
return pImage;
|
||||
}
|
||||
@ -192,7 +192,7 @@ public final class IIOUtil {
|
||||
* The names are all upper-case, and contains no duplicates.
|
||||
*
|
||||
* @return a normalized array of {@code String}s.
|
||||
* @see javax.imageio.ImageIO#getReaderFormatNames()
|
||||
* @see ImageIO#getReaderFormatNames()
|
||||
*/
|
||||
public static String[] getNormalizedReaderFormatNames() {
|
||||
return normalizeNames(ImageIO.getReaderFormatNames());
|
||||
@ -203,7 +203,7 @@ public final class IIOUtil {
|
||||
* The names are all upper-case, and contains no duplicates.
|
||||
*
|
||||
* @return a normalized array of {@code String}s.
|
||||
* @see javax.imageio.ImageIO#getWriterFormatNames()
|
||||
* @see ImageIO#getWriterFormatNames()
|
||||
*/
|
||||
public static String[] getNormalizedWriterFormatNames() {
|
||||
return normalizeNames(ImageIO.getWriterFormatNames());
|
||||
@ -216,6 +216,79 @@ public final class IIOUtil {
|
||||
normalizedNames.add(name.toUpperCase());
|
||||
}
|
||||
|
||||
return normalizedNames.toArray(new String[normalizedNames.size()]);
|
||||
return normalizedNames.toArray(new String[0]);
|
||||
}
|
||||
|
||||
// TODO: RasterUtils? Subsampler?
|
||||
public static void subsampleRow(byte[] srcRow, int srcPos, int srcWidth,
|
||||
byte[] 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 <= 8 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 8 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
Validate.isTrue(samplesPerPixel * bitsPerSample <= 8 || samplesPerPixel * bitsPerSample % 8 == 0,
|
||||
"samplesPerPixel * bitsPerSample must be < 8 or a multiple of 8 ");
|
||||
|
||||
if (bitsPerSample * samplesPerPixel % 8 == 0) {
|
||||
int pixelStride = bitsPerSample * samplesPerPixel / 8;
|
||||
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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Start bit fiddling...
|
||||
int pixelStride = bitsPerSample * samplesPerPixel;
|
||||
int mask = (1 << pixelStride) - 1;
|
||||
|
||||
for (int x = 0; x < srcWidth; x += samplePeriod) {
|
||||
int dstOff = (destPos + x / samplePeriod) * pixelStride / 8;
|
||||
int srcOff = (srcPos + x) * pixelStride / 8;
|
||||
|
||||
int srcBitPos = 8 - pixelStride - (x * pixelStride) % 8;
|
||||
int srcMask = mask << srcBitPos;
|
||||
|
||||
int dstBitPos = 8 - pixelStride - (x * pixelStride / samplePeriod) % 8;
|
||||
int dstMask = ~(mask << dstBitPos);
|
||||
|
||||
int val = ((srcRow[srcOff] & srcMask) >> srcBitPos);
|
||||
destRow[dstOff] = (byte) ((destRow[dstOff] & dstMask) | val << dstBitPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void subsampleRow(short[] srcRow, int srcPos, int srcWidth,
|
||||
short[] 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 <= 16 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 16 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
Validate.isTrue(samplesPerPixel * bitsPerSample <= 16 || samplesPerPixel * bitsPerSample % 16 == 0,
|
||||
"samplesPerPixel * bitsPerSample must be < 16 or a multiple of 16 ");
|
||||
|
||||
int pixelStride = bitsPerSample * samplesPerPixel / 16;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static void subsampleRow(int[] srcRow, int srcPos, int srcWidth,
|
||||
int[] 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
/**
|
||||
* IIOUtilTest
|
||||
*/
|
||||
public class IIOUtilTest {
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2Byte() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {-1, 0, (byte) 0xAA, 0, -1};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {-1, (byte) 0xAA, -1};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length, output, 0, 1, 8, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2ByteStride3() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {-1, -1, -1, 0, 0, 0, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, 0, 0, 0, -1, -1, -1};
|
||||
byte[] output = new byte[9];
|
||||
byte[] expected = {-1, -1, -1, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, -1, -1, -1};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length / 3, output, 0, 3, 8, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2Byte1() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {(byte) 0xaa, (byte) 0xaa, (byte) 0xaa};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {(byte) 0xff, (byte) 0xf0};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length * 8, output, 0, 1, 1, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod3_1Bit() {
|
||||
int period = 3;
|
||||
|
||||
byte[] input = {(byte) 0x92, (byte) 0x49, (byte) 0x24};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {(byte) 0xff};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length * 8, output, 0, 1, 1, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2_2Bit() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {(byte) 0xcc, (byte) 0xcc, (byte) 0xcc};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {(byte) 0xff, (byte) 0xf0};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length * 4, output, 0, 1, 2, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2_4Bit() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {(byte) 0xf0, (byte) 0xf0, (byte) 0xf0};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {(byte) 0xff, (byte) 0xf0};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length * 2, output, 0, 1, 4, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2_1Bit2Samples() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {(byte) 0xcc, (byte) 0xcc, (byte) 0xcc};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {(byte) 0xff, (byte) 0xf0};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length * 4, output, 0, 2, 1, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2_2Bit2Samples() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {(byte) 0xf0, (byte) 0xf0, (byte) 0xf0};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {(byte) 0xff, (byte) 0xf0};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length * 2, output, 0, 2, 2, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2_4Bit2Samples() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {-1, 0, (byte) 0xAA, 0, -1};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {-1, (byte) 0xAA, -1};
|
||||
|
||||
IIOUtil.subsampleRow(input, 0, input.length, output, 0, 2, 4, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsampleRowPeriod2_1BitOffset1() {
|
||||
int period = 2;
|
||||
|
||||
byte[] input = {(byte) 0xaa, (byte) 0xaa, (byte) 0xaa};
|
||||
byte[] output = new byte[divCeil(input.length, period)];
|
||||
byte[] expected = {(byte) 0xff, (byte) 0xf0};
|
||||
|
||||
IIOUtil.subsampleRow(input, 1, input.length * 8, output, 0, 1, 1, period);
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
private int divCeil(int numerator, int denominator) {
|
||||
return (numerator + denominator - 1) / denominator;
|
||||
}
|
||||
}
|
@ -48,6 +48,7 @@ import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
@ -82,8 +83,7 @@ import java.util.*;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.createStreamAdapter;
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName;
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.*;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
@ -1841,6 +1841,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
DataBuffer dataBuffer = tileRowRaster.getDataBuffer();
|
||||
int bands = dataBuffer.getNumBanks();
|
||||
boolean banded = bands > 1;
|
||||
int bitsPerSample = getBitsPerSample();
|
||||
|
||||
switch (tileRowRaster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
@ -1870,9 +1871,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + colsInTile) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataByte, x * xSub, rowDataByte, x, numBands);
|
||||
}
|
||||
IIOUtil.subsampleRow(rowDataByte, srcRegion.x * numBands, colsInTile,
|
||||
rowDataByte, srcRegion.x * numBands / xSub, numBands, bitsPerSample, xSub);
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol / xSub, (row - srcRegion.y) / ySub, srcChannel);
|
||||
@ -1913,9 +1913,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + colsInTile) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataShort, x * xSub, rowDataShort, x, numBands);
|
||||
}
|
||||
subsampleRow(rowDataShort, srcRegion.x * numBands, colsInTile,
|
||||
rowDataShort, srcRegion.x * numBands / xSub, numBands, bitsPerSample, xSub);
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol / xSub, (row - srcRegion.y) / ySub, srcChannel);
|
||||
@ -1950,9 +1949,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + colsInTile) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataInt, x * xSub, rowDataInt, x, numBands);
|
||||
}
|
||||
subsampleRow(rowDataInt, srcRegion.x * numBands, colsInTile,
|
||||
rowDataInt, srcRegion.x * numBands / xSub, numBands, bitsPerSample, xSub);
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol / xSub, (row - srcRegion.y) / ySub, srcChannel);
|
||||
@ -1998,6 +1996,9 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new AssertionError("Unsupported data type: " + tileRowRaster.getTransferType());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2294,7 +2295,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
case TIFFBaseline.COMPRESSION_NONE:
|
||||
return stream;
|
||||
case TIFFBaseline.COMPRESSION_PACKBITS:
|
||||
return new DecoderStream(createFillOrderStream(fillOrder, stream), new PackBitsDecoder(), 1024);
|
||||
return new DecoderStream(createFillOrderStream(fillOrder, stream), new PackBitsDecoder(), 256);
|
||||
case TIFFExtension.COMPRESSION_LZW:
|
||||
// NOTE: Needs large buffer for compatibility with certain encoders
|
||||
return new DecoderStream(createFillOrderStream(fillOrder, stream), LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), Math.max(width * bands, 4096));
|
||||
@ -2469,6 +2470,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadRaster() {
|
||||
return true;
|
||||
|
@ -708,6 +708,29 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSubsampleParamPixelsBinary() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = new TestData(getClassLoaderResource("/tiff/ccitt/group3_2d.tif"), new Dimension(6, 4));
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
|
||||
BufferedImage image = null;
|
||||
BufferedImage subsampled = null;
|
||||
try {
|
||||
image = reader.read(0, param);
|
||||
|
||||
param.setSourceSubsampling(2, 2, 0, 0);
|
||||
subsampled = reader.read(0, param);
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
|
||||
assertSubsampledImageDataEquals("Subsampled image data does not match expected", image, subsampled, param);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSubsampleParamPixelsJPEG() throws IOException {
|
||||
// Tiled "new style" JPEG
|
||||
|
Loading…
x
Reference in New Issue
Block a user