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();
     }
 }

Reply via email to