JPEG Exif/thumbnail fixes.

(cherry picked from commit fbc738f2d4836e82315831774c0e722a4984e967)
This commit is contained in:
Harald Kuhr 2021-01-30 16:50:54 +01:00
parent efe5f3c34a
commit d5e664cdcc

View File

@ -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<Segment> segments;
private int currentStreamIndex = 0;
private List<Long> streamOffsets = new ArrayList<>();
private final List<Long> 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.<Integer, List<String>>emptyMap());
JPEGSegmentUtil.readSegments(imageInput, Collections.<Integer, List<String>>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 {