mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-04-29 00:00:01 -04:00
JPEG Exif/thumbnail refactoring pt II.
This commit is contained in:
+16
-22
@@ -57,31 +57,30 @@ final class EXIFThumbnail {
|
||||
private EXIFThumbnail() {
|
||||
}
|
||||
|
||||
static ThumbnailReader from(final EXIF exif, final CompoundDirectory exifMetadata, final ImageReader jpegThumbnailReader, final JPEGSegmentWarningListener listener) throws IOException {
|
||||
if (exif != null && exifMetadata != null && exifMetadata.directoryCount() == 2) {
|
||||
ImageInputStream stream = exif.exifData(); // NOTE This is an in-memory stream and must not be closed...
|
||||
static ThumbnailReader from(final EXIF segment, final CompoundDirectory exif, final ImageReader jpegThumbnailReader) throws IOException {
|
||||
if (segment != null && exif != null && exif.directoryCount() >= 2) {
|
||||
ImageInputStream stream = segment.exifData(); // NOTE This is an in-memory stream and must not be closed...
|
||||
|
||||
Directory ifd1 = exifMetadata.getDirectory(1);
|
||||
Directory ifd1 = exif.getDirectory(1);
|
||||
|
||||
// Compression: 1 = no compression, 6 = JPEG compression (default)
|
||||
Entry compressionEntry = ifd1.getEntryById(TIFF.TAG_COMPRESSION);
|
||||
int compression = compressionEntry == null ? 6 : ((Number) compressionEntry.getValue()).intValue();
|
||||
|
||||
switch (compression) {
|
||||
case 6:
|
||||
return createJPEGThumbnailReader(exif, jpegThumbnailReader, listener, stream, ifd1);
|
||||
case 1:
|
||||
return createUncompressedThumbnailReader(listener, stream, ifd1);
|
||||
return createUncompressedThumbnailReader(stream, ifd1);
|
||||
case 6:
|
||||
return createJPEGThumbnailReader(segment, jpegThumbnailReader, stream, ifd1);
|
||||
default:
|
||||
listener.warningOccurred("EXIF IFD with unknown thumbnail compression (expected 1 or 6): " + compression);
|
||||
break;
|
||||
throw new IIOException("EXIF IFD with unknown thumbnail compression (expected 1 or 6): " + compression);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static UncompressedThumbnailReader createUncompressedThumbnailReader(JPEGSegmentWarningListener listener, ImageInputStream stream, Directory ifd1) throws IOException {
|
||||
private static UncompressedThumbnailReader createUncompressedThumbnailReader(ImageInputStream stream, Directory ifd1) throws IOException {
|
||||
Entry stripOffEntry = ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS);
|
||||
Entry width = ifd1.getEntryById(TIFF.TAG_IMAGE_WIDTH);
|
||||
Entry height = ifd1.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
|
||||
@@ -95,12 +94,8 @@ final class EXIFThumbnail {
|
||||
int w = ((Number) width.getValue()).intValue();
|
||||
int h = ((Number) height.getValue()).intValue();
|
||||
|
||||
// TODO: Decide on warning OR exception!
|
||||
if (bitsPerSample != null) {
|
||||
int[] bpp = (int[]) bitsPerSample.getValue();
|
||||
if (!Arrays.equals(bpp, new int[] {8, 8, 8})) {
|
||||
throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString());
|
||||
}
|
||||
if (bitsPerSample != null && !Arrays.equals((int[]) bitsPerSample.getValue(), new int[] {8, 8, 8})) {
|
||||
throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString());
|
||||
}
|
||||
|
||||
if (samplesPerPixel != null && ((Number) samplesPerPixel.getValue()).intValue() != 3) {
|
||||
@@ -111,7 +106,7 @@ final class EXIFThumbnail {
|
||||
long stripOffset = ((Number) stripOffEntry.getValue()).longValue();
|
||||
|
||||
int thumbLength = w * h * 3;
|
||||
if (stripOffset >= 0 && stripOffset + thumbLength < stream.length()) {
|
||||
if (stripOffset >= 0 && stripOffset + thumbLength <= stream.length()) {
|
||||
// Read raw image data, either RGB or YCbCr
|
||||
stream.seek(stripOffset);
|
||||
byte[] thumbData = new byte[thumbLength];
|
||||
@@ -135,11 +130,10 @@ final class EXIFThumbnail {
|
||||
}
|
||||
}
|
||||
|
||||
listener.warningOccurred("EXIF IFD with empty or incomplete uncompressed thumbnail");
|
||||
return null;
|
||||
throw new IIOException("EXIF IFD with empty or incomplete uncompressed thumbnail");
|
||||
}
|
||||
|
||||
private static JPEGThumbnailReader createJPEGThumbnailReader(EXIF exif, ImageReader jpegThumbnailReader, JPEGSegmentWarningListener listener, ImageInputStream stream, Directory ifd1) throws IOException {
|
||||
private static JPEGThumbnailReader createJPEGThumbnailReader(EXIF exif, ImageReader jpegThumbnailReader, ImageInputStream stream, Directory ifd1) throws IOException {
|
||||
Entry jpegOffEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
if (jpegOffEntry != null) {
|
||||
Entry jpegLenEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
|
||||
@@ -152,13 +146,13 @@ final class EXIFThumbnail {
|
||||
// Verify first bytes are FFD8
|
||||
stream.seek(jpegOffset);
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
if (stream.readUnsignedShort() == JPEG.SOI) {
|
||||
return new JPEGThumbnailReader(jpegThumbnailReader, stream, jpegOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listener.warningOccurred("EXIF IFD with empty or incomplete JPEG thumbnail");
|
||||
return null;
|
||||
throw new IIOException("EXIF IFD with empty or incomplete JPEG thumbnail");
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -32,6 +32,9 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JFIFThumbnail
|
||||
*
|
||||
@@ -43,14 +46,13 @@ final class JFIFThumbnail {
|
||||
private JFIFThumbnail() {
|
||||
}
|
||||
|
||||
static ThumbnailReader from(final JFIF segment, final JPEGSegmentWarningListener listener) {
|
||||
static ThumbnailReader from(final JFIF segment) throws IOException {
|
||||
if (segment != null && segment.xThumbnail > 0 && segment.yThumbnail > 0) {
|
||||
if (segment.thumbnail == null || segment.thumbnail.length < segment.xThumbnail * segment.yThumbnail) {
|
||||
listener.warningOccurred("Ignoring truncated JFIF thumbnail");
|
||||
}
|
||||
else {
|
||||
return new UncompressedThumbnailReader(segment.xThumbnail, segment.yThumbnail, segment.thumbnail);
|
||||
throw new IIOException("Truncated JFIF thumbnail");
|
||||
}
|
||||
|
||||
return new UncompressedThumbnailReader(segment.xThumbnail, segment.yThumbnail, segment.thumbnail);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+9
-4
@@ -36,7 +36,9 @@ import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.JPEGThumbnailReade
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReader;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JFXXThumbnailReader
|
||||
@@ -50,7 +52,7 @@ final class JFXXThumbnail {
|
||||
private JFXXThumbnail() {
|
||||
}
|
||||
|
||||
static ThumbnailReader from(final JFXX segment, final ImageReader thumbnailReader, final JPEGSegmentWarningListener listener) {
|
||||
static ThumbnailReader from(final JFXX segment, final ImageReader thumbnailReader) throws IOException {
|
||||
if (segment != null) {
|
||||
if (segment.thumbnail != null && segment.thumbnail.length > 2) {
|
||||
switch (segment.extensionCode) {
|
||||
@@ -64,26 +66,29 @@ final class JFXXThumbnail {
|
||||
case JFXX.INDEXED:
|
||||
int w = segment.thumbnail[0] & 0xff;
|
||||
int h = segment.thumbnail[1] & 0xff;
|
||||
|
||||
if (segment.thumbnail.length >= 2 + 768 + w * h) {
|
||||
return new IndexedThumbnailReader(w, h, segment.thumbnail, 2, segment.thumbnail, 2 + 768);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case JFXX.RGB:
|
||||
w = segment.thumbnail[0] & 0xff;
|
||||
h = segment.thumbnail[1] & 0xff;
|
||||
|
||||
if (segment.thumbnail.length >= 2 + w * h * 3) {
|
||||
return new UncompressedThumbnailReader(w, h, segment.thumbnail, 2);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
listener.warningOccurred(String.format("Unknown JFXX extension code: %d, ignoring thumbnail", segment.extensionCode));
|
||||
return null;
|
||||
throw new IIOException(String.format("Unknown JFXX extension code: %d, ignoring thumbnail", segment.extensionCode));
|
||||
}
|
||||
}
|
||||
|
||||
listener.warningOccurred("JFXX segment truncated, ignoring thumbnail");
|
||||
throw new IIOException("JFXX segment truncated, ignoring thumbnail");
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ final class JPEGImage10MetadataCleaner {
|
||||
IIOMetadataNode app0JFXX = new IIOMetadataNode("app0JFXX");
|
||||
app0JFXX.setAttribute("extensionCode", String.valueOf(jfxx.extensionCode));
|
||||
|
||||
ThumbnailReader thumbnailReader = JFXXThumbnail.from(jfxx, reader.getThumbnailReader(), JPEGSegmentWarningListener.NULL_LISTENER);
|
||||
ThumbnailReader thumbnailReader = JFXXThumbnail.from(jfxx, reader.getThumbnailReader());
|
||||
IIOMetadataNode jfifThumb;
|
||||
|
||||
switch (jfxx.extensionCode) {
|
||||
|
||||
+25
-23
@@ -909,17 +909,6 @@ public final class JPEGImageReader extends ImageReaderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Util method?
|
||||
static byte[] readFully(DataInput stream, int len) throws IOException {
|
||||
if (len == 0) {
|
||||
throw new IllegalArgumentException("len == 0");
|
||||
}
|
||||
|
||||
byte[] data = new byte[len];
|
||||
stream.readFully(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
ICC_Profile getEmbeddedICCProfile(final boolean allowBadIndexes) throws IOException {
|
||||
// ICC v 1.42 (2006) annex B:
|
||||
// APP2 marker (0xFFE2) + 2 byte length + ASCII 'ICC_PROFILE' + 0 (termination)
|
||||
@@ -1086,25 +1075,38 @@ public final class JPEGImageReader extends ImageReaderBase {
|
||||
if (thumbnails == null) {
|
||||
thumbnails = new ArrayList<>();
|
||||
|
||||
JPEGSegmentWarningDelegate listenerDelegate = new JPEGSegmentWarningDelegate();
|
||||
|
||||
// Read JFIF thumbnails if present
|
||||
ThumbnailReader thumbnailReader = JFIFThumbnail.from(getJFIF(), listenerDelegate);
|
||||
if (thumbnailReader != null) {
|
||||
thumbnails.add(thumbnailReader);
|
||||
try {
|
||||
ThumbnailReader thumbnail = JFIFThumbnail.from(getJFIF());
|
||||
if (thumbnail != null) {
|
||||
thumbnails.add(thumbnail);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
processWarningOccurred(e.getMessage());
|
||||
}
|
||||
|
||||
// Read JFXX thumbnails if present
|
||||
thumbnailReader = JFXXThumbnail.from(getJFXX(), getThumbnailReader(), listenerDelegate);
|
||||
if (thumbnailReader != null) {
|
||||
thumbnails.add(thumbnailReader);
|
||||
try {
|
||||
ThumbnailReader thumbnail = JFXXThumbnail.from(getJFXX(), getThumbnailReader());
|
||||
if (thumbnail != null) {
|
||||
thumbnails.add(thumbnail);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
processWarningOccurred(e.getMessage());
|
||||
}
|
||||
|
||||
// Read Exif thumbnails if present
|
||||
EXIF exif = getExif();
|
||||
thumbnailReader = EXIFThumbnail.from(exif, parseExif(exif), getThumbnailReader(), listenerDelegate);
|
||||
if (thumbnailReader != null) {
|
||||
thumbnails.add(thumbnailReader);
|
||||
try {
|
||||
EXIF exif = getExif();
|
||||
ThumbnailReader thumbnailReader = EXIFThumbnail.from(exif, parseExif(exif), getThumbnailReader());
|
||||
if (thumbnailReader != null) {
|
||||
thumbnails.add(thumbnailReader);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
processWarningOccurred(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user