sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/03: Replace the `PreferredSize` hack by a public API.
Date Fri, 21 Aug 2020 18:05:19 GMT
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 c5ae48b39368dffc9f148f4cb632db0727c1cafc
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Aug 21 16:12:12 2020 +0200

    Replace the `PreferredSize` hack by a public API.
---
 .../org/apache/sis/gui/coverage/RenderingData.java |  6 +-
 .../java/org/apache/sis/image/ImageCombiner.java   |  2 +-
 .../java/org/apache/sis/image/ImageProcessor.java  | 56 ++++++++++++++++++
 .../sis/internal/coverage/j2d/ImageLayout.java     | 64 ++++++++++++++++-----
 .../sis/internal/coverage/j2d/PreferredSize.java   | 67 ----------------------
 5 files changed, 111 insertions(+), 84 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index e5ca996..b7b2f2a 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -40,7 +40,6 @@ import org.apache.sis.geometry.Shapes2D;
 import org.apache.sis.image.ImageProcessor;
 import org.apache.sis.internal.coverage.j2d.ColorModelType;
 import org.apache.sis.internal.coverage.j2d.ImageUtilities;
-import org.apache.sis.internal.coverage.j2d.PreferredSize;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.math.Statistics;
 import org.apache.sis.measure.Quantities;
