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