From d5e664cdcc5bc9be0ba3f935e1b99dd3767205d8 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Sat, 30 Jan 2021 16:50:54 +0100 Subject: [PATCH] JPEG Exif/thumbnail fixes. (cherry picked from commit fbc738f2d4836e82315831774c0e722a4984e967) --- .../imageio/plugins/jpeg/JPEGImageReader.java | 98 +++++++++---------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java index 1495db08..af312269 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java @@ -30,37 +30,6 @@ package com.twelvemonkeys.imageio.plugins.jpeg; -import java.awt.*; -import java.awt.color.ColorSpace; -import java.awt.color.ICC_ColorSpace; -import java.awt.color.ICC_Profile; -import java.awt.image.*; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.SequenceInputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import javax.imageio.IIOException; -import javax.imageio.ImageIO; -import javax.imageio.ImageReadParam; -import javax.imageio.ImageReader; -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.event.IIOReadUpdateListener; -import javax.imageio.event.IIOReadWarningListener; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.metadata.IIOMetadataFormatImpl; -import javax.imageio.metadata.IIOMetadataNode; -import javax.imageio.spi.ImageReaderSpi; -import javax.imageio.stream.ImageInputStream; - import com.twelvemonkeys.imageio.ImageReaderBase; import com.twelvemonkeys.imageio.color.ColorSpaces; import com.twelvemonkeys.imageio.color.YCbCrConverter; @@ -72,7 +41,6 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment; import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil; import com.twelvemonkeys.imageio.metadata.tiff.TIFF; import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader; -import com.twelvemonkeys.imageio.stream.BufferedImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.SubImageInputStream; import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers; @@ -80,6 +48,24 @@ import com.twelvemonkeys.imageio.util.ProgressListenerBase; import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.xml.XMLSerializer; +import javax.imageio.*; +import javax.imageio.event.IIOReadUpdateListener; +import javax.imageio.event.IIOReadWarningListener; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataFormatImpl; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import java.awt.*; +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.image.*; +import java.io.*; +import java.nio.ByteOrder; +import java.util.List; +import java.util.*; + /** * A JPEG {@code ImageReader} implementation based on the JRE {@code JPEGImageReader}, * that adds support and properly handles cases where the JRE version throws exceptions. @@ -140,7 +126,7 @@ public final class JPEGImageReader extends ImageReaderBase { private List segments; private int currentStreamIndex = 0; - private List streamOffsets = new ArrayList<>(); + private final List streamOffsets = new ArrayList<>(); protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) { super(provider); @@ -197,10 +183,10 @@ public final class JPEGImageReader extends ImageReaderBase { return true; } } - catch (IIOException ignore) { + catch (IIOException e) { // May happen if no SOF is found, in case we'll just fall through if (DEBUG) { - ignore.printStackTrace(); + e.printStackTrace(); } } @@ -747,26 +733,26 @@ public final class JPEGImageReader extends ImageReaderBase { long lastKnownSOIOffset = streamOffsets.get(streamOffsets.size() - 1); imageInput.seek(lastKnownSOIOffset); - try (ImageInputStream stream = new BufferedImageInputStream(imageInput)) { // Extreme (10s -> 50ms) speedup if imageInput is FileIIS + try { for (int i = streamOffsets.size() - 1; i < imageIndex; i++) { long start = 0; if (DEBUG) { start = System.currentTimeMillis(); - System.out.println(String.format("Start seeking for image index %d", i + 1)); + System.out.printf("Start seeking for image index %d%n", i + 1); } // Need to skip over segments, as they may contain JPEG markers (eg. JFXX or EXIF thumbnail) - JPEGSegmentUtil.readSegments(stream, Collections.>emptyMap()); + JPEGSegmentUtil.readSegments(imageInput, Collections.>emptyMap()); // Now, search for EOI and following SOI... int marker; - while ((marker = stream.read()) != -1) { - if (marker == 0xFF && (0xFF00 | stream.readUnsignedByte()) == JPEG.EOI) { + while ((marker = imageInput.read()) != -1) { + if (marker == 0xFF && (0xFF00 | imageInput.readUnsignedByte()) == JPEG.EOI) { // Found EOI, now the SOI should be nearby... - while ((marker = stream.read()) != -1) { - if (marker == 0xFF && (0xFF00 | stream.readUnsignedByte()) == JPEG.SOI) { - long nextSOIOffset = stream.getStreamPosition() - 2; + while ((marker = imageInput.read()) != -1) { + if (marker == 0xFF && (0xFF00 | imageInput.readUnsignedByte()) == JPEG.SOI) { + long nextSOIOffset = imageInput.getStreamPosition() - 2; imageInput.seek(nextSOIOffset); streamOffsets.add(nextSOIOffset); @@ -780,10 +766,9 @@ public final class JPEGImageReader extends ImageReaderBase { } if (DEBUG) { - System.out.println(String.format("Seek in %d ms", System.currentTimeMillis() - start)); + System.out.printf("Seek in %d ms%n", System.currentTimeMillis() - start); } } - } catch (EOFException eof) { IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException("Image index " + imageIndex + " not found in stream"); @@ -843,9 +828,9 @@ public final class JPEGImageReader extends ImageReaderBase { return JPEGSegmentUtil.readSegments(imageInput, JPEGSegmentUtil.ALL_SEGMENTS); } - catch (IIOException | IllegalArgumentException ignore) { + catch (IIOException | IllegalArgumentException e) { if (DEBUG) { - ignore.printStackTrace(); + e.printStackTrace(); } } finally { @@ -1162,6 +1147,7 @@ public final class JPEGImageReader extends ImageReaderBase { if (jpegLength > 0 && jpegOffset + jpegLength <= stream.length()) { // Verify first bytes are FFD8 stream.seek(jpegOffset); + stream.setByteOrder(ByteOrder.BIG_ENDIAN); if (stream.readUnsignedShort() == JPEG.SOI) { thumbnails.add(new EXIFThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), 0, thumbnails.size(), ifd1, stream)); } @@ -1189,7 +1175,9 @@ public final class JPEGImageReader extends ImageReaderBase { // EXIFThumbnailReader? thumbnails.add(new EXIFThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), 0, thumbnails.size(), ifd1, stream)); } - + else { + processWarningOccurred("EXIF IFD with empty or incomplete uncompressed thumbnail"); + } } else { processWarningOccurred("EXIF IFD with uncompressed thumbnail missing StripOffsets tag"); @@ -1246,7 +1234,15 @@ public final class JPEGImageReader extends ImageReaderBase { public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException { checkThumbnailBounds(imageIndex, thumbnailIndex); - return thumbnails.get(thumbnailIndex).read(); +// processThumbnailStarted(imageIndex, thumbnailIndex); +// processThumbnailProgress(0f); + + BufferedImage thumbnail = thumbnails.get(thumbnailIndex).read();; + +// processThumbnailProgress(100f); +// processThumbnailComplete(); + + return thumbnail; } // Metadata @@ -1423,7 +1419,7 @@ public final class JPEGImageReader extends ImageReaderBase { final String arg = args[argIdx]; if (arg.charAt(0) == '-') { - if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx) { + if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx + 1) { String[] sub = args[++argIdx].split(","); try { @@ -1442,7 +1438,7 @@ public final class JPEGImageReader extends ImageReaderBase { System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'"); } } - else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx) { + else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx + 1) { String[] region = args[++argIdx].split(","); try {