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 7ca22a38781611ef8d5c724e32f243c9f634d544
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue May 12 18:51:03 2026 +0200

    Implement the support of the `tilC` box in GeoHEIF files.
---
 .../sis/storage/geoheif/CoverageBuilder.java       |   3 -
 .../sis/storage/geoheif/UncompressedImage.java     |   2 +-
 .../apache/sis/storage/isobmff/BoxRegistry.java    |   9 ++
 .../apache/sis/storage/isobmff/ContainerBox.java   |  13 +++
 .../org/apache/sis/storage/isobmff/Reader.java     |  11 +-
 .../main/org/apache/sis/storage/isobmff/Root.java  |   2 +-
 .../apache/sis/storage/isobmff/base/GroupList.java |   2 +-
 .../sis/storage/isobmff/base/ItemProperties.java   |   2 +-
 .../isobmff/base/ItemPropertyContainer.java        |   2 +-
 .../sis/storage/isobmff/base/ItemReference.java    |   3 +-
 .../org/apache/sis/storage/isobmff/base/Meta.java  |   3 +-
 .../org/apache/sis/storage/isobmff/base/Movie.java |   2 +-
 .../sis/storage/isobmff/base/OriginalFileType.java |   2 +-
 .../org/apache/sis/storage/isobmff/base/Track.java |   2 +-
 .../apache/sis/storage/isobmff/base/UserData.java  |   2 +-
 .../isobmff/geo/TiledImageConfiguration.java       | 116 ++++++++++++++++++++-
 16 files changed, 153 insertions(+), 23 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 88c7c8fbec..c9e9f8489d 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
@@ -72,9 +72,6 @@ import org.apache.sis.pending.jdk.JDK18;
  * Helper class for building the grid geometry and sample dimensions of a grid 
coverage.
  * Also opportunistically builds the coverage metadata associated to the 
resource.
  *
