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 2020/01/02 19:06:18 UTC
[sis] 01/02: Add ImageLayout as a helper class for setting the
tiling used by ComputedImage.
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 be0c05340245a9c5eca12f8bbc14ba46cae9efa4
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Jan 2 19:47:57 2020 +0100
Add ImageLayout as a helper class for setting the tiling used by ComputedImage.
---
.../sis/internal/coverage/j2d/ComputedImage.java | 96 ++++----
.../sis/internal/coverage/j2d/ImageFactory.java | 76 +++++++
.../sis/internal/coverage/j2d/ImageLayout.java | 242 +++++++++++++++++++++
.../sis/internal/coverage/j2d/ImageUtilities.java | 57 ++---
.../sis/internal/coverage/j2d/TileCache.java | 6 +-
.../sis/coverage/grid/GridCoverage2DTest.java | 4 +-
.../java/org/apache/sis/math/MathFunctions.java | 17 +-
7 files changed, 389 insertions(+), 109 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ComputedImage.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ComputedImage.java
index 8c371bf..99b3e5c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ComputedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ComputedImage.java
@@ -44,15 +44,21 @@ import org.apache.sis.util.Disposable;
*
* <p>Subclasses need to implement at least the following methods:</p>
* <ul>
- * <li>{@link #getWidth()}</li>
- * <li>{@link #getHeight()}</li>
- * <li>{@link #getTileWidth()}</li>
- * <li>{@link #getTileHeight()}</li>
- * <li>{@link #computeTile(int,int)}</li>
+ * <li>{@link #getWidth()} — the image width in pixels.</li>
+ * <li>{@link #getHeight()} — the image height in pixels.</li>
+ * <li>{@link #computeTile(int,int)} — invoked when a requested tile is not in the cache.</li>
* </ul>
*
- * <p>This class is thread-safe. Multiple tiles may be computed in
- * different background threads.</p>
+ * <p>If pixel coordinates or tile indices do not start at zero,
+ * then subclasses shall also override the following methods:</p>
+ * <ul>
+ * <li>{@link #getMinX()} — the minimum <var>x</var> coordinate (inclusive) of the image.</li>
+ * <li>{@link #getMinY()} — the minimum <var>y</var> coordinate (inclusive) of the image.</li>
+ * <li>{@link #getMinTileX()} — the minimum tile index in the <var>x</var> direction.</li>
+ * <li>{@link #getMinTileY()} — the minimum tile index in the <var>y</var> direction.</li>
+ * </ul>
+ *
+ * <p>This class is thread-safe: multiple tiles may be computed in different background threads.</p>
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.1
@@ -137,7 +143,13 @@ public abstract class ComputedImage extends PlanarImage {
/**
* Creates an initially empty image with the given sample model.
- * The tile size will be the width and height of the given sample model.
+ * The default tile size will be the width and height of the given sample model.
+ * The {@link ImageLayout#createCompatibleSampleModel(RenderedImage)} convenience method
+ * may be used for getting a sample model of desired size.
+ *
+ * <div class="note"><b>Note:</b>
+ * the restriction about sample model size matching tile size is for reducing the amount
+ * of memory consumed by {@link #createTile(int, int)}.</div>
*
* @param sampleModel the sample model shared by all tiles in this image.
* @param sources sources of this image (may be an empty array), or a null array if unknown.
@@ -147,56 +159,12 @@ public abstract class ComputedImage extends PlanarImage {
this.sampleModel = sampleModel;
if (sources != null) {
sources = sources.clone();
- this.sources = sources;
for (int i=0; i<sources.length; i++) {
ArgumentChecks.ensureNonNullElement("sources", i, sources[i]);
}
- } else {
- this.sources = null;
- }
- reference = new Cleaner(this);
- }
-
- /**
- * Creates an initially empty image with a sample model derived from the given image.
- * This constructor sets {@link #sampleModel} to a model compatible with the one used
- * by the given image, but with {@linkplain SampleModel#getWidth() width} and
- * {@linkplain SampleModel#getHeight() height} matching exactly the size of the tiles.
- *
- * <p>This constructor does <strong>not</strong> inherit other image properties.
- * In particular pixel coordinates and tile indices in this image start at (0,0)
- * unless subclass override {@link #getMinX()}, {@link #getMinY()}, {@link #getMinTileX()}
- * and {@link #getMinTileY()}.</p>
- *
- * @param image the main image from which to get tile size.
- * @param others additional sources, or {@code null} if none.
- */
- protected ComputedImage(final RenderedImage image, final RenderedImage... others) {
- ArgumentChecks.ensureNonNull("image", image);
- /*
- * Get a sample model compatible with the given one, but with the tile width and height.
- * We check if the given sample model can be used as-is and create a new one only if needed.
- * This restriction about sample model size matching tile size is for reducing the amount
- * of memory consumed by {@link #createTile(int, int)}.
- */
- final int width = image.getTileWidth();
- final int height = image.getTileHeight();
- SampleModel sm = image.getSampleModel();
- if (sm.getWidth() != width || sm.getHeight() != height) {
- sm = sm.createCompatibleSampleModel(width, height);
- }
- sampleModel = sm;
- if (others == null) {
- sources = new RenderedImage[] {image};
- } else {
- sources = new RenderedImage[others.length + 1];
- sources[0] = image;
- System.arraycopy(others, 0, sources, 1, others.length);
- for (int i=1; i<sources.length; i++) {
- ArgumentChecks.ensureNonNullElement("others", i-1, sources[i]);
- }
}
- reference = new Cleaner(this);
+ this.sources = sources; // Note: null value does not have same meaning than empty array.
+ reference = new Cleaner(this); // Create cleaner last after all arguments have been validated.
}
/**
@@ -282,7 +250,7 @@ public abstract class ComputedImage extends PlanarImage {
* @param tileX the column index of the tile to get.
* @param tileY the row index of the tile to get.
* @return the tile at the given index (never null).
- * @throws IndexOutOfBoundsException if a given tile index is out of bounds.
+ * @throws IllegalArgumentException if a given tile index is out of bounds.
* @throws ImagingOpException if an error occurred while computing the image.
*/
@Override
@@ -291,13 +259,24 @@ public abstract class ComputedImage extends PlanarImage {
final Cache<TileCache.Key,Raster> cache = TileCache.GLOBAL;
Raster tile = cache.peek(key);
if (tile == null) {
+ int min;
+ ArgumentChecks.ensureBetween("tileX", (min = getMinTileX()), min + getNumXTiles() - 1, tileX);
+ ArgumentChecks.ensureBetween("tileY", (min = getMinTileY()), min + getNumYTiles() - 1, tileY);
final Cache.Handler<Raster> handler = cache.lock(key);
try {
tile = handler.peek();
if (tile == null) {
- tile = computeTile(tileX, tileY);
+ Exception cause = null;
+ try {
+ tile = computeTile(tileX, tileY);
+ } catch (ImagingOpException e) {
+ throw e; // Let that kind of exception propagate.
+ } catch (Exception e) {
+ cause = e;
+ }
if (tile == null) {
- throw new ImagingOpException(Resources.format(Resources.Keys.CanNotComputeTile_2, tileX, tileY));
+ throw (ImagingOpException) new ImagingOpException(Resources.format(
+ Resources.Keys.CanNotComputeTile_2, tileX, tileY)).initCause(cause);
}
}
} finally {
@@ -314,8 +293,9 @@ public abstract class ComputedImage extends PlanarImage {
* @param tileX the column index of the tile to compute.
* @param tileY the row index of the tile to compute.
* @return computed tile for the given indices (can not be null).
+ * @throws Exception if an error occurred while computing the tile.
*/
- protected abstract Raster computeTile(int tileX, int tileY);
+ protected abstract Raster computeTile(int tileX, int tileY) throws Exception;
/**
* Creates an initially empty tile at the given tile grid position.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageFactory.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageFactory.java
new file mode 100644
index 0000000..8355838
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageFactory.java
@@ -0,0 +1,76 @@
+/*
+ * 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.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import org.apache.sis.util.Static;
+
+
+/**
+ * Convenience methods for creating new images.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public final class ImageFactory extends Static {
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private ImageFactory() {
+ }
+
+ /**
+ * Creates an opaque image with a gray scale color model. The image can have an arbitrary
+ * number of bands, but in current implementation only one band is used.
+ *
+ * <p><b>Warning:</b> displaying this image is very slow, except in a few special cases.
+ * It should be used only when no standard color model can be used.</p>
+ *
+ * @param dataType the color model type as one of {@code DataBuffer.TYPE_*} constants.
+ * @param width the desired image width.
+ * @param height the desired image height.
+ * @param numComponents the number of components.
+ * @param visibleBand the band to use for computing colors.
+ * @param minimum the minimal sample value expected.
+ * @param maximum the maximal sample value expected.
+ * @return the color space for the given range of values.
+ */
+ public static BufferedImage createGrayScale(final int dataType, final int width, final int height,
+ final int numComponents, final int visibleBand, final double minimum, final double maximum)
+ {
+ switch (dataType) {
+ case DataBuffer.TYPE_BYTE: {
+ if (numComponents == 1 && minimum <= 0 && maximum >= 0xFF) {
+ return new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+ }
+ break;
+ }
+ case DataBuffer.TYPE_USHORT: {
+ if (numComponents == 1 && minimum <= 0 && maximum >= 0xFFFF) {
+ return new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
+ }
+ break;
+ }
+ }
+ final ColorModel cm = ColorModelFactory.createGrayScale(DataBuffer.TYPE_INT, 1, 0, -10, 10);
+ return new BufferedImage(cm, cm.createCompatibleWritableRaster(width, height), false, null);
+ }
+}
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
new file mode 100644
index 0000000..1e29459
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageLayout.java
@@ -0,0 +1,242 @@
+/*
+ * 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.util.Arrays;
+import java.awt.Dimension;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import org.apache.sis.math.MathFunctions;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.internal.util.Strings;
+
+
+/**
+ * Derives information about image location, size and tile grid. {@code ImageLayout} does not store
+ * those information directly, but provides method for deriving those properties from a given image.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public class ImageLayout {
+ /**
+ * The minimum tile size. The {@link #toTileSize(int, int, boolean)} method will not suggest tiles
+ * smaller than this size. This size must be smaller than {@link ImageUtilities#DEFAULT_TILE_SIZE}.
+ *
+ * <p>Tiles of 180×180 pixels consume about 127 kB, assuming 4 bytes per pixel. This is about half
+ * the consumption of tiles of 256×256 pixels. We select a size which is a multiple of 90 because
+ * images are often used with a resolution of e.g. ½° per pixel.</p>
+ */
+ private static final int MIN_TILE_SIZE = 180;
+
+ /**
+ * 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, true);
+
+ /**
+ * Preferred size for tiles.
+ *
+ * @see ImageUtilities#DEFAULT_TILE_SIZE
+ */
+ private final int preferredTileWidth, preferredTileHeight;
+
+ /**
+ * Whether this instance allow tiles that are only partially filled. A value of {@code true} implies that
+ * tiles in the last row or in the last column may contain empty pixels. A value of {@code false} implies
+ * that this class will be unable to subdivide large images in smaller tiles if the image size is a prime
+ * number.
+ */
+ private final boolean allowPartialTiles;
+
+ /**
+ * Creates a new image layout.
+ *
+ * @param preferredTileSize the preferred tile size, or {@code null} for the default size.
+ * @param allowPartialTiles whether this instance allow tiles that are only partially filled.
+ */
+ protected ImageLayout(final Dimension preferredTileSize, final boolean allowPartialTiles) {
+ if (preferredTileSize != null) {
+ preferredTileWidth = preferredTileSize.width;
+ preferredTileHeight = preferredTileSize.height;
+ } else {
+ preferredTileWidth = ImageUtilities.DEFAULT_TILE_SIZE;
+ preferredTileHeight = ImageUtilities.DEFAULT_TILE_SIZE;
+ }
+ this.allowPartialTiles = allowPartialTiles;
+ }
+
+ /**
+ * Suggests a tile size close to {@code tileSize} for the specified {@code imageSize}.
+ * First, this method tries to return a tile size which is a divisor of the image size.
+ * If no such divisor is found and {@code allowPartialTiles} is {@code true}, then this
+ * method returns a size that minimize the amount of empty pixels in the last tile.
+ *
+ * @param imageSize the image size (width or height).
+ * @param preferredTileSize the preferred tile size, which is often {@value ImageUtilities#DEFAULT_TILE_SIZE}.
+ * @param allowPartialTiles whether to allow tiles that are only partially filled.
+ * @return the suggested tile size, or {@code imageSize} if none.
+ */
+ private static int toTileSize(final int imageSize, final int preferredTileSize, final boolean allowPartialTiles) {
+ if (imageSize <= 2*preferredTileSize) { // Factor 2 is arbitrary.
+ return imageSize;
+ }
+ int rmax = imageSize % preferredTileSize;
+ if (rmax == 0) return preferredTileSize;
+ /*
+ * Find tile sizes which are divisors of image size and select the one closest to desired size.
+ * Note: the (i >= 0) check is a paranoiac check redundant with (imageSize % tileSize == 0) check.
+ */
+ final int[] divisors = MathFunctions.divisors(imageSize);
+ int i = Arrays.binarySearch(divisors, preferredTileSize);
+ if (i >= 0) return divisors[i];
+ if ((i = ~i) < divisors.length) {
+ final int smaller = divisors[i];
+ final boolean tooSmall = (smaller < MIN_TILE_SIZE);
+ if (++i < divisors.length) {
+ final int larger = divisors[i];
+ if (larger < imageSize && (tooSmall || (larger - preferredTileSize) <= preferredTileSize - smaller)) {
+ return larger;
+ }
+ }
+ if (!tooSmall) {
+ return smaller;
+ }
+ }
+ /*
+ * Found no exact divisor. If we are allowed to return an approximated size,
+ * search the divisor which will minimize the amount of empty pixels.
+ */
+ int best = preferredTileSize;
+ if (allowPartialTiles) {
+ for (i = imageSize/2; --i >= MIN_TILE_SIZE;) {
+ final int r = imageSize % i;
+ if (r == 0) return i; // Should never happen since we checked divisors before, but be paranoiac.
+ if (r > rmax || (r == rmax && Math.abs(i - preferredTileSize) < Math.abs(best - preferredTileSize))) {
+ rmax = r;
+ best = i;
+ }
+ }
+ }
+ /*
+ * At this point `best` is an "optimal" tile size (the one that left as few empty pixels as possible),
+ * and `rmax` is the amount of non-empty pixels using this tile size. We will use that "optimal" size
+ * only if it fills at least 75% of the tile size. Otherwise, we arbitrarily consider that it doesn't
+ * worth to tile.
+ */
+ return (rmax >= preferredTileSize - preferredTileSize/4) ? best : imageSize;
+ }
+
+ /**
+ * Suggests a tile size for the specified image size. This method suggests a tile size which is a divisor
+ * of the given image size if possible, or a size that left as few empty pixels as possible otherwise.
+ *
+ * <p>The {@code allowPartialTile} argument should be {@code false} if the tiled image is opaque,
+ * or if the sample value for transparent pixels is different than zero. This restriction is for
+ * avoiding black or colored borders on the image left size and bottom size.</p>
+ *
+ * @param imageWidth the image width in pixels.
+ * @param imageHeight the image height in pixels.
+ * @param allowPartialTiles whether to allow tiles that are only partially filled.
+ * @return suggested tile size for the given image size.
+ */
+ public Dimension suggestTileSize(final int imageWidth, final int imageHeight, boolean allowPartialTiles) {
+ allowPartialTiles &= this.allowPartialTiles;
+ return new Dimension(toTileSize(imageWidth, preferredTileWidth, allowPartialTiles),
+ toTileSize(imageHeight, preferredTileHeight, allowPartialTiles));
+ }
+
+ /**
+ * Suggests a tile size for operations derived from the given image.
+ * If the given image is null, then this method returns the preferred tile size.
+ * Otherwise if the given image is already tiled, then this method preserves the
+ * current tile size unless the tiles are too large, in which case they may be subdivided.
+ * Otherwise (untiled image) this method proposes a tile size.
+ *
+ * <p>This method also checks whether the color model supports transparency. If not, then this
+ * method will not return a size that may result in the creation of partially empty tiles.</p>
+ *
+ * @param image the image for which to derive a tile size, or {@code null}.
+ * @return suggested tile size for the given image.
+ */
+ public Dimension suggestTileSize(final RenderedImage image) {
+ if (image == null) {
+ return new Dimension(preferredTileWidth, preferredTileHeight);
+ }
+ boolean pt = allowPartialTiles;
+ if (pt) {
+ final ColorModel cm = image.getColorModel();
+ if (cm != null) {
+ if (cm instanceof IndexColorModel) {
+ pt = ((IndexColorModel) cm).getTransparentPixel() == 0;
+ } else {
+ pt = cm.hasAlpha();
+ }
+ }
+ }
+ /*
+ * If the image is already tiled, we may select smaller tiles if the original tiles are too large
+ * but those smaller tiles must be divisors of the original size. This is necessary because image
+ * operations may assume that a call to `source.getTile(…)` will return a tile covering fully the
+ * tile to compute.
+ */
+ final boolean singleXTile = image.getNumXTiles() <= 1;
+ final boolean singleYTile = image.getNumYTiles() <= 1;
+ int width = singleXTile ? image.getWidth() : image.getTileWidth();
+ int height = singleYTile ? image.getHeight() : image.getTileHeight();
+ return new Dimension(toTileSize(width, preferredTileWidth, pt & singleXTile),
+ toTileSize(height, preferredTileHeight, pt & singleYTile));
+ }
+
+ /**
+ * Creates a sample model compatible with the sample model of the given image
+ * but with a size matching the preferred tile size. This method can be used
+ * for determining the {@code sampleModel} argument of {@link ComputedImage}
+ * constructor.
+ *
+ * @param image the image form which to get a sample model.
+ * @return image sample model with preferred tile size.
+ *
+ * @see ComputedImage#ComputedImage(SampleModel, RenderedImage...)
+ */
+ public SampleModel createCompatibleSampleModel(final RenderedImage image) {
+ ArgumentChecks.ensureNonNull("image", image);
+ final Dimension tile = suggestTileSize(image);
+ SampleModel sm = image.getSampleModel();
+ if (sm.getWidth() != tile.width || sm.getHeight() != tile.height) {
+ sm = sm.createCompatibleSampleModel(tile.width, tile.height);
+ }
+ return sm;
+ }
+
+ /**
+ * Returns a string representation for debugging purpose.
+ *
+ * @return a string representation for debugging purpose.
+ */
+ @Override
+ public String toString() {
+ return Strings.toString(getClass(),
+ "preferredTileSize", new StringBuilder().append(preferredTileWidth).append('×').append(preferredTileHeight),
+ "allowPartialTiles", allowPartialTiles);
+ }
+}
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 6cd8807..9648cfc 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
@@ -19,7 +19,6 @@ package org.apache.sis.internal.coverage.j2d;
import java.util.Arrays;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
-import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
@@ -31,6 +30,7 @@ import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import org.apache.sis.internal.feature.Resources;
import org.apache.sis.internal.system.Modules;
+import org.apache.sis.util.Static;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Vocabulary;
@@ -43,7 +43,7 @@ import org.apache.sis.util.resources.Vocabulary;
* @since 1.1
* @module
*/
-public final class ImageUtilities {
+public final class ImageUtilities extends Static {
/**
* Default width and height of tiles, in pixels.
*/
@@ -65,43 +65,6 @@ public final class ImageUtilities {
}
/**
- * Creates an opaque image with a gray scale color model. The image can have an arbitrary
- * number of bands, but in current implementation only one band is used.
- *
- * <p><b>Warning:</b> displaying this image is very slow, except in a few special cases.
- * It should be used only when no standard color model can be used.</p>
- *
- * @param dataType the color model type as one of {@code DataBuffer.TYPE_*} constants.
- * @param width the desired image width.
- * @param height the desired image height.
- * @param numComponents the number of components.
- * @param visibleBand the band to use for computing colors.
- * @param minimum the minimal sample value expected.
- * @param maximum the maximal sample value expected.
- * @return the color space for the given range of values.
- */
- public static BufferedImage createGrayScale(final int dataType, final int width, final int height,
- final int numComponents, final int visibleBand, final double minimum, final double maximum)
- {
- switch (dataType) {
- case DataBuffer.TYPE_BYTE: {
- if (numComponents == 1 && minimum <= 0 && maximum >= 0xFF) {
- return new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
- }
- break;
- }
- case DataBuffer.TYPE_USHORT: {
- if (numComponents == 1 && minimum <= 0 && maximum >= 0xFFFF) {
- return new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
- }
- break;
- }
- }
- final ColorModel cm = ColorModelFactory.createGrayScale(DataBuffer.TYPE_INT, 1, 0, -10, 10);
- return new BufferedImage(cm, cm.createCompatibleWritableRaster(width, height), false, null);
- }
-
- /**
* Returns the bounds of the given image as a new rectangle.
*
* @param image the image for which to get the bounds.
@@ -114,6 +77,22 @@ public final class ImageUtilities {
}
/**
+ * Returns the data type of the given raster.
+ *
+ * @param raster the raster for which to get the data type, or {@code null}.
+ * @return the data type, or {@link DataBuffer#TYPE_UNDEFINED} if unknown.
+ */
+ public static int getDataType(final Raster raster) {
+ if (raster != null) {
+ final DataBuffer buffer = raster.getDataBuffer();
+ if (buffer != null) {
+ return buffer.getDataType();
+ }
+ }
+ return DataBuffer.TYPE_UNDEFINED;
+ }
+
+ /**
* Names of {@link DataBuffer} types.
*/
private static final String[] TYPE_NAMES = new String[DataBuffer.TYPE_DOUBLE + 1];
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileCache.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileCache.java
index 41cd10e..6dbdb54 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileCache.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileCache.java
@@ -114,7 +114,11 @@ final class TileCache extends Cache<TileCache.Key, Raster> {
*/
@Override
public int hashCode() {
- return Long.hashCode(System.identityHashCode(image) + tileX + 65563L * tileY);
+ /*
+ * Dispatch tileX and tileY on approximately two halves of 32 bits integer.
+ * 65563 is a prime number close to 65536, the capacity of 16 bits integers.
+ */
+ return System.identityHashCode(image) + tileX + 65563 * tileY;
}
/**
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
index d3a1d64..f89d81f 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
@@ -27,7 +27,7 @@ import org.opengis.referencing.operation.MathTransform1D;
import org.opengis.referencing.datum.PixelInCell;
import org.apache.sis.coverage.SampleDimension;
import org.apache.sis.geometry.DirectPosition2D;
-import org.apache.sis.internal.coverage.j2d.ImageUtilities;
+import org.apache.sis.internal.coverage.j2d.ImageFactory;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.Units;
import org.apache.sis.referencing.crs.HardCodedCRS;
@@ -70,7 +70,7 @@ public final strictfp class GridCoverage2DTest extends TestCase {
* Create an image and set values directly as integers. We do not use one of the
* BufferedImage.TYPE_* constant because this test uses some negative values.
*/
- final BufferedImage image = ImageUtilities.createGrayScale(DataBuffer.TYPE_INT, size, size, 1, 0, -10, 10);
+ final BufferedImage image = ImageFactory.createGrayScale(DataBuffer.TYPE_INT, size, size, 1, 0, -10, 10);
final WritableRaster raster = image.getRaster();
raster.setSample(0, 0, 0, 2);
raster.setSample(1, 0, 0, 5);
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
index 51a7c96..fa69922 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
@@ -61,7 +61,7 @@ import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;
*
* @author Martin Desruisseaux (MPO, IRD, Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 1.0
+ * @version 1.1
*
* @see DecimalFunctions
* @see org.apache.sis.util.Numbers
@@ -750,8 +750,8 @@ public final class MathFunctions extends Static {
*/
return Double.NaN;
}
- exp -= (16383 - 1023); //change from 15 bias to 11 bias
- // Check cases where mantissa excess what double can support
+ exp -= (16383 - 1023); // Change from 15 bias to 11 bias.
+ // Check cases where mantissa excess what double can support.
if (exp < 0) return Double.NEGATIVE_INFINITY;
if (exp > 2046) return Double.POSITIVE_INFINITY;
@@ -852,12 +852,11 @@ testNextNumber: while (true) { // Simulate a "goto" statement (usua
int count = 1;
/*
* Searches for the first divisors among the prime numbers. We stop the search at the
- * square root of 'n' because every values above that point can be inferred from the
- * values before that point, i.e. if n=p1*p2 and p2 is greater than 'sqrt', than p1
- * most be lower than 'sqrt'.
+ * square root of `n` because every values above that point can be inferred from the
+ * values before that point, i.e. if n=p₁⋅p₂ and p₂ is greater than `sqrt`, than p₁
+ * must be lower than `sqrt`.
*/
- final int sqrt = (int) sqrt(number); // Really want rounding toward 0.
- for (int p,i=0; (p=primeNumberAt(i)) <= sqrt; i++) {
+ for (int p,i=0; (p=primeNumberAt(i))*p <= number; i++) {
if (number % p == 0) {
if (count == divisors.length) {
divisors = Arrays.copyOf(divisors, count*2);
@@ -866,7 +865,7 @@ testNextNumber: while (true) { // Simulate a "goto" statement (usua
}
}
/*
- * Completes the divisors past 'sqrt'. The numbers added here may or may not be prime
+ * Completes the divisors past `sqrt`. The numbers added here may or may not be prime
* numbers. Side note: checking that they are prime numbers would be costly, but this
* algorithm doesn't need that.
*/