mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 12:05:29 -04:00
#677 Fixed integer overflow + added tests
This commit is contained in:
parent
aab5b062bd
commit
ab13fdd09d
@ -30,21 +30,26 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.color.ColorProfiles;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.Rational;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME;
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadata.configureStreamByteOrder;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOWriteWarningListener;
|
||||
@ -54,18 +59,21 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME;
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadata.configureStreamByteOrder;
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.color.ColorProfiles;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
/**
|
||||
* TIFFImageWriter
|
||||
@ -104,8 +112,6 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
// Support storing multiple images in one stream (multi-page TIFF)
|
||||
// Support more of the ImageIO metadata (ie. compression from metadata, etc)
|
||||
|
||||
private static final Rational STANDARD_DPI = new Rational(72);
|
||||
|
||||
/**
|
||||
* Flag for active sequence writing
|
||||
*/
|
||||
@ -411,7 +417,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
|
||||
case TIFFExtension.COMPRESSION_LZW:
|
||||
stream = IIOUtil.createStreamAdapter(imageOutput);
|
||||
stream = new EncoderStream(stream, new LZWEncoder(((image.getTileWidth() * samplesPerPixel * bitPerSample + 7) / 8) * image.getTileHeight()));
|
||||
stream = new EncoderStream(stream, new LZWEncoder((((long) image.getTileWidth() * samplesPerPixel * bitPerSample + 7) / 8) * image.getTileHeight()));
|
||||
if (entries.containsKey(TIFF.TAG_PREDICTOR) && entries.get(TIFF.TAG_PREDICTOR).getValue().equals(TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING)) {
|
||||
stream = new HorizontalDifferencingStream(stream, image.getTileWidth(), samplesPerPixel, bitPerSample, imageOutput.getByteOrder());
|
||||
}
|
||||
@ -587,15 +593,16 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
case DataBuffer.TYPE_SHORT:
|
||||
final int shortStride = stride / 2;
|
||||
if (numComponents == 1) {
|
||||
// System.err.println("Writing USHORT -> " + numBands * 2 + "_BYTES");
|
||||
|
||||
for (int b = 0; b < dataBuffer.getNumBanks(); b++) {
|
||||
for (int y = offsetY; y < tileHeight + offsetY; y++) {
|
||||
int yOff = y * stride / 2;
|
||||
final int yOff = y * shortStride;
|
||||
|
||||
for (int x = offsetX; x < tileWidth + offsetX; x++) {
|
||||
final int xOff = yOff + x;
|
||||
int xOff = yOff + x;
|
||||
|
||||
buffer.putShort((short) (dataBuffer.getElem(b, xOff) & 0xffff));
|
||||
}
|
||||
@ -637,15 +644,15 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
|
||||
case DataBuffer.TYPE_INT:
|
||||
// TODO: This is incorrect for 32 bits/sample, only works for packed (INT_(A)RGB)
|
||||
final int intStride = stride / 4;
|
||||
if (1 == numComponents) {
|
||||
// System.err.println("Writing INT -> " + numBands * 4 + "_BYTES");
|
||||
|
||||
for (int b = 0; b < dataBuffer.getNumBanks(); b++) {
|
||||
for (int y = offsetY; y < tileHeight + offsetY; y++) {
|
||||
int yOff = y * stride / 4;
|
||||
int yOff = y * intStride;
|
||||
|
||||
for (int x = offsetX; x < tileWidth + offsetX; x++) {
|
||||
final int xOff = yOff + x;
|
||||
int xOff = yOff + x;
|
||||
|
||||
buffer.putInt(dataBuffer.getElem(b, xOff));
|
||||
}
|
||||
|
@ -30,20 +30,33 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.Rational;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
import com.twelvemonkeys.io.NullOutputStream;
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME;
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataTest.createTIFFFieldNode;
|
||||
import static com.twelvemonkeys.imageio.util.ImageReaderAbstractTest.assertRGBEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.NodeList;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.event.IIOWriteProgressListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
@ -53,22 +66,20 @@ import javax.imageio.stream.FileCacheImageOutputStream;
|
||||
import javax.imageio.stream.FileImageOutputStream;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.imageio.stream.ImageOutputStreamImpl;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME;
|
||||
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataTest.createTIFFFieldNode;
|
||||
import static com.twelvemonkeys.imageio.util.ImageReaderAbstractTest.assertRGBEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.mockito.Mockito.*;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.Rational;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
import com.twelvemonkeys.io.NullOutputStream;
|
||||
|
||||
/**
|
||||
* TIFFImageWriterTest
|
||||
@ -638,7 +649,12 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter
|
||||
int maxH = Math.min(300, image.getHeight());
|
||||
for (int y = 0; y < maxH; y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
assertRGBEquals(String.format("Pixel differ: @%d,%d", x, y), orig.getRGB(x, y), image.getRGB(x, y), 0);
|
||||
try {
|
||||
assertRGBEquals("", orig.getRGB(x, y), image.getRGB(x, y), 0);
|
||||
}
|
||||
catch (AssertionError err) {
|
||||
fail(String.format("Pixel differ: @%d,%d %s", x, y, err.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1266,6 +1282,50 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShortOverflowHuge() throws IOException {
|
||||
int width = 34769;
|
||||
int height = 33769;
|
||||
|
||||
// Create a huge image without actually allocating memory...
|
||||
DataBuffer buffer = new NullDataBuffer(DataBuffer.TYPE_USHORT, width * height);
|
||||
WritableRaster raster = Raster.createWritableRaster(new ComponentSampleModel(buffer.getDataType(), width, height, 1, width, new int[] {0}), buffer, null);
|
||||
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, buffer.getDataType());
|
||||
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||
|
||||
// Write image without any exception
|
||||
TIFFImageWriter writer = createWriter();
|
||||
try (ImageOutputStream stream = new NullImageOutputStream()) {
|
||||
writer.setOutput(stream);
|
||||
writer.write(image);
|
||||
}
|
||||
finally {
|
||||
writer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntOverflowHuge() throws IOException {
|
||||
int width = 34769;
|
||||
int height = 33769;
|
||||
|
||||
// Create a huge image without actually allocating memory...
|
||||
DataBuffer buffer = new NullDataBuffer(DataBuffer.TYPE_INT, width * height);
|
||||
WritableRaster raster = Raster.createWritableRaster(new ComponentSampleModel(buffer.getDataType(), width, height, 1, width, new int[] {0}), buffer, null);
|
||||
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, buffer.getDataType());
|
||||
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||
|
||||
// Write image without any exception
|
||||
TIFFImageWriter writer = createWriter();
|
||||
try (ImageOutputStream stream = new NullImageOutputStream()) {
|
||||
writer.setOutput(stream);
|
||||
writer.write(image);
|
||||
}
|
||||
finally {
|
||||
writer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImageInfo {
|
||||
final int width;
|
||||
final int height;
|
||||
@ -1278,4 +1338,35 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter
|
||||
this.compression = compression;
|
||||
}
|
||||
}
|
||||
|
||||
// Special purpose output stream that acts as a sink
|
||||
private static class NullImageOutputStream extends ImageOutputStreamImpl {
|
||||
@Override public void write(int b) {
|
||||
}
|
||||
|
||||
@Override public void write(byte[] b, int off, int len) {
|
||||
}
|
||||
|
||||
@Override public int read() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int read(byte[] b, int off, int len) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Special purpose data buffer that does not require memory, to allow very large images
|
||||
private static class NullDataBuffer extends DataBuffer {
|
||||
public NullDataBuffer(int type, int size) {
|
||||
super(type, size);
|
||||
}
|
||||
|
||||
@Override public int getElem(int bank, int i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void setElem(int bank, int i, int val) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user