From ee424583c4cbaae9dd378c98fcf9ab4e085e680d Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Sat, 11 Nov 2023 15:03:09 +0100 Subject: [PATCH] Clean-up --- .../plugins/tiff/DelegateTileDecoder.java | 15 ++-- .../imageio/plugins/tiff/JPEGTileDecoder.java | 8 +- .../imageio/plugins/tiff/TIFFImageReader.java | 82 +++++++++++-------- .../imageio/plugins/tiff/TileDecoder.java | 4 +- 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/DelegateTileDecoder.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/DelegateTileDecoder.java index a24d11c7..0909edc9 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/DelegateTileDecoder.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/DelegateTileDecoder.java @@ -26,8 +26,7 @@ class DelegateTileDecoder extends TileDecoder { protected final ImageReader delegate; protected final ImageReadParam param; - // TODO: Naming... Is this only due to color space conversion? Is it because we need to read raster? - private final Predicate needsConversion; + private final Predicate needsRasterConversion; private final RasterConverter converter; private Boolean readRasterAndConvert; @@ -35,11 +34,11 @@ class DelegateTileDecoder extends TileDecoder { this(warningListener, createDelegate(format), originalParam, imageReader -> false, null); } - DelegateTileDecoder(final IIOReadWarningListener warningListener, final String format, final ImageReadParam originalParam, final Predicate needsConversion, final RasterConverter converter) throws IOException { - this(warningListener, createDelegate(format), originalParam, needsConversion, converter); + DelegateTileDecoder(final IIOReadWarningListener warningListener, final String format, final ImageReadParam originalParam, final Predicate needsRasterConversion, final RasterConverter converter) throws IOException { + this(warningListener, createDelegate(format), originalParam, needsRasterConversion, converter); } - private DelegateTileDecoder(final IIOReadWarningListener warningListener, final ImageReader delegate, final ImageReadParam originalParam, final Predicate needsConversion, final RasterConverter converter) { + private DelegateTileDecoder(final IIOReadWarningListener warningListener, final ImageReader delegate, final ImageReadParam originalParam, final Predicate needsRasterConversion, final RasterConverter converter) { super(warningListener); this.delegate = notNull(delegate, "delegate"); @@ -48,7 +47,7 @@ class DelegateTileDecoder extends TileDecoder { param = delegate.getDefaultReadParam(); param.setSourceSubsampling(originalParam.getSourceXSubsampling(), originalParam.getSourceYSubsampling(), 0, 0); - this.needsConversion = needsConversion; + this.needsRasterConversion = needsRasterConversion; this.converter = converter; } @@ -57,7 +56,7 @@ class DelegateTileDecoder extends TileDecoder { // If it's the TwelveMonkeys one, we will be able to read JPEG Lossless etc. Iterator readers = ImageIO.getImageReadersByFormatName(format); if (!readers.hasNext()) { - throw new IIOException("Could not instantiate " + format + "ImageReader"); + throw new IIOException("No ImageReader registered for '" + format + "' format"); } return readers.next(); @@ -70,7 +69,7 @@ class DelegateTileDecoder extends TileDecoder { if (readRasterAndConvert == null) { // All tiles in an image will use the same format, test once and cache result - readRasterAndConvert = needsConversion.test(delegate); + readRasterAndConvert = needsRasterConversion.test(delegate); } if (!readRasterAndConvert) { diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/JPEGTileDecoder.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/JPEGTileDecoder.java index 493dfd68..8e1663a0 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/JPEGTileDecoder.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/JPEGTileDecoder.java @@ -15,8 +15,8 @@ import java.util.function.Predicate; * @author last modified by $Author: haraldk$ * @version $Id: JPEGTileDecoder.java,v 1.0 09/11/2023 haraldk Exp$ */ -class JPEGTileDecoder extends DelegateTileDecoder { - JPEGTileDecoder(final IIOReadWarningListener warningListener, final byte[] jpegTables, final ImageReadParam originalParam, final Predicate needsConversion, final RasterConverter converter) throws IOException { +final class JPEGTileDecoder extends DelegateTileDecoder { + JPEGTileDecoder(final IIOReadWarningListener warningListener, final byte[] jpegTables, final int numTiles, final ImageReadParam originalParam, final Predicate needsConversion, final RasterConverter converter) throws IOException { super(warningListener, "JPEG", originalParam, needsConversion, converter); if (jpegTables != null) { @@ -29,9 +29,9 @@ class JPEGTileDecoder extends DelegateTileDecoder { // http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#abbrev delegate.getStreamMetadata(); } - else { + else if (numTiles > 1) { warningListener.warningOccurred(delegate, "Missing JPEGTables for tiled/striped TIFF with compression: 7 (JPEG)"); - // ...and the JPEG reader will probably choke on missing tables... + // ...and the JPEG reader might choke on missing tables... } } } diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java index 44263c22..fab26bbd 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java @@ -1172,13 +1172,18 @@ public final class TIFFImageReader extends ImageReaderBase { break; } - case TIFFCustom.COMPRESSION_WEBP: case TIFFExtension.COMPRESSION_JPEG: + case TIFFCustom.COMPRESSION_WEBP: + case TIFFCustom.COMPRESSION_JBIG: + case TIFFCustom.COMPRESSION_JPEG2000: readUsingDelegate(imageIndex, compression, interpretation, width, height, tilesAcross, tilesDown, stripTileWidth, stripTileHeight, srcRegion, stripTileOffsets, stripTileByteCounts, param, destination, samplesInTile); break; + case TIFFExtension.COMPRESSION_OLD_JPEG: { // JPEG ('old-style' JPEG, later overridden in Technote2) // http://www.remotesensing.org/libtiff/TIFFTechNote2.html + // TODO: Rewrite to use readUsingDelegate with special case handling inside OldJPEGTileDecoder + int srcRow = 0; Boolean needsCSConversion = null; @@ -1497,10 +1502,8 @@ public final class TIFFImageReader extends ImageReaderBase { case TIFFCustom.COMPRESSION_PIXARFILM: case TIFFCustom.COMPRESSION_PIXARLOG: case TIFFCustom.COMPRESSION_DCS: - case TIFFCustom.COMPRESSION_JBIG: // Doable with JBIG plugin? case TIFFCustom.COMPRESSION_SGILOG: case TIFFCustom.COMPRESSION_SGILOG24: - case TIFFCustom.COMPRESSION_JPEG2000: // Doable with JPEG2000 plugin? throw new IIOException("Unsupported TIFF Compression value: " + compression); default: @@ -1521,7 +1524,7 @@ public final class TIFFImageReader extends ImageReaderBase { // JPEG ('new-style' JPEG) // Read data - try (TileDecoder tileDecoder = createTileDecoder(param, compression, interpretation, samplesInTile)) { + try (TileDecoder tileDecoder = createTileDecoder(param, compression, interpretation, tilesAcross * tilesDown, samplesInTile)) { processImageStarted(imageIndex); // Better yet, would be to delegate read progress here... int row = 0; @@ -1569,37 +1572,39 @@ public final class TIFFImageReader extends ImageReaderBase { } } - private DelegateTileDecoder createTileDecoder(ImageReadParam param, int compression, final int interpretation, final int samplesInTile) throws IOException { - if (compression == TIFFExtension.COMPRESSION_JPEG) { - // JPEG_TABLES should be a full JPEG 'abbreviated table specification', containing: - // SOI, DQT, DHT, (optional markers that we ignore)..., EOI - Entry tablesEntry = currentIFD.getEntryById(TIFF.TAG_JPEG_TABLES); - byte[] tablesValue = tablesEntry != null ? (byte[]) tablesEntry.getValue() : null; + private TileDecoder createTileDecoder(ImageReadParam param, int compression, final int interpretation, final int numTiles, final int samplesInTile) throws IOException { + try { + IIOReadWarningListener warningListener = (source, warning) -> processWarningOccurred(warning); - Predicate needsConversion = (reader) -> needsCSConversion(compression, interpretation, readJPEGMetadataSafe(reader)); - RasterConverter csConverter = (raster) -> { - switch (raster.getTransferType()) { - case DataBuffer.TYPE_BYTE: - normalizeColor(interpretation, samplesInTile, ((DataBufferByte) raster.getDataBuffer()).getData()); - break; - case DataBuffer.TYPE_USHORT: - normalizeColor(interpretation, samplesInTile, ((DataBufferUShort) raster.getDataBuffer()).getData()); - break; - default: - throw new IllegalStateException("Unsupported transfer type: " + raster.getTransferType()); - } - }; + if (compression == TIFFExtension.COMPRESSION_JPEG) { + // JPEG_TABLES should be a full JPEG 'abbreviated table specification', containing: + // SOI, DQT, DHT, (optional markers that we ignore)..., EOI + Entry tablesEntry = currentIFD.getEntryById(TIFF.TAG_JPEG_TABLES); + byte[] jpegTables = tablesEntry != null ? (byte[]) tablesEntry.getValue() : null; - return new JPEGTileDecoder((source, warning) -> processWarningOccurred(warning), tablesValue, param, needsConversion, csConverter); + Predicate needsConversion = (reader) -> needsCSConversion(compression, interpretation, readJPEGMetadataSafe(reader)); + RasterConverter normalize = (raster) -> normalizeColor(interpretation, samplesInTile, raster); + + return new JPEGTileDecoder(warningListener, jpegTables, numTiles, param, needsConversion, normalize); + } + else if (compression == TIFFCustom.COMPRESSION_JBIG) { + // TODO: Create interop test suite using third party plugin. + // LEAD Tools have one sample file: https://leadtools.com/support/forum/resource.ashx?a=545&b=1 + // Haven't found any plugins. There is however a JBIG2 plugin... + return new DelegateTileDecoder(warningListener, "JBIG", param); + } + else if (compression == TIFFCustom.COMPRESSION_JPEG2000) { + // TODO: Create interop test suite using third party plugin + // LEAD Tools have one sample file: https://leadtools.com/support/forum/resource.ashx?a=545&b=1 + // The open source JAI JP2K reader decodes this as a fully black image... + return new DelegateTileDecoder(warningListener, "JPEG2000", param); + } + else if (compression == TIFFCustom.COMPRESSION_WEBP) { + return new DelegateTileDecoder(warningListener, "WebP", param); + } } - else if (compression == TIFFCustom.COMPRESSION_JBIG) { - return new DelegateTileDecoder((source, warning) -> processWarningOccurred(warning), "JBIG", param); - } - else if (compression == TIFFCustom.COMPRESSION_JPEG2000) { - return new DelegateTileDecoder((source, warning) -> processWarningOccurred(warning), "JP2K", param); - } - else if (compression == TIFFCustom.COMPRESSION_WEBP) { - return new DelegateTileDecoder((source, warning) -> processWarningOccurred(warning), "WebP", param); + catch (IIOException e) { + throw new IIOException("Unsupported TIFF Compression value: " + compression, e); } throw new IIOException("Unsupported TIFF Compression value: " + compression); @@ -2165,6 +2170,19 @@ public final class TIFFImageReader extends ImageReaderBase { } } + private void normalizeColor(int interpretation, int numBands, Raster raster) throws IOException { + switch (raster.getTransferType()) { + case DataBuffer.TYPE_BYTE: + normalizeColor(interpretation, numBands, ((DataBufferByte) raster.getDataBuffer()).getData()); + break; + case DataBuffer.TYPE_USHORT: + normalizeColor(interpretation, numBands, ((DataBufferUShort) raster.getDataBuffer()).getData()); + break; + default: + throw new IllegalStateException("Unsupported transfer type: " + raster.getTransferType()); + } + } + private void normalizeColor(int photometricInterpretation, int numBands, byte[] data) throws IOException { switch (photometricInterpretation) { case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO: diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TileDecoder.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TileDecoder.java index 9ed7259c..26a93c4b 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TileDecoder.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TileDecoder.java @@ -6,6 +6,8 @@ import java.awt.*; import java.awt.image.*; import java.io.IOException; +import static com.twelvemonkeys.lang.Validate.notNull; + /** * TileDecoder. * @@ -18,7 +20,7 @@ abstract class TileDecoder implements AutoCloseable { protected final IIOReadWarningListener warningListener; public TileDecoder(IIOReadWarningListener warningListener) { - this.warningListener = warningListener; + this.warningListener = notNull(warningListener, "warningListener"); } abstract void decodeTile(ImageInputStream input, Rectangle sourceRegion, Point destinationOffset, BufferedImage destination) throws IOException;