@@ -188,6 +187,7 @@ final class RenderingData implements Cloneable {
         selectedDerivative = Stretching.NONE;
         processor = new ImageProcessor();
         processor.setErrorAction(ImageProcessor.ErrorAction.LOG);
+        processor.setImageResizingPolicy(ImageProcessor.Resizing.EXPAND);
     }
 
     /**
@@ -314,9 +314,9 @@ final class RenderingData implements Cloneable {
         displayToObjective = AffineTransforms2D.castOrCopy(inverse);
         final MathTransform cornerToDisplay = MathTransforms.concatenate(cornerToObjective,
objectiveToDisplay);
         final MathTransform displayToCenter = MathTransforms.concatenate(inverse, centerToObjective.inverse());
-        final PreferredSize bounds = (PreferredSize) Shapes2D.transform(
+        final Rectangle bounds = (Rectangle) Shapes2D.transform(
                 MathTransforms.bidimensional(cornerToDisplay),
-                ImageUtilities.getBounds(recoloredImage), new PreferredSize());
+                ImageUtilities.getBounds(recoloredImage), new Rectangle());
         /*
          * Apply a map projection on the image, then convert the floating point results to
integer values
          * that we can use with IndexColorModel.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java b/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java
index c503e0e..3b0d49c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageCombiner.java
@@ -94,7 +94,7 @@ public class ImageCombiner implements Consumer<RenderedImage> {
 
         /** Creates a new layout which will request the specified sample model. */
         Layout(final SampleModel sampleModel) {
-            super(null);
+            super(null, false);
             ArgumentChecks.ensureNonNull("sampleModel", sampleModel);
             this.sampleModel = sampleModel;
             minTile = new Point();
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
index eb95181..df2d310 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
@@ -66,6 +66,9 @@ import org.apache.sis.measure.Units;
  *     {@linkplain #setCategoryColors(Function) Category colors} for mapping sample values
  *     (identified by their range, name or unit of measurement) to colors.
  *   </li><li>
+ *     {@linkplain #setImageResizingPolicy(Resizing) Image resizing policy} to apply
+ *     if a requested image size prevent the image to be tiled.
+ *   </li><li>
  *     {@linkplain #setPositionalAccuracyHints(Quantity...) Positional accuracy hints}
  *     for enabling the use of faster algorithm when a lower accuracy is acceptable.
  *   </li><li>
@@ -154,6 +157,33 @@ public class ImageProcessor implements Cloneable {
     private ImageLayout layout;
 
     /**
+     * Whether {@code ImageProcessor} can produce an image of different size compared to
requested size.
+     * An image may be resized if the requested size can not be subdivided into tiles of
reasonable size.
+     * For example if the image width is a prime number, there is no way to divide the image
horizontally with
+     * an integer number of tiles. The only way to get an integer number of tiles is to change
the image size.
+     *
+     * <p>The image resizing policy may be used by any operation that involve a {@linkplain
#resample resampling}.
+     * If a resizing is applied, the new size will be written in the {@code bounds} argument
(a {@link Rectangle}).</p>
+     *
+     * @see #getImageResizingPolicy()
+     * @see #setImageResizingPolicy(Resizing)
+     */
+    public enum Resizing {
+        /**
+         * Image size is unmodified; the requested value is used unconditionally.
+         * It may result in big tiles (potentially a single tile for the whole image)
+         * if the image size is not divisible by a tile size.
+         */
+        NONE,
+
+        /**
+         * Image size can be increased. {@code ImageProcessor} will try to increase
+         * by the smallest amount of pixels allowing the image to be subdivided in tiles.
+         */
+        EXPAND
+    }
+
+    /**
      * Interpolation to use during resample operations.
      *
      * @see #getInterpolation()
@@ -382,6 +412,26 @@ public class ImageProcessor implements Cloneable {
     }
 
     /**
+     * Returns whether {@code ImageProcessor} can produce an image of different size compared
to requested size.
+     * If this processor can use a different size, the enumeration value specifies what kind
of changes may be applied.
+     *
+     * @return the image resizing policy.
+     */
+    public synchronized Resizing getImageResizingPolicy() {
+        return layout.isBoundsAdjustmentAllowed ? Resizing.EXPAND : Resizing.NONE;
+    }
+
+    /**
+     * Sets whether {@code ImageProcessor} can produce an image of different size compared
to requested size.
+     *
+     * @param  policy   the new image resizing policy.
+     */
+    public synchronized void setImageResizingPolicy(final Resizing policy) {
+        ArgumentChecks.ensureNonNull("policy", policy);
+        layout = (policy == Resizing.EXPAND) ? ImageLayout.SIZE_ADJUST : ImageLayout.DEFAULT;
+    }
+
+    /**
      * Returns hints about the desired positional accuracy, in "real world" units or in pixel
units.
      * This is an empty array by default, which means that {@code ImageProcessor} aims for
the best
      * accuracy it can produce. If the returned array is non-empty and contains accuracies
large enough,
@@ -787,12 +837,15 @@ public class ImageProcessor implements Cloneable {
      * <ul>
      *   <li>{@linkplain #getInterpolation() Interpolation method} (nearest neighbor,
bilinear, <i>etc</i>).</li>
      *   <li>{@linkplain #getFillValues() Fill values} for pixels outside source image.</li>
+     *   <li>{@linkplain #getImageResizingPolicy() Image resizing policy} to apply
+     *       if {@code bounds} size is not divisible by a tile size.</li>
      *   <li>{@linkplain #getPositionalAccuracyHints() Positional accuracy hints}
      *       for enabling faster resampling at the cost of lower precision.</li>
      * </ul>
      *
      * @param  source    the image to be resampled.
      * @param  bounds    domain of pixel coordinates of resampled image to create.
+     *                   Updated by this method if {@link Resizing#EXPAND} policy is applied.
      * @param  toSource  conversion of pixel coordinates from resampled image to {@code source}
image.
      * @return resampled image (may be {@code source}).
      */
@@ -983,6 +1036,8 @@ public class ImageProcessor implements Cloneable {
      * <ul>
      *   <li>{@linkplain #getInterpolation() Interpolation method} (nearest neighbor,
bilinear, <i>etc</i>).</li>
      *   <li>{@linkplain #getFillValues() Fill values} for pixels outside source image.</li>
+     *   <li>{@linkplain #getImageResizingPolicy() Image resizing policy} to apply
+     *       if {@code bounds} size is not divisible by a tile size.</li>
      *   <li>{@linkplain #getPositionalAccuracyHints() Positional accuracy hints}
      *       for enabling faster resampling at the cost of lower precision.</li>
      *   <li>{@linkplain #getCategoryColors() Category colors}.</li>
@@ -990,6 +1045,7 @@ public class ImageProcessor implements Cloneable {
      *
      * @param  source    the image to be resampled and recolored.
      * @param  bounds    domain of pixel coordinates of resampled image to create.
+     *                   Updated by this method if {@link Resizing#EXPAND} policy is applied.
      * @param  toSource  conversion of pixel coordinates from resampled image to {@code source}
image.
      * @param  ranges    description of {@code source} bands, or {@code null} if none. This
is typically
      *                   obtained by {@link org.apache.sis.coverage.grid.GridCoverage#getSampleDimensions()}.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
index 449b1de..dd1656a 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
@@ -55,7 +55,12 @@ public class ImageLayout {
      * The default instance which will target {@value ImageUtilities#DEFAULT_TILE_SIZE} pixels
as tile
      * width and height.
      */
-    public static final ImageLayout DEFAULT = new ImageLayout(null);
+    public static final ImageLayout DEFAULT = new ImageLayout(null, false);
+
+    /**
+     * Same as {@link #DEFAULT}, but makes image size an integer amount of tiles.
+     */
+    public static final ImageLayout SIZE_ADJUST = new ImageLayout(null, true);
 
     /**
      * Preferred size for tiles.
@@ -65,11 +70,23 @@ public class ImageLayout {
     private final int preferredTileWidth, preferredTileHeight;
 
     /**
+     * Whether image size can be modified if needed. Changes are applied only if an image
can not be tiled
+     * because {@link #suggestTileSize(int, int, boolean)} can not find a tile size close
to the desired size.
+     * For example if the image width is a prime number, there is no way to divide the image
horizontally with
+     * an integer number of tiles. The only way to get an integer number of tiles is to change
the image size.
+     *
+     * <p>If this flag is {@code true}, then the {@code bounds} argument given to the
+     * {@link #suggestTileSize(RenderedImage, Rectangle, boolean)} will be modified in-place.</p>
+     */
+    public final boolean isBoundsAdjustmentAllowed;
+
+    /**
      * Creates a new image layout.
      *
-     * @param  preferredTileSize  the preferred tile size, or {@code null} for the default
size.
+     * @param  preferredTileSize          the preferred tile size, or {@code null} for the
default size.
+     * @param  isBoundsAdjustmentAllowed  whether image size can be modified if needed.
      */
-    public ImageLayout(final Dimension preferredTileSize) {
+    public ImageLayout(final Dimension preferredTileSize, final boolean isBoundsAdjustmentAllowed)
{
         if (preferredTileSize != null) {
             preferredTileWidth  = preferredTileSize.width;
             preferredTileHeight = preferredTileSize.height;
@@ -77,6 +94,7 @@ public class ImageLayout {
             preferredTileWidth  = ImageUtilities.DEFAULT_TILE_SIZE;
             preferredTileHeight = ImageUtilities.DEFAULT_TILE_SIZE;
         }
+        this.isBoundsAdjustmentAllowed = isBoundsAdjustmentAllowed;
     }
 
     /**
@@ -189,11 +207,12 @@ public class ImageLayout {
      *
      * @param  image   the image for which to derive a tile size, or {@code null}.
      * @param  bounds  the bounds of the image to create, or {@code null} if same as {@code
image}.
+     * @param  allowPartialTiles  whether to allow tiles that are only partially filled.
+     *         This argument is ignored (reset to {@code false}) if the given image is opaque.
      * @return suggested tile size for the given image.
      */
-    public Dimension suggestTileSize(final RenderedImage image, final Rectangle bounds) {
-        boolean allowPartialTiles = (bounds instanceof PreferredSize);
-        if (allowPartialTiles && image != null) {
+    public Dimension suggestTileSize(final RenderedImage image, final Rectangle bounds, boolean
allowPartialTiles) {
+        if (allowPartialTiles && image != null && !isBoundsAdjustmentAllowed)
{
             final ColorModel cm = image.getColorModel();
             allowPartialTiles = (cm != null);
             if (allowPartialTiles) {
@@ -228,15 +247,33 @@ public class ImageLayout {
         final Dimension tileSize = new Dimension(
                 toTileSize(width,  preferredTileWidth,  allowPartialTiles & singleXTile),
                 toTileSize(height, preferredTileHeight, allowPartialTiles & singleYTile));
-        if (allowPartialTiles) {
-            ((PreferredSize) bounds).makeDivisible(tileSize);
+        /*
+         * Optionally adjust the image bounds for making it divisible by the tile size.
+         */
+        if (isBoundsAdjustmentAllowed && bounds != null && !bounds.isEmpty())
{
+            final int sx = sizeToAdd(bounds.width,  tileSize.width);
+            final int sy = sizeToAdd(bounds.height, tileSize.height);
+            if ((bounds.width  += sx) < 0) bounds.width  -= tileSize.width;     // if
(overflow) reduce to valid range.
+            if ((bounds.height += sy) < 0) bounds.height -= tileSize.height;
+            bounds.translate(-sx/2, -sy/2);
         }
         return tileSize;
     }
 
     /**
-     * Creates a banded sample model of the given type with a {@linkplain #suggestTileSize(RenderedImage,
Rectangle)
-     * the suggested size} for the given image.
+     * Computes the size to add to the width or height for making it divisible by the given
tile size.
+     */
+    private static int sizeToAdd(int size, final int tileSize) {
+        size %= tileSize;
+        if (size != 0) {
+            size = tileSize - size;
+        }
+        return size;
+    }
+
+    /**
+     * Creates a banded sample model of the given type with
+     * {@linkplain #suggestTileSize(RenderedImage, Rectangle, boolean) the suggested tile
size} for the given image.
      *
      * @param  type      desired data type as a {@link java.awt.image.DataBuffer} constant.
      * @param  numBands  desired number of bands.
@@ -247,7 +284,7 @@ public class ImageLayout {
     public BandedSampleModel createBandedSampleModel(final int type, final int numBands,
             final RenderedImage image, final Rectangle bounds)
     {
-        final Dimension tile = suggestTileSize(image, bounds);
+        final Dimension tile = suggestTileSize(image, bounds, isBoundsAdjustmentAllowed);
         return RasterFactory.unique(new BandedSampleModel(type, tile.width, tile.height,
numBands));
     }
 
@@ -265,7 +302,7 @@ public class ImageLayout {
      */
     public SampleModel createCompatibleSampleModel(final RenderedImage image, final Rectangle
bounds) {
         ArgumentChecks.ensureNonNull("image", image);
-        final Dimension tile = suggestTileSize(image, bounds);
+        final Dimension tile = suggestTileSize(image, bounds, isBoundsAdjustmentAllowed);
         SampleModel sm = image.getSampleModel();
         if (sm.getWidth() != tile.width || sm.getHeight() != tile.height) {
             sm = sm.createCompatibleSampleModel(tile.width, tile.height);
@@ -292,6 +329,7 @@ public class ImageLayout {
     @Override
     public String toString() {
         return Strings.toString(getClass(),
-                "preferredTileSize", new StringBuilder().append(preferredTileWidth).append('×').append(preferredTileHeight));
+                "preferredTileSize", new StringBuilder().append(preferredTileWidth).append('×').append(preferredTileHeight),
+                "isBoundsAdjustmentAllowed", isBoundsAdjustmentAllowed);
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/PreferredSize.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/PreferredSize.java
deleted file mode 100644
index 06f564d..0000000
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/PreferredSize.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.sis.internal.coverage.j2d;
-
-import java.awt.Dimension;
-import java.awt.Rectangle;
-
-
-/**
- * Specifies an image size which can be modified by {@link ImageLayout} if needed. Changes
are applied only if
- * an image can not be tiled because {@link ImageLayout} can not find a tile size close to
the desired size.
- * For example if the image width is a prime number, there is no way to divide the image
horizontally with
- * an integer number of tiles. The only way to get an integer number of tiles is to change
the image size.
- * The use of this class is understood by {@link ImageLayout} as a permission to do so.
- *
- * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
- * @since   1.1
- * @module
- */
-@SuppressWarnings("serial")     // Not intended to be serialized.
-public final class PreferredSize extends Rectangle {
-    /**
-     * Creates a new rectangle.
-     */
-    public PreferredSize() {
-    }
-
-    /**
-     * Adjusts the bounds for making it divisible by the given tile size.
-     */
-    final void makeDivisible(final Dimension tileSize) {
-        if (!isEmpty()) {
-            final int sx = sizeToAdd(width,   tileSize.width);
-            final int sy = sizeToAdd(height,  tileSize.height);
-            if ((width  += sx) < 0) width  -= tileSize.width;       // if (overflow) reduce
to valid range.
-            if ((height += sy) < 0) height -= tileSize.height;
-            if (x < (x -= sx/2)) x = Integer.MIN_VALUE;             // if (overflow) set
to minimal value.
-            if (y < (y -= sy/2)) y = Integer.MIN_VALUE;
-        }
-    }
-
-    /**
-     * Computes the size to add to the width or height for making it divisible by the given
tile size.
-     */
-    private static int sizeToAdd(int size, final int tileSize) {
-        size %= tileSize;
-        if (size != 0) {
-            size = tileSize - size;
-        }
-        return size;
-    }
-}


Mime
View raw message