- * <p>The call to {@link #buildAndFreeze()} shall be last because metadata are 
completed
- * as side-effect of other method calls (for building name, grid geometry, 
<i>etc</i>).</p>
- *
  * @author Johann Sorel (Geomatys)
  * @author Martin Desruisseaux (Geomatys)
  */
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 d36d2ec798..dae972670f 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
@@ -33,7 +33,7 @@ import org.apache.sis.storage.isobmff.ByteRanges;
 
 
 /**
- * A single image ({@code 'unci'} item type) from the HEIF file.
+ * A single image ({@code 'unci'} item type) from the <abbr>HEIF</abbr> file.
  * An image may be used as a tile in a larger image ({@code 'grid'} item type).
  * The image may be implicitly tiled if {@link #numXTiles} is greater than 1.
  *
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/BoxRegistry.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/BoxRegistry.java
index e77c97c890..92de12832c 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/BoxRegistry.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/BoxRegistry.java
@@ -39,6 +39,15 @@ public abstract class BoxRegistry {
     protected BoxRegistry() {
     }
 
+    /**
+     * Returns the registry for all boxes.
+     *
+     * @return the registry for all boxes.
+     */
+    public static BoxRegistry global() {
+        return MainBoxRegistry.INSTANCE;
+    }
+
     /**
      * Creates a new box for the given box type.
      * Unknown types should be ignored.
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/ContainerBox.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/ContainerBox.java
index 65338d2da8..bd83bd5c03 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/ContainerBox.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/ContainerBox.java
@@ -42,6 +42,19 @@ public abstract class ContainerBox extends Box {
      */
     public final Box[] children;
 
+    /**
+     * Creates a new box and loads the payload using the global box registry.
+     *
+     * @param  reader    the reader from which to read the payload.
+     * @param  indexed   whether index matter. If {@code true}, then the 
children array may contain null elements.
+     * @throws IOException if an error occurred while reading the payload.
+     * @throws DataStoreContentException if the <abbr>HEIF</abbr> file is 
malformed.
+     * @throws DataStoreException if the reading failed for another reason.
+     */
+    protected ContainerBox(final Reader reader, final boolean indexed) throws 
IOException, DataStoreException {
+        this(reader, MainBoxRegistry.INSTANCE, indexed);
+    }
+
     /**
      * Creates a new box and loads the payload using the given box registry.
      * A custom registry is specified for filtering the type of boxes to 
accept.
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
index aa19fdaf51..0a52fcdc91 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
@@ -207,7 +207,7 @@ public final class Reader implements Cloneable {
                 listeners.warning(record);
             }
         }
-        assert endOfCurrentBox == end;      // Ensure that the value has not 
been modified.
+        endOfCurrentBox = end;      // May have been modified by recursive 
invocations.
         assert (end < 0) || input.getStreamPosition() <= end : 
Box.formatFourCC(box.type());
         return box;
     }
@@ -217,7 +217,7 @@ public final class Reader implements Cloneable {
      * A custom registry is specified for filtering the type of boxes to 
accept.
      * Boxes of unknown types are ignored and skipped.
      *
-     * @param  registry  the registry to use for box instantiations, or {@code 
null} for the main registry.
+     * @param  registry  the registry to use for box instantiations.
      * @param  indexed   whether index matter. If {@code true}, then the 
returned array may contain null elements.
      * @return children boxes. May contain null elements if {@code indexed} is 
true.
      * @throws IOException if an error occurred while reading the box.
@@ -227,10 +227,9 @@ public final class Reader implements Cloneable {
      * @throws DataStoreContentException if the <abbr>HEIF</abbr> file is 
malformed.
      * @throws DataStoreException if the reading failed for another reason.
      */
-    public final List<Box> readChildren(BoxRegistry registry, final boolean 
indexed) throws IOException, DataStoreException {
-        if (registry == null) {
-            registry = MainBoxRegistry.INSTANCE;
-        }
+    public final List<Box> readChildren(final BoxRegistry registry, final 
boolean indexed)
+            throws IOException, DataStoreException
+    {
         final var children = new ArrayList<Box>();
         final long end = endOfCurrentBox - 2*Integer.BYTES;     // With a 
margin of at least the space of a box header.
         final boolean toEnd = (end < 0);
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Root.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Root.java
index d2e88c78c8..4b7e4dbe46 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Root.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Root.java
@@ -38,7 +38,7 @@ public final class Root extends ContainerBox {
      * @throws DataStoreException if the reading failed for another reason.
      */
     public Root(final Reader reader) throws IOException, DataStoreException {
-        super(reader, MainBoxRegistry.INSTANCE, false);
+        super(reader, false);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/GroupList.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/GroupList.java
index 632a867116..e28f030e2d 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/GroupList.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/GroupList.java
@@ -58,6 +58,6 @@ public final class GroupList extends ContainerBox {
      * @throws DataStoreException if the stream contains inconsistent or 
unsupported data.
      */
     public GroupList(final Reader reader) throws IOException, 
DataStoreException {
-        super(reader, null, false);
+        super(reader, false);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
index c0fc1aea60..897be8d2cc 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
@@ -64,7 +64,7 @@ public final class ItemProperties extends ContainerBox {
      * @throws DataStoreException if the stream contains inconsistent or 
unsupported data.
      */
     public ItemProperties(final Reader reader) throws IOException, 
DataStoreException {
-        super(reader, null, false);
+        super(reader, false);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyContainer.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyContainer.java
index f6565a2fa4..538d12efcd 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyContainer.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyContainer.java
@@ -59,6 +59,6 @@ public final class ItemPropertyContainer extends ContainerBox 
{
      * @throws DataStoreException if the stream contains inconsistent or 
unsupported data.
      */
     public ItemPropertyContainer(final Reader reader) throws IOException, 
DataStoreException {
-        super(reader, null, true);
+        super(reader, true);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemReference.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemReference.java
index 1ce50ee2ff..8c15872943 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemReference.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemReference.java
@@ -19,6 +19,7 @@ package org.apache.sis.storage.isobmff.base;
 import java.io.IOException;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.base.MetadataBuilder;
+import org.apache.sis.storage.isobmff.BoxRegistry;
 import org.apache.sis.storage.isobmff.FullBox;
 import org.apache.sis.storage.isobmff.Reader;
 
@@ -62,7 +63,7 @@ public final class ItemReference extends FullBox {
     public ItemReference(final Reader reader) throws IOException, 
DataStoreException {
         super(reader);
         requireVersionZero();
-        references = reader.readChildren(null, 
false).toArray(SingleItemTypeReference[]::new);
+        references = reader.readChildren(BoxRegistry.global(), 
false).toArray(SingleItemTypeReference[]::new);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Meta.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Meta.java
index fa1803abeb..f454142ba9 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Meta.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Meta.java
@@ -21,6 +21,7 @@ import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.isobmff.Box;
 import org.apache.sis.storage.isobmff.Reader;
 import org.apache.sis.storage.isobmff.FullBox;
+import org.apache.sis.storage.isobmff.BoxRegistry;
 import org.apache.sis.storage.base.MetadataBuilder;
 
 
@@ -85,7 +86,7 @@ public final class Meta extends FullBox {
     public Meta(final Reader reader) throws IOException, DataStoreException {
         super(reader);
         requireVersionZero();
-        children = reader.readChildren(null, false).toArray(Box[]::new);
+        children = reader.readChildren(BoxRegistry.global(), 
false).toArray(Box[]::new);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Movie.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Movie.java
index 31b75fc69a..33fa4a7cfc 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Movie.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Movie.java
@@ -54,6 +54,6 @@ public final class Movie extends ContainerBox {
      * @throws DataStoreException if the stream contains inconsistent or 
unsupported data.
      */
     public Movie(final Reader reader) throws IOException, DataStoreException {
-        super(reader, null, false);
+        super(reader, false);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/OriginalFileType.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/OriginalFileType.java
index a870eaabae..2c98ddd455 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/OriginalFileType.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/OriginalFileType.java
@@ -56,6 +56,6 @@ public final class OriginalFileType extends ContainerBox {
      * @throws DataStoreException if the stream contains inconsistent or 
unsupported data.
      */
     public OriginalFileType(final Reader reader) throws IOException, 
DataStoreException {
-        super(reader, null, false);
+        super(reader, false);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Track.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Track.java
index 0b9ae6d7f6..2cc5f475d0 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Track.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Track.java
@@ -56,6 +56,6 @@ public final class Track extends ContainerBox {
      * @throws DataStoreException if the stream contains inconsistent or 
unsupported data.
      */
     public Track(final Reader reader) throws IOException, DataStoreException {
-        super(reader, null, false);
+        super(reader, false);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/UserData.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/UserData.java
index 4b9bc64e7e..61c670135a 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/UserData.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/UserData.java
@@ -54,6 +54,6 @@ public final class UserData extends ContainerBox {
      * @throws DataStoreException if the stream contains inconsistent or 
unsupported data.
      */
     public UserData(final Reader reader) throws IOException, 
DataStoreException {
-        super(reader, null, false);
+        super(reader, false);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
index 80be557151..843c211537 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
@@ -17,14 +17,21 @@
 package org.apache.sis.storage.isobmff.geo;
 
 import java.io.IOException;
+import org.apache.sis.io.stream.ChannelDataInput;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.isobmff.Box;
+import org.apache.sis.storage.isobmff.BoxRegistry;
 import org.apache.sis.storage.isobmff.FullBox;
 import org.apache.sis.storage.isobmff.Reader;
+import org.apache.sis.util.collection.TreeTable;
 
 
 /**
- * Configuration of the tile matrix.
+ * Configuration of the tile matrix. All tiles have the same size.
+ * Tiles at the right or bottom border may extend beyond the total image size.
  *
- * @todo Not yet implemented.
+ * <h4>Container</h4>
+ * The container can be an {@link 
org.apache.sis.storage.isobmff.base.ItemPropertyContainer}.
  *
  * @author Johann Sorel (Geomatys)
  * @author Martin Desruisseaux (Geomatys)
@@ -44,13 +51,116 @@ public final class TiledImageConfiguration extends FullBox 
{
         return BOXTYPE;
     }
 
+    /**
+     * Tile width in pixels.
+     */
+    @Interpretation(Type.UNSIGNED)
+    public final int tileWidth;
+
+    /**
+     * Tile height in pixels.
+     */
+    @Interpretation(Type.UNSIGNED)
+    public final int tileHeight;
+
+    /**
+     * The image type used for all tiles.
+     * Examples: {@code hvc1} for h265 compression or {@code j2k1} for 
JPEG2000.
+     */
+    @Interpretation(Type.FOURCC)
+    public final int tileItemType;
+
+    /**
+     * The size of dimensions other than the two first dimensions.
+     * This array is empty for two-dimensional images.
+     * The sizes of the first two dimensions are obtained from the mandatory
+     * {@link org.apache.sis.storage.isobmff.image.ImageSpatialExtents} box.
+     */
+    @Interpretation(Type.UNSIGNED)
+    public final int[] extraDimensionSizes;
+
+    /**
+     * Image item properties used when decoding a tile image.
+     * This includes at least all mandatory item properties for an image item 
of type {@link #tileItemType}
+     * with the exception of {@code ispe}. If {@code tileImageProperties} does 
not contain an {@code ispe}
+     * property box, the decoder shall synthesize an {@code ispe} property 
with {@link #tileWidth} and
+     * {@link #tileHeight} as the size.
+     */
+    public final Box[] tileImageProperties;
+
     /**
      * Creates a new box and loads the payload from the given reader.
      *
      * @param  reader  the reader from which to read the payload.
      * @throws IOException if an error occurred while reading the payload.
+     * @throws DataStoreException if the box creation failed for a logical 
error.
      */
-    public TiledImageConfiguration(final Reader reader) throws IOException {
+    public TiledImageConfiguration(final Reader reader) throws IOException, 
DataStoreException {
         super(reader);
+        requireVersionZero();
+        final ChannelDataInput input = reader.input;
+        tileWidth           = input.readInt();
+        tileHeight          = input.readInt();
+        tileItemType        = input.readInt();
+        extraDimensionSizes = input.readInts(input.readUnsignedByte());
+        tileImageProperties = new Box[input.readUnsignedByte()];
+        for (int i = 0; i < tileImageProperties.length; i++) {
+            tileImageProperties[i] = reader.readBox(BoxRegistry.global());
+        }
+    }
+
+    /**
+     * Mapping from {@link #flags} to the offset length.
+     */
+    private static final byte[] OFFSET_LENGTHS = {Integer.SIZE, 40, 48, 
Long.SIZE};
+
+    /**
+     * Mapping from {@link #flags} to the size length.
+     * Value 0 means that size is not stored. Instead, it is computed from 
offsets.
+     */
+    private static final byte[] SIZE_LENGTHS = {0, 3*Byte.SIZE, Integer.SIZE, 
Long.SIZE};
+
+    /**
+     * Returns the number of bits used to store the offset to the image data 
of a specific tile.
+     *
+     * @return the number of bits for tile offset.
+     */
+    public final int offsetFieldLength() {
+        return OFFSET_LENGTHS[flags & 0x03];
+        // `IndexOutOfBoundsException` cannot happen because of `0x03` mask.
+    }
+
+    /**
+     * Returns the number of bits used to store the length of the image data 
of a specific tile.
+     *
+     * @return the number of bits for tile length.
+     */
+    public final int sizeFieldLength() {
+        return SIZE_LENGTHS[(flags >>> 2) & 0x03];
+        // `IndexOutOfBoundsException` cannot happen because of `0x03` mask.
+    }
+
+    /**
+     * Returns a hint about whether tiles are in sequential order.
+     *
+     * @return whether tiles are in sequential order.
+     */
+    public final boolean sequential() {
+        return (flags & 0x10) != 0;
+    }
+
+    /**
+     * Appends properties other than the ones defined by public fields.
+     * Those properties will be shown last in the tree.
+     *
+     * @param  context  the tree being formatted. Can be used for fetching 
contextual information.
+     * @param  target   the node where to add properties.
+     */
+    @Override
+    protected void appendTreeNodes(final Tree context, final TreeTable.Node 
target) {
+        Tree.addNode(target, "offsetFieldLength", offsetFieldLength());
+        Tree.addNode(target, "sizeFieldLength",   sizeFieldLength());
+        Tree.addNode(target, "sequential",        sequential());
+        super.appendTreeNodes(context, target);
     }
 }

Reply via email to