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 cd3eb5eaf8377414343ef631a09db3c3a1531987 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Jan 22 11:07:46 2025 +0100 Move `ReshapedImage` to an internal package for reuse by other modules, and add a constructor for creating a translated image. It will be needed for work on aggregation in later commit. --- .../sis/coverage/grid/BufferedGridCoverage.java | 4 +- .../apache/sis/coverage/grid/GridCoverage2D.java | 23 +++++----- .../grid => image/privy}/ReshapedImage.java | 51 +++++++++++++++++----- .../coverage/grid/ResampledGridCoverageTest.java | 1 + .../grid => image/privy}/ReshapedImageTest.java | 12 ++--- 5 files changed, 60 insertions(+), 31 deletions(-) diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/BufferedGridCoverage.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/BufferedGridCoverage.java index 098515b9c8..e4105667ea 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/BufferedGridCoverage.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/BufferedGridCoverage.java @@ -166,7 +166,7 @@ public class BufferedGridCoverage extends GridCoverage { final long expectedSize = getSampleCount(extent, numBands); final long bufferSize = Math.multiplyFull(data.getSize(), numBanks); if (bufferSize < expectedSize) { - final StringBuilder b = new StringBuilder(); + final var b = new StringBuilder(); for (int i=0; i < extent.getDimension(); i++) { if (i != 0) b.append(" × "); b.append(extent.getSize(i)); @@ -254,7 +254,7 @@ public class BufferedGridCoverage extends GridCoverage { } try { return cachedRenderings.computeIfAbsent(sliceExtent, (slice) -> { - ImageRenderer renderer = new ImageRenderer(this, slice); + var renderer = new ImageRenderer(this, slice); renderer.setData(data); return renderer.createImage(); }); diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java index 90753bb89a..e3bd525acd 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java @@ -45,6 +45,7 @@ import org.opengis.referencing.operation.MathTransform1D; import org.apache.sis.image.DataType; import org.apache.sis.coverage.SampleDimension; import org.apache.sis.image.privy.ImageUtilities; +import org.apache.sis.image.privy.ReshapedImage; import org.apache.sis.feature.internal.Resources; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.Debug; @@ -90,7 +91,7 @@ import org.opengis.coverage.PointOutsideCoverageException; * @author Martin Desruisseaux (Geomatys) * @author Johann Sorel (Geomatys) * @author Alexis Manin (Geomatys) - * @version 1.4 + * @version 1.5 * @since 1.1 */ public class GridCoverage2D extends GridCoverage { @@ -274,7 +275,7 @@ public class GridCoverage2D extends GridCoverage { */ private static RenderedImage unwrapIfSameSize(RenderedImage data) { if (data instanceof ReshapedImage) { - final RenderedImage source = ((ReshapedImage) data).source; + final var source = ((ReshapedImage) data).source; if (source.getWidth() == data.getWidth() && source.getHeight() == data.getHeight()) { data = source; } @@ -347,8 +348,8 @@ public class GridCoverage2D extends GridCoverage { * @param crs coordinate reference system, or {@code null} if none. */ private static GridExtent createExtent(final int dimension, final Rectangle bounds, final CoordinateReferenceSystem crs) { - final long[] low = new long[dimension]; - final long[] high = new long[dimension]; + final var low = new long[dimension]; + final var high = new long[dimension]; low [0] = bounds.x; low [1] = bounds.y; high[0] = bounds.width + low[0] - 1; // Inclusive. @@ -588,11 +589,11 @@ public class GridCoverage2D extends GridCoverage { /* * Convert the coordinates from this grid coverage coordinate system to the image coordinate system. * The coverage coordinates may require 64 bits integers, but after translation the (x,y) coordinates - * should be in 32 bits integers range. Do not cast to 32 bits now however; this will be done later. + * should be in 32 bits integers range. Do not cast to 32 bits now however, this will be done later. */ final long xmin = addExact(sliceExtent.getLow (xDimension), gridToImageX); final long ymin = addExact(sliceExtent.getLow (yDimension), gridToImageY); - final long xmax = addExact(sliceExtent.getHigh(xDimension), gridToImageX); + final long xmax = addExact(sliceExtent.getHigh(xDimension), gridToImageX); // Inclusive final long ymax = addExact(sliceExtent.getHigh(yDimension), gridToImageY); /* * BufferedImage.getSubimage() returns a new image with upper-left coordinate at (0,0), @@ -600,7 +601,7 @@ public class GridCoverage2D extends GridCoverage { * upper-left point is inside the image. */ if (data instanceof BufferedImage) { - BufferedImage result = (BufferedImage) data; + var result = (BufferedImage) data; /* * BufferedImage origin should be (0, 0). But for consistency with image API, * we consider it as variable. @@ -645,7 +646,7 @@ public class GridCoverage2D extends GridCoverage { * with Raster.createChild(…), but that would force us to invoke RenderedImage.getTile(…) which * may force data loading earlier than desired. */ - final ReshapedImage result = new ReshapedImage(data, xmin, ymin, xmax, ymax); + final var result = new ReshapedImage(data, xmin, ymin, xmax, ymax); return result.isIdentity() ? data : result; } catch (ArithmeticException e) { throw new CannotEvaluateException(e.getMessage(), e); @@ -665,9 +666,9 @@ public class GridCoverage2D extends GridCoverage { void appendDataLayout(final TreeTable.Node root, final Vocabulary vocabulary, final TableColumn<CharSequence> column) { final TreeTable.Node branch = root.newChild(); branch.setValue(column, vocabulary.getString(Vocabulary.Keys.ImageLayout)); - final NumberFormat nf = NumberFormat.getIntegerInstance(vocabulary.getLocale()); - final FieldPosition pos = new FieldPosition(0); - final StringBuffer buffer = new StringBuffer(); + final var nf = NumberFormat.getIntegerInstance(vocabulary.getLocale()); + final var pos = new FieldPosition(0); + final var buffer = new StringBuffer(); write: for (int item=0; ; item++) try { switch (item) { case 0: { diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ReshapedImage.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ReshapedImage.java similarity index 87% rename from endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ReshapedImage.java rename to endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ReshapedImage.java index 2091cd364a..f131176aae 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ReshapedImage.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ReshapedImage.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.sis.coverage.grid; +package org.apache.sis.image.privy; import java.util.Vector; import java.util.Objects; @@ -35,18 +35,18 @@ import org.apache.sis.image.PlanarImage; /** * A view over another image with the origin relocated to a new position. - * Only the pixel coordinates are changed; the tile indices stay the same. + * Only the pixel coordinates are changed, the tile indices stay the same. * However, the image view may expose less tiles than the wrapped image. * This wrapper does not change image size otherwise than by an integer number of tiles. * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) */ -final class ReshapedImage extends PlanarImage { +public final class ReshapedImage extends PlanarImage { /** * The image to translate. */ - final RenderedImage source; + public final RenderedImage source; /** * Value to add for converting a column index from the coordinate system of the wrapped image @@ -79,6 +79,33 @@ final class ReshapedImage extends PlanarImage { */ private final int minTileX, minTileY; + /** + * Creates an image with the data of the given image translated by the given amount. + * The number of tiles and their indexes are unchanged. + * + * @param source the image to translate. + * @param tx the translation to apply on <var>x</var> coordinates. + * @param ty the translation to apply on <var>y</var> coordinates. + * @throws ArithmeticException if image indices would overflow 32-bits integer capacity. + */ + public ReshapedImage(RenderedImage source, long tx, long ty) { + while (source instanceof ReshapedImage) { + final var r = (ReshapedImage) source; + tx = addExact(r.offsetX, tx); + ty = addExact(r.offsetY, ty); + source = r.source; + } + this.source = source; + offsetX = toIntExact(tx); + offsetY = toIntExact(ty); + minX = toIntExact(tx + source.getMinX()); + minY = toIntExact(ty + source.getMinY()); + width = source.getWidth(); + height = source.getHeight(); + minTileX = source.getMinTileX(); + minTileY = source.getMinTileY(); + } + /** * Creates a new image with the same data as the given image but located at different coordinates. * In addition, this constructor can reduce the number of tiles. @@ -88,9 +115,9 @@ final class ReshapedImage extends PlanarImage { * @param ymin minimal <var>y</var> coordinate of the requested region, inclusive. * @param xmax maximal <var>x</var> coordinate of the requested region, inclusive. * @param ymax maximal <var>y</var> coordinate of the requested region, inclusive. - * @throws ArithmeticException if image indices would overflow 32 bits integer capacity. + * @throws ArithmeticException if image indices would overflow 32-bits integer capacity. */ - ReshapedImage(final RenderedImage source, final long xmin, final long ymin, final long xmax, final long ymax) { + public ReshapedImage(final RenderedImage source, final long xmin, final long ymin, final long xmax, final long ymax) { this.source = source; /* * Compute indices of all tiles to retain in this image. All local fields are `long` in order to force @@ -106,14 +133,14 @@ final class ReshapedImage extends PlanarImage { final long th = source.getTileHeight(); final long xo = source.getTileGridXOffset(); final long yo = source.getTileGridYOffset(); - final long minTX = floorDiv(max(lowerX, xmin) - xo, tw); // Indices of the first tile to retain. - final long minTY = floorDiv(max(lowerY, ymin) - yo, th); - final long maxTX = floorDiv(min(upperX, xmax) - xo, tw); // Indices of the last tile to retain (inclusive). - final long maxTY = floorDiv(min(upperY, ymax) - yo, th); + final long minTX = floorDiv(max(xmin, lowerX) - xo, tw); // Indices of the first tile to retain. + final long minTY = floorDiv(max(ymin, lowerY) - yo, th); + final long maxTX = floorDiv(min(xmax, upperX-1) - xo, tw); // Indices of the last tile to retain (inclusive). + final long maxTY = floorDiv(min(ymax, upperY-1) - yo, th); /* * Coordinates in source image of the first pixel to show in this relocated image. * They are the coordinates of the upper-left corner of the first tile to retain, - * clamped to image bounds if needed. This is not yet coordinates of this image. + * clamped to image bounds if needed. This is not yet the coordinates of this image. */ final long sx = max(lowerX, minTX * tw + xo); final long sy = max(lowerY, minTY * th + yo); @@ -137,7 +164,7 @@ final class ReshapedImage extends PlanarImage { /** * Returns {@code true} if this image does not move and does not subset the wrapped image. */ - final boolean isIdentity() { + public boolean isIdentity() { // The use of >= is a paranoiac check, but the > case should never happen actually. return offsetX == 0 && offsetY == 0 && width >= source.getWidth() && height >= source.getHeight(); } diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java index 7fbf205b5f..f0530b7ddb 100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java @@ -37,6 +37,7 @@ import org.apache.sis.geometry.DirectPosition2D; import org.apache.sis.geometry.ImmutableEnvelope; import org.apache.sis.image.Interpolation; import org.apache.sis.image.privy.TiledImage; +import org.apache.sis.image.privy.ReshapedImage; import org.apache.sis.referencing.CommonCRS; import org.apache.sis.referencing.privy.Formulas; import org.apache.sis.referencing.privy.AffineTransform2D; diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ReshapedImageTest.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ReshapedImageTest.java similarity index 94% rename from endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ReshapedImageTest.java rename to endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ReshapedImageTest.java index 6bc4c17ba0..c2aa208379 100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ReshapedImageTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ReshapedImageTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.sis.coverage.grid; +package org.apache.sis.image.privy; import java.util.Random; import java.awt.image.DataBuffer; @@ -145,7 +145,7 @@ public final class ReshapedImageTest extends TestCase { */ @Test public void testMultiTiles() { - final Random random = TestUtilities.createRandomNumberGenerator(219970242558564L); + final Random random = TestUtilities.createRandomNumberGenerator(); final int dataMinX, dataMinY; dataMinX = random.nextInt(20) - 10; dataMinY = random.nextInt(20) - 10; @@ -155,9 +155,9 @@ public final class ReshapedImageTest extends TestCase { numYTiles = 4; width = numXTiles * TILE_WIDTH; height = numYTiles * TILE_HEIGHT; - final TiledImageMock data = new TiledImageMock(DataBuffer.TYPE_USHORT, 1, dataMinX, dataMinY, - width, height, TILE_WIDTH, TILE_HEIGHT, minTileX, minTileY, - random.nextBoolean()); // Banded or interleaved sample model + final var data = new TiledImageMock(DataBuffer.TYPE_USHORT, 1, dataMinX, dataMinY, + width, height, TILE_WIDTH, TILE_HEIGHT, minTileX, minTileY, + random.nextBoolean()); // Banded or interleaved sample model data.validate(); data.initializeAllTiles(0); /* @@ -165,7 +165,7 @@ public final class ReshapedImageTest extends TestCase { */ tileXOffset = (minX = 7) - minTileX * TILE_WIDTH; tileYOffset = (minY = 13) - minTileY * TILE_HEIGHT; - ReshapedImage image = new ReshapedImage(data, dataMinX - 7, dataMinY - 13, 100, 100); + var image = new ReshapedImage(data, dataMinX - 7, dataMinY - 13, 100, 100); verifyLayout(image); assertValuesEqual(image.getData(), 0, new int[][] { { 100, 101, 102 , 200, 201, 202 , 300, 301, 302 , 400, 401, 402 , 500, 501, 502},