You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by da...@apache.org on 2012/12/12 06:41:38 UTC
svn commit: r1420531 - in /commons/proper/imaging/trunk/src: changes/
main/java/org/apache/commons/imaging/common/
main/java/org/apache/commons/imaging/formats/tiff/
main/java/org/apache/commons/imaging/formats/tiff/constants/
main/java/org/apache/comm...
Author: damjan
Date: Wed Dec 12 05:41:35 2012
New Revision: 1420531
URL: http://svn.apache.org/viewvc?rev=1420531&view=rev
Log:
Add ability to load partial TIFF images
Jira issue key: IMAGING-94
Submitted by: Gary Lucas <gwlucas at sonalysts dot com>
Modified:
commons/proper/imaging/trunk/src/changes/changes.xml
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/common/ImageBuilder.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageData.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/constants/TiffConstants.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderTiled.java
Modified: commons/proper/imaging/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/changes/changes.xml?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/changes/changes.xml (original)
+++ commons/proper/imaging/trunk/src/changes/changes.xml Wed Dec 12 05:41:35 2012
@@ -248,6 +248,9 @@ The <action> type attribute can be add,u
<action issue="IMAGING-99" dev="damjan" type="fix" due-to="st.h">
java.io.IOException: Could not read block
</action>
+ <action issue="IMAGING-94" dev="damjan" type="add" due-to="gwlucas">
+ Add ability to load partial TIFF images
+ </action>
</release>
</body>
Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/common/ImageBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/common/ImageBuilder.java?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/common/ImageBuilder.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/common/ImageBuilder.java Wed Dec 12 05:41:35 2012
@@ -14,61 +14,195 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+
+/**
+ * Development notes:
+ * This class was introduced to the Apache Commons Imaging library in
+ * order to improve performance in building images. The setRGB method
+ * provided by this class represents a substantial improvement in speed
+ * compared to that of the BufferedImage class that was originally used
+ * in Apache Sanselan.
+ * This increase is attained because ImageBuilder is a highly specialized
+ * class that does not need to perform the general-purpose logic required
+ * for BufferedImage. If you need to modify this class to add new
+ * image formats or functionality, keep in mind that some of its methods
+ * are invoked literally millions of times when building an image.
+ * Since even the introduction of something as small as a single conditional
+ * inside of setRGB could result in a noticeable increase in the
+ * time to read a file, changes should be made with care.
+ * During development, I experimented with inlining the setRGB logic
+ * in some of the code that uses it. This approach did not significantly
+ * improve performance, leading me to speculate that the Java JIT compiler
+ * might have inlined the method at run time. Further investigation
+ * is required.
+ *
+ */
package org.apache.commons.imaging.common;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
+import java.awt.image.RasterFormatException;
import java.awt.image.WritableRaster;
import java.util.Properties;
+/**
+ * A utility class primary intended for storing data obtained by reading
+ * image files.
+ */
public class ImageBuilder {
private final int[] data;
private final int width;
private final int height;
private final boolean hasAlpha;
+ /**
+ * Construct an ImageBuilder instance
+ * @param width the width of the image to be built
+ * @param height the height of the image to be built
+ * @param hasAlpha indicates whether the image has an alpha channel
+ * (the selection of alpha channel does not change the memory
+ * requirements for the ImageBuilder or resulting BufferedImage.
+ */
public ImageBuilder(final int width, final int height, final boolean hasAlpha) {
+ if(width<=0){
+ throw new RasterFormatException("zero or negative width value");
+ }
+ if(height<=0){
+ throw new RasterFormatException("zero or negative height value");
+ }
+
data = new int[width * height];
this.width = width;
this.height = height;
this.hasAlpha = hasAlpha;
}
+ /**
+ * Get the width of the ImageBuilder pixel field
+ * @return a positive integer
+ */
public int getWidth() {
return width;
}
+ /**
+ * Get the height of the ImageBuilder pixel field
+ * @return a positive integer
+ */
public int getHeight() {
return height;
}
+ /**
+ * Get the RGB or ARGB value for the pixel at the position (x,y)
+ * within the image builder pixel field. For performance reasons
+ * no bounds checking is applied.
+ * @param x the X coordinate of the pixel to be read
+ * @param y the Y coordinate of the pixel to be read
+ * @return
+ */
public int getRGB(final int x, final int y) {
final int rowOffset = y * width;
return data[rowOffset + x];
}
+ /**
+ * Set the RGB or ARGB value for the pixel at position (x,y)
+ * within the image builder pixel field. For performance reasons,
+ * no bounds checking is applied.
+ * @param x the X coordinate of the pixel to be set
+ * @param y the Y coordinate of the pixel to be set
+ * @param argb the RGB or ARGB value to be stored.
+ */
public void setRGB(final int x, final int y, final int argb) {
final int rowOffset = y * width;
data[rowOffset + x] = argb;
}
+ /**
+ * Create a BufferedImage using the data stored in the ImageBuilder.
+ * @return a valid BufferedImage.
+ */
public BufferedImage getBufferedImage() {
+ return makeBufferedImage(data, width, height, hasAlpha);
+ }
+
+ /**
+ * Gets a subimage from the ImageBuilder using the specified parameters.
+ * If the parameters specify a rectangular region that is not entirely
+ * contained within the bounds defined by the ImageBuilder, this method will
+ * throw a RasterFormatException. This runtime-exception behavior
+ * is consistent with the behavior of the getSubimage method
+ * provided by BufferdImage.
+ * @param x the X coordinate of the upper-left corner of the
+ * specified rectangular region
+ * @param y the Y coordinate of the upper-left corner of the
+ * specified rectangular region
+ * @param w the width of the specified rectangular region
+ * @param h the height of the specified rectangular region
+ * @return a BufferedImage that constructed from the deta within the
+ * specified rectangular region
+ * @throws RasterFormatException f the specified area is not contained
+ * within this ImageBuilder
+ */
+ public BufferedImage getSubimage(int x, int y, int w, int h)
+ {
+ if (w <= 0) {
+ throw new RasterFormatException("negative or zero subimage width");
+ }
+ if (h <= 0) {
+ throw new RasterFormatException("negative or zero subimage height");
+ }
+ if (x < 0 || x >= width) {
+ throw new RasterFormatException("subimage x is outside raster");
+ }
+ if (x + w > width) {
+ throw new RasterFormatException(
+ "subimage (x+width) is outside raster");
+ }
+ if (y < 0 || y >= height) {
+ throw new RasterFormatException("subimage y is outside raster");
+ }
+ if (y + h > height) {
+ throw new RasterFormatException(
+ "subimage (y+height) is outside raster");
+ }
+
+
+ // Transcribe the data to an output image array
+ int[] argb = new int[w * h];
+ int k = 0;
+ for (int iRow = 0; iRow < h; iRow++) {
+ int dIndex = (iRow + y) * width + x;
+ System.arraycopy(this.data, dIndex, argb, k, w);
+ k += w;
+
+ }
+
+ return makeBufferedImage(argb, w, h, hasAlpha);
+
+ }
+
+ private BufferedImage makeBufferedImage(
+ int[] argb, int w, int h, boolean useAlpha)
+ {
ColorModel colorModel;
WritableRaster raster;
- final DataBufferInt buffer = new DataBufferInt(data, width * height);
- if (hasAlpha) {
+ final DataBufferInt buffer = new DataBufferInt(argb, w * h);
+ if (useAlpha) {
colorModel = new DirectColorModel(32, 0x00ff0000, 0x0000ff00,
0x000000ff, 0xff000000);
- raster = WritableRaster.createPackedRaster(buffer, width, height,
- width, new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff,
+ raster = WritableRaster.createPackedRaster(buffer, w, h,
+ w, new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff,
0xff000000 }, null);
} else {
colorModel = new DirectColorModel(24, 0x00ff0000, 0x0000ff00,
0x000000ff);
- raster = WritableRaster.createPackedRaster(buffer, width, height,
- width, new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff },
+ raster = WritableRaster.createPackedRaster(buffer, w, h,
+ w, new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff },
null);
}
return new BufferedImage(colorModel, raster,
Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageData.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageData.java?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageData.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageData.java Wed Dec 12 05:41:35 2012
@@ -62,6 +62,26 @@ public abstract class TiffImageData {
predictor, samplesPerPixel, width, height, compression,
byteOrder, this);
}
+
+ /**
+ * Get the width of individual tiles. Note that if the overall
+ * image width is not a multiple of the tile width, then
+ * the last column of tiles may extend beyond the image width.
+ * @return an integer value greater than zero
+ */
+ public int getTileWidth(){
+ return tileWidth;
+ }
+
+ /**
+ * Get the height of individual tiles. Note that if the overall
+ * image height is not a multiple of the tile height, then
+ * the last row of tiles may extend beyond the image height.
+ * @return an integer value greater than zero
+ */
+ public int getTileHeight(){
+ return tileLength;
+ }
// public TiffElement[] getElements()
// {
Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java Wed Dec 12 05:41:35 2012
@@ -17,6 +17,7 @@
package org.apache.commons.imaging.formats.tiff;
import java.awt.Dimension;
+import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
@@ -435,6 +436,40 @@ public class TiffImageParser extends Ima
return result;
}
+ /**
+ * Gets a buffered image specified by the byte source.
+ * The TiffImageParser class features support for a number of options that
+ * are unique to the TIFF format. These options can be specified by
+ * supplying the appropriate parameters using the keys from the
+ * TiffConstants class and the params argument for this method.
+ * <h4>Loading Partial Images</h4>
+ * The TIFF parser includes support for loading partial images without
+ * committing significantly more memory resources than are necessary
+ * to store the image. This feature is useful for conserving memory
+ * in applications that require a relatively small sub image from a
+ * very large TIFF file. The specifications for partial images are
+ * as follows:
+ * <code><pre>
+ * HashMap<String, Object> params = new HashMap<String, Object>();
+ * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_X, new Integer(x));
+ * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_Y, new Integer(y));
+ * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_WIDTH, new Integer(width));
+ * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_HEIGHT, new Integer(height));
+ * </pre></code>
+ * Note that the arguments x, y, width, and height must specify a
+ * valid rectangular region that is fully contained within the
+ * source TIFF image.
+ * @param byteSource A valid instance of ByteSource
+ * @param params Optional instructions for special-handling or
+ * interpretation of the input data (null objects are permitted and
+ * must be supported by implementations).
+ * @return A valid instance of BufferedImage.
+ * @throws ImageReadException In the event that the the specified
+ * content does not conform to the format of the specific parser
+ * implementation.
+ * @throws IOException In the event of unsuccessful read or
+ * access operation.
+ */
@Override
public BufferedImage getBufferedImage(final ByteSource byteSource, final Map<String,Object> params)
throws ImageReadException, IOException {
@@ -470,8 +505,67 @@ public class TiffImageParser extends Ima
return results;
}
+ private Integer getIntegerParameter(
+ String key, Map<String, Object>params)
+ throws ImageReadException
+ {
+
+ if(!params.containsKey(key))
+ return null;
+
+ Object obj = params.get(key);
+
+ if(obj instanceof Integer){
+ return (Integer)obj;
+ }
+ throw new ImageReadException(
+ "Non-Integer parameter "+key);
+ }
+
+ private Rectangle checkForSubImage(
+ Map<String, Object> params)
+ throws ImageReadException
+ {
+ Integer ix0, iy0, iwidth, iheight;
+ ix0 = getIntegerParameter(
+ TiffConstants.PARAM_KEY_SUBIMAGE_X, params);
+ iy0 = getIntegerParameter(
+ TiffConstants.PARAM_KEY_SUBIMAGE_Y, params);
+ iwidth = getIntegerParameter(
+ TiffConstants.PARAM_KEY_SUBIMAGE_WIDTH, params);
+ iheight = getIntegerParameter(
+ TiffConstants.PARAM_KEY_SUBIMAGE_HEIGHT, params);
+ if (ix0 == null && iy0 == null && iwidth == null && iheight == null)
+ return null;
+
+ StringBuilder sb = new StringBuilder();
+ if (ix0 == null)
+ sb.append(" x0,");
+ if (iy0 == null)
+ sb.append(" y0,");
+ if (iwidth == null)
+ sb.append(" width,");
+ if (iheight == null)
+ sb.append(" height,");
+ if (sb.length() > 0) {
+ sb.setLength(sb.length() - 1);
+ throw new ImageReadException(
+ "Incomplete subimage parameters, missing"
+ + sb.toString());
+ }
+
+ int x0 = ix0.intValue();
+ int y0 = iy0.intValue();
+ int width = iwidth.intValue();
+ int height = iheight.intValue();
+
+ return new Rectangle(x0, y0, width, height);
+ }
+
protected BufferedImage getBufferedImage(final TiffDirectory directory,
- final ByteOrder byteOrder, final Map<String,Object> params) throws ImageReadException, IOException {
+ final ByteOrder byteOrder, final Map<String,Object> params)
+ throws ImageReadException, IOException
+ {
final List<TiffField> entries = directory.entries;
if (entries == null) {
@@ -485,7 +579,43 @@ public class TiffImageParser extends Ima
final int width = directory.findField(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH,
true).getIntValue();
final int height = directory.findField(
- TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true).getIntValue();
+ TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true).getIntValue();
+ Rectangle subImage = checkForSubImage(params);
+ if(subImage!=null){
+ // Check for valid subimage specification. The following checks
+ // are consistent with BufferedImage.getSubimage()
+ if (subImage.width <= 0) {
+ throw new ImageReadException("negative or zero subimage width");
+ }
+ if (subImage.height <= 0) {
+ throw new ImageReadException("negative or zero subimage height");
+ }
+ if(subImage.x<0 || subImage.x>=width){
+ throw new ImageReadException("subimage x is outside raster");
+ }
+ if(subImage.x+subImage.width>width){
+ throw new ImageReadException(
+ "subimage (x+width) is outside raster");
+ }
+ if(subImage.y<0 || subImage.y>=height){
+ throw new ImageReadException("subimage y is outside raster");
+ }
+ if(subImage.y+subImage.height>height){
+ throw new ImageReadException(
+ "subimage (y+height) is outside raster");
+ }
+
+ // if the subimage is just the same thing as the whole
+ // image, suppress the subimage processing
+ if(subImage.x==0
+ && subImage.y==0
+ && subImage.width==width
+ && subImage.height==height){
+ subImage = null;
+ }
+ }
+
+
int samplesPerPixel = 1;
final TiffField samplesPerPixelField = directory
.findField(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL);
@@ -524,8 +654,7 @@ public class TiffImageParser extends Ima
+ bitsPerSample.length + ")");
}
- final boolean hasAlpha = false;
- final ImageBuilder imageBuilder = new ImageBuilder(width, height, hasAlpha);
+
final PhotometricInterpreter photometricInterpreter = getPhotometricInterpreter(
directory, photometricInterpretation, bitsPerPixel,
@@ -537,11 +666,18 @@ public class TiffImageParser extends Ima
photometricInterpreter, bitsPerPixel, bitsPerSample, predictor,
samplesPerPixel, width, height, compression, byteOrder);
- dataReader.readImageData(imageBuilder);
+ BufferedImage result = null;
+ if (subImage != null) {
+ result = dataReader.readImageData(subImage);
+ } else {
+ boolean hasAlpha = false;
+ ImageBuilder imageBuilder = new ImageBuilder(width, height, hasAlpha);
+ dataReader.readImageData(imageBuilder);
+ result = imageBuilder.getBufferedImage();
+ }
photometricInterpreter.dumpstats();
-
- return imageBuilder.getBufferedImage();
+ return result;
}
private PhotometricInterpreter getPhotometricInterpreter(
Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/constants/TiffConstants.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/constants/TiffConstants.java?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/constants/TiffConstants.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/constants/TiffConstants.java Wed Dec 12 05:41:35 2012
@@ -66,4 +66,10 @@ public interface TiffConstants
public static final int TIFF_FLAG_T4_OPTIONS_UNCOMPRESSED_MODE = 2;
public static final int TIFF_FLAG_T4_OPTIONS_FILL = 4;
public static final int TIFF_FLAG_T6_OPTIONS_UNCOMPRESSED_MODE = 2;
+
+
+ public static final String PARAM_KEY_SUBIMAGE_X = "SUBIMAGE_X";
+ public static final String PARAM_KEY_SUBIMAGE_Y = "SUBIMAGE_Y";
+ public static final String PARAM_KEY_SUBIMAGE_WIDTH = "SUBIMAGE_WIDTH";
+ public static final String PARAM_KEY_SUBIMAGE_HEIGHT = "SUBIMAGE_HEIGHT";
}
Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java Wed Dec 12 05:41:35 2012
@@ -16,6 +16,8 @@
*/
package org.apache.commons.imaging.formats.tiff.datareaders;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -61,6 +63,12 @@ public abstract class DataReader impleme
public abstract void readImageData(ImageBuilder imageBuilder)
throws ImageReadException, IOException;
+
+ public abstract BufferedImage readImageData(Rectangle subImage)
+ throws ImageReadException, IOException;
+
+
+
/**
* Reads samples and returns them in an int array.
*
@@ -200,5 +208,4 @@ public abstract class DataReader impleme
"Tiff: unknown/unsupported compression: " + compression);
}
}
-
}
Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java Wed Dec 12 05:41:35 2012
@@ -16,6 +16,8 @@
*/
package org.apache.commons.imaging.formats.tiff.datareaders;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -52,9 +54,12 @@ public final class DataReaderStrips exte
this.byteOrder = byteOrder;
}
- private void interpretStrip(final ImageBuilder imageBuilder, final byte bytes[],
- final int pixels_per_strip) throws ImageReadException, IOException {
- if (y >= height) {
+ private void interpretStrip(
+ final ImageBuilder imageBuilder,
+ final byte bytes[],
+ final int pixels_per_strip,
+ final int yLimit) throws ImageReadException, IOException {
+ if (y >= yLimit) {
return;
}
@@ -111,8 +116,8 @@ public final class DataReaderStrips exte
if (predictor != 2 && bitsPerPixel == 8 && allSamplesAreOneByte) {
int k = 0;
int nRows = pixels_per_strip / width;
- if (y + nRows > height) {
- nRows = height - y;
+ if (y + nRows > yLimit) {
+ nRows = yLimit - y;
}
final int i0 = y;
final int i1 = y + nRows;
@@ -130,8 +135,8 @@ public final class DataReaderStrips exte
} else if (predictor != 2 && bitsPerPixel == 24 && allSamplesAreOneByte) {
int k = 0;
int nRows = pixels_per_strip / width;
- if (y + nRows > height) {
- nRows = height - y;
+ if (y + nRows > yLimit) {
+ nRows = yLimit - y;
}
final int i0 = y;
final int i1 = y + nRows;
@@ -178,8 +183,8 @@ public final class DataReaderStrips exte
if (x < width) {
samples = applyPredictor(samples);
- photometricInterpreter.interpretPixel(imageBuilder, samples, x,
- y);
+ photometricInterpreter.interpretPixel(
+ imageBuilder, samples, x, y);
}
x++;
@@ -188,7 +193,7 @@ public final class DataReaderStrips exte
resetPredictor();
y++;
bis.flushCache();
- if (y >= height) {
+ if (y >= yLimit) {
break;
}
}
@@ -213,9 +218,82 @@ public final class DataReaderStrips exte
final byte decompressed[] = decompress(compressed, compression,
(int) bytesPerStrip, width, (int) rowsInThisStrip);
- interpretStrip(imageBuilder, decompressed, (int) pixelsPerStrip);
+ interpretStrip(
+ imageBuilder,
+ decompressed,
+ (int) pixelsPerStrip,
+ height);
}
}
+
+
+ @Override
+ public BufferedImage readImageData(Rectangle subImage)
+ throws ImageReadException, IOException
+ {
+ // the legacy code is optimized to the reading of whole
+ // strips (except for the last strip in the image, which can
+ // be a partial). So create a working image with compatible
+ // dimensions and read that. Later on, the working image
+ // will be sub-imaged to the proper size.
+
+ // strip0 and strip1 give the indices of the strips containing
+ // the first and last rows of pixels in the subimage
+ int strip0 = subImage.y / rowsPerStrip;
+ int strip1 = (subImage.y + subImage.height - 1) / rowsPerStrip;
+ int workingHeight = (strip1 - strip0 + 1) * rowsPerStrip;
+
+
+ // the legacy code uses a member element "y" to keep track
+ // of the row index of the output image that is being processed
+ // by interpretStrip. y is set to zero before the first
+ // call to interpretStrip. y0 will be the index of the first row
+ // in the full image (the source image) that will be processed.
+
+ int y0 = strip0 * rowsPerStrip;
+ int yLimit = subImage.y-y0+subImage.height;
+
+
+ // TO DO: we can probably save some processing by using yLimit instead
+ // or working
+ ImageBuilder workingBuilder =
+ new ImageBuilder(width, workingHeight, false);
+
+ for (int strip = strip0; strip <= strip1; strip++) {
+ long rowsPerStripLong = 0xFFFFffffL & rowsPerStrip;
+ long rowsRemaining = height - (strip * rowsPerStripLong);
+ long rowsInThisStrip = Math.min(rowsRemaining, rowsPerStripLong);
+ long bytesPerRow = (bitsPerPixel * width + 7) / 8;
+ long bytesPerStrip = rowsInThisStrip * bytesPerRow;
+ long pixelsPerStrip = rowsInThisStrip * width;
+
+ byte compressed[] = imageData.strips[strip].getData();
+
+ byte decompressed[] = decompress(compressed, compression,
+ (int) bytesPerStrip, width, (int) rowsInThisStrip);
+
+ interpretStrip(
+ workingBuilder,
+ decompressed,
+ (int) pixelsPerStrip,
+ yLimit);
+ }
+
+
+ if (subImage.x == 0
+ && subImage.y == y0
+ && subImage.width == width
+ && subImage.height == workingHeight) {
+ // the subimage exactly matches the ImageBuilder bounds
+ return workingBuilder.getBufferedImage();
+ } else {
+ return workingBuilder.getSubimage(
+ subImage.x,
+ subImage.y - y0,
+ subImage.width,
+ subImage.height);
+ }
+ }
}
Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderTiled.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderTiled.java?rev=1420531&r1=1420530&r2=1420531&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderTiled.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderTiled.java Wed Dec 12 05:41:35 2012
@@ -16,6 +16,8 @@
*/
package org.apache.commons.imaging.formats.tiff.datareaders;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -60,7 +62,7 @@ public final class DataReaderTiled exten
}
private void interpretTile(final ImageBuilder imageBuilder, final byte bytes[],
- final int startX, final int startY) throws ImageReadException, IOException {
+ final int startX, final int startY, final int xLimit, final int yLimit) throws ImageReadException, IOException {
// changes introduced May 2012
// The following block of code implements changes that
// reduce image loading time by using special-case processing
@@ -82,15 +84,15 @@ public final class DataReaderTiled exten
int k = 0;
final int i0 = startY;
int i1 = startY + tileLength;
- if (i1 > height) {
+ if (i1 > yLimit) {
// the tile is padded past bottom of image
- i1 = height;
+ i1 = yLimit;
}
final int j0 = startX;
int j1 = startX + tileWidth;
- if (j1 > width) {
+ if (j1 > xLimit) {
// the tile is padded to beyond the tile width
- j1 = width;
+ j1 = xLimit;
}
if (photometricInterpreter instanceof PhotometricInterpreterRgb) {
for (int i = i0; i < i1; i++) {
@@ -136,7 +138,7 @@ public final class DataReaderTiled exten
getSamplesAsBytes(bis, samples);
- if ((x < width) && (y < height)) {
+ if ((x < xLimit) && (y < yLimit)) {
samples = applyPredictor(samples);
photometricInterpreter.interpretPixel(imageBuilder, samples, x,
y);
@@ -171,7 +173,7 @@ public final class DataReaderTiled exten
final byte decompressed[] = decompress(compressed, compression,
bytesPerTile, tileWidth, tileLength);
- interpretTile(imageBuilder, decompressed, x, y);
+ interpretTile(imageBuilder, decompressed, x, y, width, height);
x += tileWidth;
if (x >= width) {
@@ -184,4 +186,60 @@ public final class DataReaderTiled exten
}
}
+
+ @Override
+ public BufferedImage readImageData(final Rectangle subImage)
+ throws ImageReadException, IOException
+ {
+ int bitsPerRow = tileWidth * bitsPerPixel;
+ int bytesPerRow = (bitsPerRow + 7) / 8;
+ int bytesPerTile = bytesPerRow * tileLength;
+ int x = 0, y = 0;
+
+ // tileWidth is the width of the tile
+ // tileLength is the height of the tile
+ int col0 = subImage.x / tileWidth;
+ int col1 = (subImage.x + subImage.width - 1) / tileWidth;
+ int row0 = subImage.y / tileLength;
+ int row1 = (subImage.y + subImage.height - 1) / tileLength;
+
+ int nCol = col1 - col0 + 1;
+ int nRow = row1 - row0 + 1;
+ int workingWidth = nCol * tileWidth;
+ int workingHeight = nRow * tileLength;
+
+ int nColumnsOfTiles = (width + tileWidth - 1) / tileWidth;
+
+ int x0 = col0*tileWidth;
+ int y0 = row0*tileLength;
+
+ ImageBuilder workingBuilder =
+ new ImageBuilder(workingWidth, workingHeight, false);
+
+ for (int iRow = row0; iRow <= row1; iRow++) {
+ for (int iCol = col0; iCol <= col1; iCol++) {
+ int tile = iRow * nColumnsOfTiles+iCol;
+ byte compressed[] = imageData.tiles[tile].getData();
+ byte decompressed[] = decompress(compressed, compression,
+ bytesPerTile, tileWidth, tileLength);
+ x = iCol * tileWidth - x0;
+ y = iRow * tileLength - y0;
+ interpretTile(workingBuilder, decompressed, x, y, workingWidth, workingHeight);
+ }
+ }
+
+ if (subImage.x == x0
+ && subImage.y == y0
+ && subImage.width == workingWidth
+ && subImage.height == workingHeight) {
+ return workingBuilder.getBufferedImage();
+ }else{
+ return workingBuilder.getSubimage(
+ subImage.x - x0,
+ subImage.y - y0,
+ subImage.width,
+ subImage.height);
+ }
+ }
+
}