# HG changeset patch # User bpb # Date 1484949945 28800 # Fri Jan 20 14:05:45 2017 -0800 # Node ID 54d59cff50dd36e0dcb4dd5ff8516abfdd13bc94 # Parent d4150b065b15c00493dfa7fb1de0fea7387a136c [mq]: 8154228-no-byte-counts diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java @@ -414,6 +414,133 @@ } // + // Retrieve the value of a baseline field as a long. + // + private long getFieldAsLong(int tagNumber) { + TIFFField f = getTIFFField(tagNumber); + return f == null ? -1 : f.getAsLong(0); + } + + // + // Retrieve the value of a baseline field as an int. + // + private int getFieldAsInt(int tagNumber) { + TIFFField f = getTIFFField(tagNumber); + return f == null ? -1 : f.getAsInt(0); + } + + // + // Calculate the number of bytes in each strip or tile. This method + // is to be used if and only if no fields exist which provide this + // information. The parameter must be empty and if the method succeeds + // will contain a single element. + // + private boolean calculateByteCounts(int expectedSize, + List byteCounts) { + if (!byteCounts.isEmpty()) { + throw new IllegalArgumentException("byteCounts is not empty"); + } + + // must be interleaved + if (getFieldAsInt(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION) == + BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { + return false; + } + + // must be uncompressed + if (getFieldAsInt(BaselineTIFFTagSet.TAG_COMPRESSION) != + BaselineTIFFTagSet.COMPRESSION_NONE) { + return false; + } + + // must have image dimensions + long w = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_WIDTH); + if (w < 0) { + return false; + } + long h = getFieldAsLong(BaselineTIFFTagSet.TAG_IMAGE_LENGTH); + if (h < 0) { + return false; + } + + long tw = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_WIDTH); + if (tw < 0) { + tw = w; + } + long th = getFieldAsLong(BaselineTIFFTagSet.TAG_TILE_LENGTH); + if (th < 0) { + th = getFieldAsLong(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP); + if (th < 0) { + th = h; + } + } + + int[] bitsPerSample = null; + TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); + if (f != null) { + bitsPerSample = f.getAsInts(); + } else { + int samplesPerPixel = + getFieldAsInt(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); + if (samplesPerPixel < 0) { + samplesPerPixel = 1; + } + bitsPerSample = new int[samplesPerPixel]; + Arrays.fill(bitsPerSample, 8); + } + + int bitsPerPixel = 0; + for (int bps : bitsPerSample) { + bitsPerPixel += bps; + } + + int bytesPerRow = (int)(tw*bitsPerPixel + 7)/8; + int bytesPerPacket = (int)th*bytesPerRow; + + long nx = (w + tw - 1)/tw; + long ny = (h + th - 1)/th; + + if (nx*ny != expectedSize) { + return false; + } + + boolean isTiled = + getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS) != null; + + int tagNumber; + if (isTiled) { + tagNumber = BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS; + } else { + tagNumber = BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS; + } + + TIFFTag t = BaselineTIFFTagSet.getInstance().getTag(tagNumber); + f = getTIFFField(tagNumber); + if (f != null) { + removeTIFFField(tagNumber); + } + + int numPackets = (int)(nx*ny); + long[] packetByteCounts = new long[numPackets]; + Arrays.fill(packetByteCounts, bytesPerPacket); + + // if the strip or tile width does not exceed the image width and the + // image height is not a multiple of the strip or tile height, then + // truncate the estimate of the byte count of the last strip to avoid + // reading past the end of the data + if (tw <= w && h % th != 0) { + int numRowsInLastStrip = (int)(h - (ny - 1)*th); + packetByteCounts[numPackets - 1] = numRowsInLastStrip*bytesPerRow; + } + + f = new TIFFField(t, TIFFTag.TIFF_LONG, numPackets, packetByteCounts); + addTIFFField(f); + byteCounts.add(f); + + return true; + } + + // // Verify that data pointed to outside of the IFD itself are within the // stream. To be called after all fields have been read and populated. // @@ -502,8 +629,19 @@ // Ensure there is at least a data pointer for JPEG interchange format or // both data offsets and byte counts for other compression types. - if (jpegOffset == null && (offsets.size() == 0 || byteCounts.size() == 0)) { - throw new IIOException("Insufficient data offsets or byte counts"); + if (jpegOffset == null + && (offsets.size() == 0 || byteCounts.size() == 0)) { + boolean throwException = true; + if (offsets.size() != 0 && byteCounts.size() == 0) { + // Attempt to calculate missing byte counts + int expectedSize = offsets.get(0).getCount(); + throwException = + !calculateByteCounts(expectedSize, byteCounts); + } + if (throwException) { + throw new IIOException + ("Insufficient data offsets or byte counts"); + } } // JPEGQTables - one 64-byte table for each offset.