This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 3cd871f3cc3193a5eb5560411ce43ebb2244f01b Author: jsorel <[email protected]> AuthorDate: Wed Jun 3 12:31:09 2026 +0200 feat(GeoHeif): support single chunk zlib compressed tiles --- .../sis/storage/geoheif/CoverageBuilder.java | 28 +++++++++++++++ .../sis/storage/geoheif/UncompressedImage.java | 40 ++++++++++++++++++++-- .../isobmff/mpeg/CompressedUnitsItemInfo.java | 9 ++++- .../isobmff/mpeg/CompressionConfiguration.java | 5 +-- 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java index f72a91de76..ab53ba7e16 100644 --- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java +++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java @@ -66,6 +66,8 @@ import org.apache.sis.util.collection.Containers; import org.apache.sis.util.internal.shared.Numerics; import org.apache.sis.util.resources.Errors; import org.apache.sis.pending.jdk.JDK18; +import org.apache.sis.storage.isobmff.mpeg.CompressedUnitsItemInfo; +import org.apache.sis.storage.isobmff.mpeg.CompressionConfiguration; /** @@ -104,6 +106,12 @@ final class CoverageBuilder implements Emptiable { */ private int width, height; + /** + * Information about compression. + */ + private CompressionConfiguration compression; + private CompressedUnitsItemInfo compressedUnits; + /** * Coefficients of the matrix that defines the "grid to <abbr>CRS</abbr>" coordinate conversion. * May be {@code null} if no such information was found. @@ -276,6 +284,18 @@ final class CoverageBuilder implements Emptiable { if (!duplicated) palette = c; break; } + case CompressionConfiguration.BOXTYPE: { + var c = (CompressionConfiguration) property; + duplicated = (compression != null); + if (!duplicated) compression = c; + break; + } + case CompressedUnitsItemInfo.BOXTYPE: { + var c = (CompressedUnitsItemInfo) property; + duplicated = (compressedUnits != null); + if (!duplicated) compressedUnits = c; + break; + } default: { unknownBoxes.merge(property.typeKey(), properties.essential(i), Boolean::logicalOr); continue; @@ -290,6 +310,14 @@ final class CoverageBuilder implements Emptiable { } } + public CompressionConfiguration getCompression() { + return compression; + } + + public CompressedUnitsItemInfo getCompressedUnits() { + return compressedUnits; + } + /** * If any boxes were unrecognized, reports these boxes in a warning. * If at least one ignored box was flagged as essential, then this method returns {@code true} diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java index 8d65edfa79..a796f8ed5c 100644 --- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java +++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java @@ -23,6 +23,11 @@ import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.RasterFormatException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.zip.InflaterInputStream; import org.apache.sis.image.DataType; import org.apache.sis.image.internal.shared.ImageUtilities; import org.apache.sis.image.internal.shared.RasterFactory; @@ -30,7 +35,10 @@ import org.apache.sis.io.stream.ChannelDataInput; import org.apache.sis.io.stream.HyperRectangleReader; import org.apache.sis.io.stream.Region; import org.apache.sis.storage.DataStoreException; +import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.isobmff.ByteRanges; +import org.apache.sis.storage.isobmff.mpeg.CompressedUnitsItemInfo; +import org.apache.sis.storage.isobmff.mpeg.CompressionConfiguration; /** @@ -58,6 +66,9 @@ final class UncompressedImage extends Image { */ private final SampleModel sampleModel; + private final CompressionConfiguration compression; + private final CompressedUnitsItemInfo compressionUnits; + /** * Creates a new tile. * @@ -72,6 +83,8 @@ final class UncompressedImage extends Image { super(builder, locator, name); sampleModel = builder.sampleModel(); dataType = builder.dataType(); // Shall be after `sampleModel()`. + compression = builder.getCompression(); + compressionUnits = builder.getCompressedUnits(); } /** @@ -122,7 +135,30 @@ final class UncompressedImage extends Image { final long tileIndex = addExact(multiplyExact(context.subTileY, numXTiles), context.subTileX); final long offset = addExact(multiplyExact(tileIndex, tileSize), skipBytes); locator.resolve(offset, tileSize - skipBytes, context); - return (final ChannelDataInput input) -> { + + return (ChannelDataInput input) -> { + + long origin = context.offset() - skipBytes; + + if (compression != null) { + //we need to uncompress the data + if (compression.compressionType.equals("zlib") + && compression.unitType == CompressionConfiguration.UNIT_TYPE_IMAGE_TILE + && compressionUnits.numCompressedUnits == 1) { + final long off = compressionUnits.units[0][0]; + final long size = compressionUnits.units[0][1]; + input.skipNBytes((int) off); + final byte[] compressedChunk = input.readBytes((int) size); + InflaterInputStream iis = new InflaterInputStream(new ByteArrayInputStream(compressedChunk)); + input = new StorageConnector(iis).getStorageAs(ChannelDataInput.class); + + origin = 0; + + } else { + throw new IOException("Unsupported compression configuration with type : " + compression.compressionType); + } + } + /* * Now read all banks and store the values in the image buffer. * If there is many banks (`InterleavingMode.COMPONENT`), these @@ -130,7 +166,7 @@ final class UncompressedImage extends Image { */ input.buffer.order(byteOrder); final var hr = new HyperRectangleReader(ImageUtilities.toNumberEnum(dataType), input); - hr.setOrigin(context.offset() - skipBytes); + hr.setOrigin(origin); final WritableRaster raster = context.createRaster(); final long[] rasterSize = size(raster.getSampleModel()); final Region rasterRegion = Arrays.equals(sourceSize, rasterSize) ? region : region(sourceSize, rasterSize); diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressedUnitsItemInfo.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressedUnitsItemInfo.java index 3c9a46bedf..22670c1080 100644 --- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressedUnitsItemInfo.java +++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressedUnitsItemInfo.java @@ -61,6 +61,11 @@ public final class CompressedUnitsItemInfo extends FullBox { public final int unitSize; public final int numCompressedUnits; + /** + * Compressed units offsets and sizes. + */ + public long[][] units; + /** * Creates a new box and loads the payload from the given reader. * @@ -75,9 +80,11 @@ public final class CompressedUnitsItemInfo extends FullBox { unitSize = UNIT_SIZE_BITS_TABLE[(int) input.readBits(3)]; input.skipRemainingBits(); numCompressedUnits = input.readInt(); + + units = readUnits(reader); } - public long[][] readUnits(final Reader reader) throws IOException { + private long[][] readUnits(final Reader reader) throws IOException { final ChannelDataInput input = reader.input; final long[][] units = new long[numCompressedUnits][2]; for (int i = 0; i < numCompressedUnits; i++) { diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java index eb41d91ca8..e6c0318eab 100644 --- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java +++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java @@ -18,6 +18,7 @@ package org.apache.sis.storage.isobmff.mpeg; import java.io.IOException; import org.apache.sis.io.stream.ChannelDataInput; +import org.apache.sis.storage.isobmff.Box; import org.apache.sis.storage.isobmff.FullBox; import org.apache.sis.storage.isobmff.Reader; import org.apache.sis.storage.isobmff.Incomplete; @@ -57,7 +58,7 @@ public final class CompressionConfiguration extends FullBox { /** * Identifier of the compression, FourCC code. */ - public final int compressionType; + public final String compressionType; public final int unitType; /** @@ -70,7 +71,7 @@ public final class CompressionConfiguration extends FullBox { super(reader); requireVersionZero(); final ChannelDataInput input = reader.input; - compressionType = input.readInt(); + compressionType = Box.formatFourCC(input.readInt()); unitType = input.readUnsignedByte(); } }
