From 724cf663033de560fac005719421250f60bc5fac Mon Sep 17 00:00:00 2001 From: Bram Bouwens Date: Thu, 28 Jan 2021 13:24:02 +0100 Subject: [PATCH] Issue #72: Very slow writing of simple 16/32-bit gray-scale TIFF images Send the DataBuffer to the TIFFCompressor, as that knows the stream and therefore its byte order, so it can serialize it and then send it to the original encode(..) method. --- .../impl/plugins/tiff/TIFFImageWriter.java | 9 +- .../plugins/tiff/TIFFCompressor.java | 83 ++++++++++++++++++- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/github/jaiimageio/impl/plugins/tiff/TIFFImageWriter.java b/src/main/java/com/github/jaiimageio/impl/plugins/tiff/TIFFImageWriter.java index 51c9e32..e6be1dd 100644 --- a/src/main/java/com/github/jaiimageio/impl/plugins/tiff/TIFFImageWriter.java +++ b/src/main/java/com/github/jaiimageio/impl/plugins/tiff/TIFFImageWriter.java @@ -1780,14 +1780,11 @@ private int writeTile(Rectangle tileRect, TIFFCompressor compressor) return compressor.encode(buf, 0, width, height, sampleSize, (tileRect.width + 7)/8); - } else if(bitDepth == 8 && - sm.getDataType() == DataBuffer.TYPE_BYTE) { + } else if(bitDepth == DataBuffer.getDataTypeSize(sm.getDataType())) { + ComponentSampleModel csm = (ComponentSampleModel)raster.getSampleModel(); - byte[] buf = - ((DataBufferByte)raster.getDataBuffer()).getData(); - int off = csm.getOffset(minX - raster.getSampleModelTranslateX(), @@ -1798,7 +1795,7 @@ private int writeTile(Rectangle tileRect, TIFFCompressor compressor) System.out.println("Optimized component case"); } - return compressor.encode(buf, off, + return compressor.encode(raster.getDataBuffer(), off, width, height, sampleSize, csm.getScanlineStride()); } diff --git a/src/main/java/com/github/jaiimageio/plugins/tiff/TIFFCompressor.java b/src/main/java/com/github/jaiimageio/plugins/tiff/TIFFCompressor.java index 630900f..6e7e695 100644 --- a/src/main/java/com/github/jaiimageio/plugins/tiff/TIFFCompressor.java +++ b/src/main/java/com/github/jaiimageio/plugins/tiff/TIFFCompressor.java @@ -49,6 +49,11 @@ import javax.imageio.ImageWriter; import javax.imageio.metadata.IIOMetadata; import javax.imageio.stream.ImageOutputStream; +import java.awt.image.*; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; import com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter; @@ -278,7 +283,83 @@ public abstract int encode(byte[] b, int off, int width, int height, int[] bitsPerSample, int scanlineStride) throws IOException; - + + /** + * Encodes the supplied image data, writing to the currently set + * ImageOutputStream. + * + * @param dataBuffer an DataBuffer with pixels + * @param off the starting offset of the data to be written in the + * array b. + * @param width the width of the rectangle of pixels to be written. + * @param height the height of the rectangle of pixels to be written. + * @param bitsPerSample an array of ints indicting + * the number of bits used to represent each image sample within + * a pixel. + * @param scanlineStride the number of bytes separating each + * row of the input data. + * + * @return the number of bytes written. + * + * @throws IOException if the supplied data cannot be encoded by + * this TIFFCompressor, or if any I/O error occurs + * during writing. + */ + private byte[] currentTile = null; + public int encode(DataBuffer dataBuffer, int off, + int width, int height, + int[] bitsPerSample, + int scanlineStride) throws IOException { + final int dataType = dataBuffer.getDataType(); + if (dataType == DataBuffer.TYPE_BYTE) { + return encode(((DataBufferByte) dataBuffer).getData(), off, width, height, bitsPerSample, scanlineStride); + } + final int dataTypeSize = DataBuffer.getDataTypeSize(dataType) / Byte.SIZE; + int tileSize = width * height * dataTypeSize; + if (currentTile == null || currentTile.length < tileSize) + currentTile = new byte[tileSize]; + final ByteBuffer buffer = ByteBuffer.wrap(currentTile).order(stream.getByteOrder()); + switch (dataType) { + case DataBuffer.TYPE_USHORT: { + ShortBuffer sb = buffer.asShortBuffer(); + short[] shorts = ((DataBufferUShort) dataBuffer).getData(); + for (int i = 0; i < height; i++) { + sb.put(shorts, off, width); + off += scanlineStride; + } + break; + } + case DataBuffer.TYPE_SHORT: { + ShortBuffer sb = buffer.asShortBuffer(); + short[] shorts = ((DataBufferShort) dataBuffer).getData(); + for (int i = 0; i < height; i++) { + sb.put(shorts, off, width); + off += scanlineStride; + } + break; + } + case DataBuffer.TYPE_INT: { + IntBuffer ib = buffer.asIntBuffer(); + int[] ints = ((DataBufferInt) dataBuffer).getData(); + for (int i = 0; i < height; i++) { + ib.put(ints, off, width); + off += scanlineStride; + } + break; + } + case DataBuffer.TYPE_FLOAT: { + FloatBuffer fb = buffer.asFloatBuffer(); + float[] floats = ((DataBufferFloat) dataBuffer).getData(); + for (int i = 0; i < height; i++) { + fb.put(floats, off, width); + off += scanlineStride; + } + break; + } + } + return encode(currentTile, 0, width, height, bitsPerSample, width * dataTypeSize); + } + /** * Allows any resources held by this object to be released. The * result of calling any other method (other than