You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/04/08 16:38:07 UTC

[sis] 01/03: Make `getTileWidth()` and `getTileHeight()` methods final in `ComputedImage`. Add design notes in Javadoc for explaining some rational.

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 65c2a49846fd3aafb3d23415c923ed5f66a6b1f7
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Apr 8 15:27:44 2023 +0200

    Make `getTileWidth()` and `getTileHeight()` methods final in `ComputedImage`.
    Add design notes in Javadoc for explaining some rational.
---
 .../java/org/apache/sis/image/ComputedImage.java   | 36 ++++++++++++----------
 .../org/apache/sis/image/SourceAlignedImage.java   |  8 ++---
 .../sis/internal/coverage/j2d/ImageUtilities.java  | 30 ++++++++++++++++++
 3 files changed, 53 insertions(+), 21 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
index 126fe58caf..c991a18cb4 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
@@ -43,6 +43,7 @@ import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.coverage.grid.GridExtent;     // For javadoc
 import org.apache.sis.internal.feature.Resources;
+import org.apache.sis.internal.coverage.j2d.ImageUtilities;
 
 
 /**
@@ -401,37 +402,41 @@ public abstract class ComputedImage extends PlanarImage implements Disposable {
      * @return the sample model of this image.
      */
     @Override
-    public SampleModel getSampleModel() {
+    public final SampleModel getSampleModel() {
         return sampleModel;
     }
 
     /**
-     * Returns the width of tiles in this image. The default implementation returns {@link SampleModel#getWidth()}.
+     * Returns the width of tiles in this image.
+     * In {@code ComputedImage} implementation, this is fixed to {@link SampleModel#getWidth()}.
      *
-     * <h4>Note</h4>
-     * A raster can have a smaller width than its sample model, for example when a raster is a view over a subregion
-     * of another raster. But this is not recommended in the particular case of this {@code ComputedImage} class,
-     * because it would cause {@link #createTile(int, int)} to consume more memory than necessary.
+     * <h4>Design note</h4>
+     * In theory it is legal to have a tile width smaller than the sample model width,
+     * for example when a raster is a view over a subregion of another raster.
+     * But this is not allowed in {@code ComputedImage} class, because it would
+     * cause {@link #createTile(int, int)} to consume more memory than necessary.
      *
      * @return the width of this image in pixels.
      */
     @Override
-    public int getTileWidth() {
+    public final int getTileWidth() {
         return sampleModel.getWidth();
     }
 
     /**
-     * Returns the height of tiles in this image. The default implementation returns {@link SampleModel#getHeight()}.
+     * Returns the height of tiles in this image.
+     * In {@code ComputedImage} implementation, this is fixed to {@link SampleModel#getHeight()}.
      *
-     * <h4>Note</h4>
-     * A raster can have a smaller height than its sample model, for example when a raster is a view over a subregion
-     * of another raster. But this is not recommended in the particular case of this {@code ComputedImage} class,
-     * because it would cause {@link #createTile(int, int)} to consume more memory than necessary.
+     * <h4>Design note</h4>
+     * In theory it is legal to have a tile height smaller than the sample model height,
+     * for example when a raster is a view over a subregion of another raster.
+     * But this is not allowed in {@code ComputedImage} class, because it would
+     * cause {@link #createTile(int, int)} to consume more memory than necessary.
      *
      * @return the height of this image in pixels.
      */
     @Override
-    public int getTileHeight() {
+    public final int getTileHeight() {
         return sampleModel.getHeight();
     }
 
@@ -588,9 +593,8 @@ public abstract class ComputedImage extends PlanarImage implements Disposable {
      * @return initially empty tile for the given indices (cannot be null).
      */
     protected WritableRaster createTile(final int tileX, final int tileY) {
-        // A temporary `int` overflow may occur before the final addition.
-        final int x = Math.toIntExact((((long) tileX) - getMinTileX()) * getTileWidth()  + getMinX());
-        final int y = Math.toIntExact((((long) tileY) - getMinTileY()) * getTileHeight() + getMinY());
+        final int x = ImageUtilities.tileToPixelX(this, tileX);
+        final int y = ImageUtilities.tileToPixelY(this, tileY);
         return WritableRaster.createWritableRaster(sampleModel, new Point(x,y));
     }
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java
index 608c3a3c7e..35df3ce8c6 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/SourceAlignedImage.java
@@ -33,10 +33,10 @@ import org.apache.sis.util.Workaround;
  * Tiles in this image have the same size than tiles in the source image.
  * See {@link ComputedImage} javadoc for more information about tile computation.
  *
- * <div class="note"><b>Relationship with other classes</b><br>
+ * <h2>Relationship with other classes</h2>
  * This class is similar to {@link ImageAdapter} except that it extends {@link ComputedImage}
  * and does not forward {@link #getTile(int, int)}, {@link #getData()} and other data methods
- * to the source image.</div>
+ * to the source image.
  *
  * <h2>Sub-classing</h2>
  * Subclasses need to implement at least the {@link #computeTile(int, int, WritableRaster)} method.
@@ -46,7 +46,7 @@ import org.apache.sis.util.Workaround;
  * The {@link #equals(Object)} and {@link #hashCode()} methods should also be overridden.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
  * @since   1.1
  */
 abstract class SourceAlignedImage extends ComputedImage {
@@ -182,8 +182,6 @@ abstract class SourceAlignedImage extends ComputedImage {
     @Override public final int getMinTileY()        {return getSource().getMinTileY();}
     @Override public final int getNumXTiles()       {return getSource().getNumXTiles();}
     @Override public final int getNumYTiles()       {return getSource().getNumYTiles();}
-    @Override public final int getTileWidth()       {return getSource().getTileWidth();}
-    @Override public final int getTileHeight()      {return getSource().getTileHeight();}
     @Override public final int getTileGridXOffset() {return getSource().getTileGridXOffset();}
     @Override public final int getTileGridYOffset() {return getSource().getTileGridYOffset();}
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
index 8b6bdbb63c..9c0280ba60 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
@@ -486,9 +486,16 @@ public final class ImageUtilities extends Static {
     /**
      * Converts a <var>x</var> pixel coordinates to a tile index.
      *
+     * <h4>Implementation note</h4>
+     * This method performs its calculation using <cite>tile grid offset</cite> instead of minimum coordinate
+     * values because the former does not assume that image coordinates start at the beginning of first tile.
+     * In practice it would be risky to have image {@code minX} different than first tile {@code minX},
+     * but Apache SIS tries to handle the most general cases when possible.
+     *
      * @param  image  the image containing tiles.
      * @param  x      the pixel coordinate for which to get tile index.
      * @return tile index for the given pixel coordinate.
+     * @throws ArithmeticException if the result overflows 32 bits integer.
      */
     public static int pixelToTileX(final RenderedImage image, final int x) {
         return toIntExact(floorDiv((x - (long) image.getTileGridXOffset()), image.getTileWidth()));
@@ -496,10 +503,12 @@ public final class ImageUtilities extends Static {
 
     /**
      * Converts a <var>y</var> pixel coordinates to a tile index.
+     * See {@link #pixelToTileX(RenderedImage, int)} for an implementation note.
      *
      * @param  image  the image containing tiles.
      * @param  y      the pixel coordinate for which to get tile index.
      * @return tile index for the given pixel coordinate.
+     * @throws ArithmeticException if the result overflows 32 bits integer.
      */
     public static int pixelToTileY(final RenderedImage image, final int y) {
         return toIntExact(floorDiv((y - (long) image.getTileGridYOffset()), image.getTileHeight()));
@@ -509,9 +518,16 @@ public final class ImageUtilities extends Static {
      * Converts a tile column index to smallest <var>x</var> pixel coordinate inside the tile.
      * The returned value is a coordinate of the pixel in upper-left corner.
      *
+     * <h4>Implementation note</h4>
+     * This method performs its calculation using <cite>tile grid offset</cite> instead of minimum coordinate
+     * values because the former does not assume that image coordinates start at the beginning of first tile.
+     * In practice it would be risky to have image {@code minX} different than first tile {@code minX},
+     * but Apache SIS tries to handle the most general cases when possible.
+     *
      * @param  image  the image containing tiles.
      * @param  tileX  the tile index for which to get pixel coordinate.
      * @return smallest <var>x</var> pixel coordinate inside the tile.
+     * @throws ArithmeticException if the result overflows 32 bits integer.
      */
     public static int tileToPixelX(final RenderedImage image, final int tileX) {
         // Following `long` arithmetic never overflows even if all values are `Integer.MAX_VALUE`.
@@ -521,10 +537,12 @@ public final class ImageUtilities extends Static {
     /**
      * Converts a tile row index to smallest <var>y</var> pixel coordinate inside the tile.
      * The returned value is a coordinate of the pixel in upper-left corner.
+     * See {@link #tileToPixelX(RenderedImage, int)} for an implementation note.
      *
      * @param  image  the image containing tiles.
      * @param  tileY  the tile index for which to get pixel coordinate.
      * @return smallest <var>y</var> pixel coordinate inside the tile.
+     * @throws ArithmeticException if the result overflows 32 bits integer.
      */
     public static int tileToPixelY(final RenderedImage image, final int tileY) {
         return toIntExact(multiplyFull(tileY, image.getTileHeight()) + image.getTileGridYOffset());
@@ -534,9 +552,15 @@ public final class ImageUtilities extends Static {
      * Converts pixel coordinates to pixel indices.
      * This method does <strong>not</strong> clip the rectangle to image bounds.
      *
+     * <h4>Implementation note</h4>
+     * This method performs its calculation using <cite>tile grid offset</cite> instead of minimum coordinate
+     * values because the former does not assume that image coordinates start at the beginning of first tile.
+     * The intend is to be consistent with {@link #pixelToTileX(RenderedImage, int)}.
+     *
      * @param  image   the image containing tiles.
      * @param  pixels  the pixel coordinates for which to get tile indices.
      * @return tile indices that fully contain the pixel coordinates.
+     * @throws ArithmeticException if the result overflows 32 bits integer.
      */
     public static Rectangle pixelsToTiles(final RenderedImage image, final Rectangle pixels) {
         final Rectangle r = new Rectangle();
@@ -562,9 +586,15 @@ public final class ImageUtilities extends Static {
      * Tiles will be fully included in the returned range of pixel indices.
      * This method does <strong>not</strong> clip the rectangle to image bounds.
      *
+     * <h4>Implementation note</h4>
+     * This method performs its calculation using <cite>tile grid offset</cite> instead of minimum coordinate
+     * values because the former does not assume that image coordinates start at the beginning of first tile.
+     * The intend is to be consistent with {@link #tileToPixelX(RenderedImage, int)}.
+     *
      * @param  image  the image containing tiles.
      * @param  tiles  the tile indices for which to get pixel coordinates.
      * @return pixel coordinates that fully contain the tiles.
+     * @throws ArithmeticException if the result overflows 32 bits integer.
      */
     public static Rectangle tilesToPixels(final RenderedImage image, final Rectangle tiles) {
         final Rectangle r = new Rectangle();