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