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 b372f8748d83d2372892821ee1eaf7535af1c2b3 Author: Martin Desruisseaux <[email protected]> AuthorDate: Sat Jan 4 14:04:33 2020 +0100 Consolidation of Raster/ColorModel/SampleModel factories. --- .../java/org/apache/sis/image/ComputedImage.java | 3 +- .../java/org/apache/sis/image/PlanarImage.java | 18 ++--- .../coverage/j2d/BandedSampleConverter.java | 2 +- .../internal/coverage/j2d/ColorModelFactory.java | 25 ++++++- .../sis/internal/coverage/j2d/ImageFactory.java | 76 ---------------------- .../sis/internal/coverage/j2d/ImageUtilities.java | 2 + .../coverage/j2d/MultiBandsIndexColorModel.java | 2 +- .../sis/internal/coverage/j2d/RasterFactory.java | 70 ++++++++++++++++++-- .../internal/coverage/j2d/ScaledColorSpace.java | 45 ++++++++++++- .../sis/coverage/grid/GridCoverage2DTest.java | 4 +- 10 files changed, 143 insertions(+), 104 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 8f728f2..aa6f19c 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 @@ -100,7 +100,8 @@ public abstract class ComputedImage extends PlanarImage { * Invoked when the enclosing image has been garbage-collected. This method removes all cached tiles * that were owned by the enclosing image. This method should not perform other cleaning work than * removing cached tiles because it is not guaranteed to be invoked if {@link TileCache#GLOBAL} - * does not contain any tile for the enclosing image. + * does not contain any tile for the enclosing image (because there would be nothing preventing + * this weak reference to be garbage collected before {@code dispose()} is invoked). */ @Override public void dispose() { diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java index 494009d..f7a4b5d 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java +++ b/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java @@ -18,7 +18,6 @@ package org.apache.sis.image; import java.awt.Image; import java.awt.Rectangle; -import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.SampleModel; @@ -26,12 +25,12 @@ import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.util.Vector; -import org.apache.sis.internal.util.Numerics; -import org.apache.sis.util.resources.Errors; -import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.Classes; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.resources.Errors; +import org.apache.sis.internal.util.Numerics; import org.apache.sis.internal.coverage.j2d.ImageUtilities; -import org.apache.sis.internal.coverage.j2d.ScaledColorSpace; +import org.apache.sis.internal.coverage.j2d.ColorModelFactory; /** @@ -432,14 +431,7 @@ colors: if (cm != null) { if (cm instanceof IndexColorModel) { buffer.append(((IndexColorModel) cm).getMapSize()).append(" indexed colors"); } else { - final ColorSpace cs = cm.getColorSpace(); - if (cs != null) { - if (cs instanceof ScaledColorSpace) { - ((ScaledColorSpace) cs).formatRange(buffer.append("showing ")); - } else if (cs.getType() == ColorSpace.TYPE_GRAY) { - buffer.append("grayscale"); - } - } + ColorModelFactory.formatDescription(cm.getColorSpace(), buffer); } final String transparency; switch (cm.getTransparency()) { diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java index e5c4a1d..4f87725 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java @@ -89,7 +89,7 @@ public final class BandedSampleConverter extends ComputedImage { layout = ImageLayout.DEFAULT; } final Dimension tile = layout.suggestTileSize(source); - return new BandedSampleModel(targetType, tile.width, tile.height, numBands); + return RasterFactory.unique(new BandedSampleModel(targetType, tile.width, tile.height, numBands)); } /** diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java index c784929..39a332c 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java @@ -36,6 +36,7 @@ import org.apache.sis.util.ArraysExt; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.collection.WeakHashSet; import org.apache.sis.util.collection.WeakValueHashMap; +import org.apache.sis.util.Debug; /** @@ -63,7 +64,7 @@ public final class ColorModelFactory { * * @see #unique(ColorModel) */ - @SuppressWarnings("rawtypes") + @SuppressWarnings("rawtypes") // TODO: Remove after we removed ColorModelPatch. private static final WeakHashSet<ColorModelPatch> CACHE = new WeakHashSet<>(ColorModelPatch.class); /** @@ -396,7 +397,7 @@ public final class ColorModelFactory { * @param maximum the maximal sample value expected. * @return the color space for the given range of values. * - * @see ImageFactory#createGrayScale(int, int, int, int, int, double, double) + * @see RasterFactory#createGrayScaleImage(int, int, int, int, int, double, double) */ public static ColorModel createGrayScale(final int dataType, final int numComponents, final int visibleBand, final double minimum, final double maximum) @@ -421,7 +422,25 @@ public final class ColorModelFactory { if (numComponents == 1 && minimum == 0 && maximum == 1) { return ColorSpace.getInstance(ColorSpace.CS_GRAY); } - return new ScaledColorSpace(numComponents, visibleBand, minimum, maximum); + return new ScaledColorSpace(numComponents, visibleBand, minimum, maximum).unique(); + } + + /** + * Appends a description of the given color space in the given buffer. + * This is used for {@code toString()} method implementations. + * + * @param cs the color space to describe, or {@code null}. + * @param buffer where to append the description. + */ + @Debug + public static void formatDescription(final ColorSpace cs, final StringBuilder buffer) { + if (cs != null) { + if (cs instanceof ScaledColorSpace) { + ((ScaledColorSpace) cs).formatRange(buffer.append("showing ")); + } else if (cs.getType() == ColorSpace.TYPE_GRAY) { + buffer.append("grayscale"); + } + } } /** 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 deleted file mode 100644 index 8355838..0000000 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageFactory.java +++ /dev/null @@ -1,76 +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.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/ImageUtilities.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java index 9648cfc..437b60b 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 @@ -37,6 +37,8 @@ import org.apache.sis.util.resources.Vocabulary; /** * Utility methods related to images and their color model or sample model. + * Those methods only fetch information, they do not create new rasters or sample/color models + * (see {@code *Factory} classes for creating those objects). * * @author Martin Desruisseaux (Geomatys) * @version 1.1 diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java index 404ffa3..988be37 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/MultiBandsIndexColorModel.java @@ -207,7 +207,7 @@ final class MultiBandsIndexColorModel extends IndexColorModel { */ @Override public SampleModel createCompatibleSampleModel(final int width, final int height) { - return new BandedSampleModel(transferType, width, height, numBands); + return RasterFactory.unique(new BandedSampleModel(transferType, width, height, numBands)); } /** diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java index 2cdcea3..527e69a 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java @@ -17,8 +17,11 @@ package org.apache.sis.internal.coverage.j2d; import java.awt.Point; +import java.awt.image.ColorModel; +import java.awt.image.SampleModel; import java.awt.image.BandedSampleModel; import java.awt.image.ComponentSampleModel; +import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferDouble; @@ -26,10 +29,9 @@ import java.awt.image.DataBufferFloat; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; -import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.RasterFormatException; -import java.awt.image.SampleModel; import java.awt.image.WritableRaster; +import java.awt.image.BufferedImage; import java.nio.Buffer; import java.nio.ReadOnlyBufferException; import org.apache.sis.internal.feature.Resources; @@ -38,24 +40,70 @@ import org.apache.sis.util.ArraysExt; import org.apache.sis.util.Numbers; import org.apache.sis.util.Static; import org.apache.sis.util.Workaround; +import org.apache.sis.util.collection.WeakHashSet; /** - * Creates rasters from given properties. + * Creates rasters from given properties. Contains also convenience methods for + * creating {@link BufferedImage} since that kind of images wraps a single raster. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.0 + * @version 1.1 * @since 1.0 * @module */ public final class RasterFactory extends Static { /** + * Shared instances of {@link SampleModel}s. + * + * @see #unique(SampleModel) + */ + private static final WeakHashSet<SampleModel> POOL = new WeakHashSet<>(SampleModel.class); + + /** * Do not allow instantiation of this class. */ private RasterFactory() { } /** + * 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 createGrayScaleImage(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); + } + + /** * Wraps the given data buffer in a raster. * The sample model type is selected according the number of bands and the pixel stride. * The number of bands is determined by {@code bandOffsets.length}, which should be one of followings: @@ -152,7 +200,7 @@ public final class RasterFactory extends Static { model = new ComponentSampleModel(dataType, width, height, pixelStride, scanlineStride, bankIndices, bandOffsets); } } - return WritableRaster.createWritableRaster(model, buffer, location); + return WritableRaster.createWritableRaster(unique(model), buffer, location); } /** @@ -224,4 +272,16 @@ public final class RasterFactory extends Static { default: return null; } } + + /** + * Returns a unique instance of the given sample model. This method can be invoked after a new sample + * has been created in order to share the same instance for many similar {@code Raster} instances. + * + * @param <T> the type of the given {@code sampleModel}. + * @param sampleModel the sample model to make unique. + * @return a unique instance of the given sample model. May be {@code sampleModel} itself. + */ + static <T extends SampleModel> T unique(final T sampleModel) { + return POOL.unique(sampleModel); + } } diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java index 0ee9e58..0e97622 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorSpace.java @@ -17,7 +17,9 @@ package org.apache.sis.internal.coverage.j2d; import java.awt.color.ColorSpace; +import org.apache.sis.internal.util.Numerics; import org.apache.sis.util.Debug; +import org.apache.sis.util.collection.WeakHashSet; /** @@ -36,13 +38,18 @@ import org.apache.sis.util.Debug; * @since 1.0 * @module */ -public final class ScaledColorSpace extends ColorSpace { +final class ScaledColorSpace extends ColorSpace { /** * For cross-version compatibility. */ private static final long serialVersionUID = 438226855772441165L; /** + * Shared instances of {@link ScaledColorSpace}s. + */ + private static final WeakHashSet<ScaledColorSpace> POOL = new WeakHashSet<>(ScaledColorSpace.class); + + /** * Minimal normalized RGB value. */ private static final float MIN_VALUE = 0f; @@ -69,6 +76,7 @@ public final class ScaledColorSpace extends ColorSpace { /** * Creates a color model for the given range of values. + * Callers should invoke {@link #unique()} on the newly created instance. * * @param numComponents the number of components. * @param visibleBand the band to use for computing colors. @@ -181,9 +189,42 @@ public final class ScaledColorSpace extends ColorSpace { * @param buffer where to append the range of values. */ @Debug - public final void formatRange(final StringBuilder buffer) { + final void formatRange(final StringBuilder buffer) { buffer.append('[').append(getMinValue(visibleBand)) .append(" … ").append(getMaxValue(visibleBand)) .append(" in band ").append(visibleBand).append(']'); } + + /** + * Returns a unique instance of this color space. May be {@code this}. + */ + final ScaledColorSpace unique() { + return POOL.unique(this); + } + + /** + * Returns a hash code value for this color model. + * Defined for implementation of {@link #unique()}. + */ + @Override + public int hashCode() { + return Float.floatToIntBits(scale) + 31 * Float.floatToIntBits(offset) + 7 * getNumComponents() + visibleBand; + } + + /** + * Compares this color space with the given object for equality. + * Defined for implementation of {@link #unique()}. + */ + @Override + public boolean equals(final Object obj) { + if (obj instanceof ScaledColorSpace) { + final ScaledColorSpace that = (ScaledColorSpace) obj; + return Numerics.equals(scale, that.scale) && + Numerics.equals(offset, that.offset) && + visibleBand == that.visibleBand && + getNumComponents() == that.getNumComponents() && + getType() == that.getType(); + } + return false; + } } 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 f89d81f..3841c68 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.ImageFactory; +import org.apache.sis.internal.coverage.j2d.RasterFactory; 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 = ImageFactory.createGrayScale(DataBuffer.TYPE_INT, size, size, 1, 0, -10, 10); + final BufferedImage image = RasterFactory.createGrayScaleImage(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);
