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 8018b2255165b4ac93a172a77105ad406e9c53eb
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Dec 24 12:11:37 2024 +0100

    Remove the `ColorModel` property from `ImageLayout` for avoiding an overlap 
with `Colorizer`.
---
 .../org/apache/sis/image/BandAggregateLayout.java  |  2 +-
 .../main/org/apache/sis/image/Colorizer.java       | 28 ++++++-
 .../main/org/apache/sis/image/ImageLayout.java     | 98 ++++++----------------
 .../main/org/apache/sis/image/ImageOverlay.java    | 15 ++--
 .../main/org/apache/sis/image/ImageProcessor.java  |  5 +-
 5 files changed, 68 insertions(+), 80 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandAggregateLayout.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandAggregateLayout.java
index d17e6c2164..7b5ae49354 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandAggregateLayout.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandAggregateLayout.java
@@ -229,7 +229,7 @@ final class BandAggregateLayout extends ImageLayout {
             final Rectangle domain, final Dimension preferredTileSize, final 
boolean exactTileSize,
             final Point minTile, final int commonDataType, final int numBands, 
final int scanlineStride)
     {
-        super(null, null, preferredTileSize, !exactTileSize, false, false, 
minTile);
+        super(null, preferredTileSize, !exactTileSize, false, false, minTile);
         this.bandsPerSource = bandsPerSource;
         this.bandSelect     = bandSelect;
         this.sources        = sources;
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Colorizer.java 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Colorizer.java
index 18a8f28754..be69136e2f 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Colorizer.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Colorizer.java
@@ -34,8 +34,10 @@ import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.privy.ColorModelBuilder;
 import org.apache.sis.coverage.privy.ColorScaleBuilder;
 import org.apache.sis.coverage.privy.ColorModelFactory;
+import org.apache.sis.coverage.privy.ImageUtilities;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.privy.UnmodifiableArrayList;
 
 
 /**
@@ -63,7 +65,7 @@ public interface Colorizer extends Function<Colorizer.Target, 
Optional<ColorMode
      * such as the {@link SampleDimension}s of the target coverage.
      *
      * @author  Martin Desruisseaux (Geomatys)
-     * @version 1.4
+     * @version 1.5
      * @since   1.4
      */
     class Target {
@@ -107,6 +109,30 @@ public interface Colorizer extends 
Function<Colorizer.Target, Optional<ColorMode
             this.visibleBand = -1;
         }
 
+
+        /**
+         * Creates a new target with the same sample dimensions and visible 
band as the given image.
+         * This is a convenience constructor for operations producing the same 
kind of data than an
+         * existing image, taken as a template. The list of sample dimensions 
is fetched from the
+         * image property associated to the {@value 
PlanarImage#SAMPLE_DIMENSIONS_KEY} key.
+         *
+         * @param model     sample model of the computed image to colorize 
(mandatory).
+         * @param template  the image from which to get the sample dimensions 
and visible band, or {@code null} if none.
+         * @since 1.5
+         */
+        public Target(SampleModel model, final RenderedImage template) {
+            this.model  = Objects.requireNonNull(model);
+            visibleBand = ImageUtilities.getVisibleBand(template);
+            if (template != null) {
+                final Object value = 
template.getProperty(PlanarImage.SAMPLE_DIMENSIONS_KEY);
+                if (value instanceof SampleDimension[]) {
+                    ranges = UnmodifiableArrayList.wrap((SampleDimension[]) 
value);
+                    return;
+                }
+            }
+            ranges = null;
+        }
+
         /**
          * Returns the sample model of the computed image to colorize.
          * The color model created by {@link #apply(Target)}
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageLayout.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageLayout.java
index a6a457b425..2aa8e81412 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageLayout.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageLayout.java
@@ -47,6 +47,10 @@ import org.apache.sis.feature.internal.Resources;
  * may be replaced by the tile size {@linkplain #suggestTileSize(int, int) 
computed}
  * by this {@code ImageLayout} class.</p>
  *
+ * <p>This class contains no information about colors.
+ * The {@link ColorModel} to associate with a {@link SampleModel}
+ * is controlled by a separated interface: {@link Colorizer}.</p>
+ *
  * <p>Instances of this class are immutable and thread-safe.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
@@ -79,17 +83,13 @@ public class ImageLayout {
      * Image sizes are preserved but the tile sizes are flexible. The last row 
and last column of tiles
      * in an image are allowed to be only partially filled.
      */
-    public static final ImageLayout DEFAULT = new ImageLayout(null, null, 
null, true, false, true, null);
-
-    /**
-     * Preferred color model, or {@code null} if none. If no {@link 
#sampleModel} is specified, it may
-     * be {@linkplain ColorModel#createCompatibleSampleModel(int, int) 
derived} from this color model.
-     */
-    protected final ColorModel colorModel;
+    public static final ImageLayout DEFAULT = new ImageLayout(null, null, 
true, false, true, null);
 
     /**
      * Preferred sample model, or {@code null} if none. The sample model width 
and height may be replaced
      * by the tile size {@linkplain #suggestTileSize(int, int) computed} by 
this {@code ImageLayout} class.
+     *
+     * @see #createCompatibleSampleModel(RenderedImage, Rectangle)
      */
     protected final SampleModel sampleModel;
 
@@ -152,30 +152,21 @@ public class ImageLayout {
 
     /**
      * Creates a new image layout with the given properties.
-     * If both the given color model and sample model are non-null,
-     * then then shall be {@linkplain 
ColorModel#isCompatibleSampleModel(SampleModel) compatible}.
      *
-     * @param  colorModel                      preferred color model, or 
{@code null} if none.
      * @param  sampleModel                     preferred sample model, or 
{@code null} if none.
      * @param  preferredTileSize               preferred tile size, or {@code 
null} for the default size.
      * @param  isTileSizeAdjustmentAllowed     whether tile size can be 
modified if needed.
      * @param  isImageBoundsAdjustmentAllowed  whether image size can be 
modified if needed.
      * @param  isPartialTilesAllowed           whether to allow tiles that are 
only partially filled.
      * @param  preferredMinTile                preferred tile index where 
image start their tile matrix, or {@code null} for (0,0).
-     * @throws IllegalArgumentException if the given color model and sample 
model are incompatible.
      */
-    protected ImageLayout(final ColorModel  colorModel,
-                          final SampleModel sampleModel,
+    protected ImageLayout(final SampleModel sampleModel,
                           final Dimension   preferredTileSize,
                           final boolean     isTileSizeAdjustmentAllowed,
                           final boolean     isImageBoundsAdjustmentAllowed,
                           final boolean     isPartialTilesAllowed,
                           final Point       preferredMinTile)
     {
-        if (colorModel != null && sampleModel != null && 
!colorModel.isCompatibleSampleModel(sampleModel)) {
-            throw new 
IllegalArgumentException(Resources.format(Resources.Keys.IncompatibleColorModel));
-        }
-        this.colorModel  = colorModel;
         this.sampleModel = sampleModel;
         if (preferredTileSize != null) {
             preferredTileWidth  = Math.max(1, preferredTileSize.width);
@@ -217,7 +208,7 @@ public class ImageLayout {
 
         /** Creates a new layout with exactly the tile size of given image. */
         FixedDestination(final WritableRenderedImage destination, final int 
minTileX, final int minTileY) {
-            super(destination.getColorModel(), destination.getSampleModel(),
+            super(destination.getSampleModel(),
                   new Dimension(destination.getTileWidth(), 
destination.getTileHeight()),
                   false, false, true, new Point(minTileX, minTileY));
             this.destination = destination;
@@ -234,33 +225,6 @@ public class ImageLayout {
         }
     }
 
-    /**
-     * Returns a new layout with the same properties than this layout except 
for the color model.
-     * If the given argument value results in no change, returns {@code this}.
-     *
-     * @param  model    the new color model, or {@code null} if none.
-     * @param  cascade  whether to derive a new sample model from the given 
color model.
-     * @return the layout for the given color model.
-     * @throws IllegalArgumentException if {@code cascade} is {@code false}
-     *         and the the given color model is incompatible with the current 
sample model.
-     */
-    public ImageLayout withColorModel(final ColorModel model, final boolean 
cascade) {
-        SampleModel sm = sampleModel;
-        if (model != null) {
-            if (cascade) {
-                sm = model.createCompatibleSampleModel(preferredTileWidth, 
preferredTileHeight);
-            } else if (sm != null && !model.isCompatibleSampleModel(sm)) {
-                throw new 
IllegalArgumentException(Resources.format(Resources.Keys.IncompatibleColorModel));
-            }
-        }
-        if (Objects.equals(colorModel, model) && Objects.equals(sampleModel, 
sm)) {
-            return this;
-        }
-        return new ImageLayout(model, sm,
-                getPreferredTileSize(), isTileSizeAdjustmentAllowed, 
isImageBoundsAdjustmentAllowed, isPartialTilesAllowed,
-                getPreferredMinTile());
-    }
-
     /**
      * Returns a new layout with the same properties than this layout except 
for the sample model.
      * If the given argument value results in no change, returns {@code this}.
@@ -268,23 +232,18 @@ public class ImageLayout {
      * @param  model    the new sample model, or {@code null} if none.
      * @param  cascade  whether to set the preferred tile size to the size of 
the given sample model.
      * @return the layout for the given sample model.
-     * @throws IllegalArgumentException if sample model is incompatible with 
the current color model.
      */
     public ImageLayout withSampleModel(final SampleModel model, final boolean 
cascade) {
         int width  = preferredTileWidth;
         int height = preferredTileHeight;
-        if (model != null) {
-            if (cascade) {
-                width  = model.getWidth();
-                height = model.getHeight();
-            } else if (colorModel != null && 
!colorModel.isCompatibleSampleModel(model)) {
-                throw new 
IllegalArgumentException(Resources.format(Resources.Keys.IncompatibleColorModel));
-            }
+        if (cascade && model != null) {
+            width  = model.getWidth();
+            height = model.getHeight();
         }
         if (Objects.equals(sampleModel, model) && width == preferredTileWidth 
&& height == preferredTileHeight) {
             return this;
         }
-        return new ImageLayout(colorModel, model, new Dimension(width, height),
+        return new ImageLayout(model, new Dimension(width, height),
                 isTileSizeAdjustmentAllowed, isImageBoundsAdjustmentAllowed, 
isPartialTilesAllowed,
                 getPreferredMinTile());
     }
@@ -300,7 +259,7 @@ public class ImageLayout {
      */
     public ImageLayout allowTileSizeAdjustments(boolean allowed) {
         if (isTileSizeAdjustmentAllowed == allowed) return this;
-        return new ImageLayout(colorModel, sampleModel,
+        return new ImageLayout(sampleModel,
                 getPreferredTileSize(), allowed, 
isImageBoundsAdjustmentAllowed, isPartialTilesAllowed,
                 getPreferredMinTile());
     }
@@ -316,7 +275,7 @@ public class ImageLayout {
      */
     public ImageLayout allowImageBoundsAdjustments(boolean allowed) {
         if (isImageBoundsAdjustmentAllowed == allowed) return this;
-        return new ImageLayout(colorModel, sampleModel,
+        return new ImageLayout(sampleModel,
                 getPreferredTileSize(), isTileSizeAdjustmentAllowed, allowed, 
isPartialTilesAllowed,
                 getPreferredMinTile());
     }
@@ -332,15 +291,14 @@ public class ImageLayout {
      */
     public ImageLayout allowPartialTiles(boolean allowed) {
         if (isPartialTilesAllowed == allowed) return this;
-        return new ImageLayout(colorModel, sampleModel,
+        return new ImageLayout(sampleModel,
                 getPreferredTileSize(), isTileSizeAdjustmentAllowed, 
isImageBoundsAdjustmentAllowed, allowed,
                 getPreferredMinTile());
     }
 
     /**
      * Creates a new layout with the tile size and tile indices of the given 
image.
-     * Other properties of this {@code ImageLayout} (color model, sample model 
and
-     * all Boolean flags) are inherited unchanged.
+     * Other properties of this {@code ImageLayout} (sample model and all 
Boolean flags) are inherited unchanged.
      * If the given argument value results in no change, returns {@code this}.
      *
      * @param  source  image from which to take tile size and tile indices.
@@ -359,7 +317,7 @@ public class ImageLayout {
         {
             return this;
         }
-        return new ImageLayout(colorModel, sampleModel,
+        return new ImageLayout(sampleModel,
                 preferredTileSize, isTileSizeAdjustmentAllowed, 
isImageBoundsAdjustmentAllowed, isPartialTilesAllowed,
                 preferredMinTile);
     }
@@ -377,7 +335,7 @@ public class ImageLayout {
         if (size.width == preferredTileWidth && size.height == 
preferredTileHeight) {
             return this;
         }
-        return new ImageLayout(colorModel, sampleModel,
+        return new ImageLayout(sampleModel,
                 size, isTileSizeAdjustmentAllowed, 
isImageBoundsAdjustmentAllowed,
                 isPartialTilesAllowed, getPreferredMinTile());
     }
@@ -491,8 +449,7 @@ public class ImageLayout {
      * 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 {@linkplain #colorModel 
preferred color model} or, if the latter
-     * is unspecified, the {@linkplain RenderedImage#getColorModel() image 
color model} supports transparency.
+     * <p>This method checks whether the {@linkplain 
RenderedImage#getColorModel() image color model} supports transparency.
      * If not, then this method will not return a size that may result in the 
creation of partially empty tiles.
      * In other words, the {@link #isPartialTilesAllowed} flag is ignored 
(handled as {@code false}) for opaque
      * images.</p>
@@ -505,17 +462,16 @@ public class ImageLayout {
         if (bounds != null && bounds.isEmpty()) {
             throw new 
IllegalArgumentException(Errors.format(Errors.Keys.EmptyArgument_1, "bounds"));
         }
-        ColorModel cm = colorModel;
         boolean allowPartialTiles = isPartialTilesAllowed;
         if (allowPartialTiles && image != null && 
!isImageBoundsAdjustmentAllowed) {
-            cm = image.getColorModel();
+            final ColorModel cm = image.getColorModel();
             allowPartialTiles = (cm != null);
-        }
-        if (allowPartialTiles) {
-            if (cm instanceof IndexColorModel) {
-                allowPartialTiles = ((IndexColorModel) 
cm).getTransparentPixel() == 0;
-            } else if (cm != null) {
-                allowPartialTiles = (cm.getTransparency() != 
ColorModel.OPAQUE);
+            if (allowPartialTiles) {
+                if (cm instanceof IndexColorModel) {
+                    allowPartialTiles = ((IndexColorModel) 
cm).getTransparentPixel() == 0;
+                } else {
+                    allowPartialTiles = (cm.getTransparency() != 
ColorModel.OPAQUE);
+                }
             }
         }
         /*
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java
index 2749c490e4..fb6fd7ca6d 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java
@@ -85,19 +85,20 @@ final class ImageOverlay extends MultiSourceImage {
      * @param  sources       the images to overlay. Null array elements are 
ignored.
      * @param  bounds        range of pixel coordinates, or {@code null} for 
the union of all source images.
      * @param  sampleModel   the sample model, of {@code null} for automatic.
-     * @param  colorModel    the color model, of {@code null} for automatic.
+     * @param  colorizer     function for deriving a color model, of {@code 
null} for automatic.
      * @param  autoTileSize  whether this method is allowed to change the tile 
size.
      * @param  parallel      whether parallel computation is allowed.
      * @return the image overlay, or one of the given sources if only one is 
suitable.
      * @throws IllegalArgumentException if there is an incompatibility between 
some source images
      *         or if no image intersect the bounds.
      */
-    static RenderedImage create(RenderedImage[] sources, Rectangle bounds, 
SampleModel sampleModel, ColorModel colorModel,
-                                final boolean autoTileSize, final boolean 
parallel)
+    static RenderedImage create(RenderedImage[] sources, Rectangle bounds, 
SampleModel sampleModel,
+            final Colorizer colorizer, final boolean autoTileSize, final 
boolean parallel)
     {
         final Area aoi = (bounds != null) ? new Area(bounds) : null;
         Area[] contributions = new Area[sources.length];
         final Area validArea = new Area();
+        ColorModel colorModel = null;
         /*
          * Filter the source images for keeping only the ones that intersect 
the bounds.
          * Check image compatibility (number of bands) and color model in the 
same loop.
@@ -146,14 +147,16 @@ final class ImageOverlay extends MultiSourceImage {
         }
         /*
          * Except if there is no image, the sample model should be non-null at 
this point.
-         * However, the color model may still be null if none was specified in 
argument and
-         * no compatible color model was found in the source images. Leave 
thet color model
-         * to null (i.e., we don't invent colors when we don't know what they 
should be).
+         * The color model is optionally specified in the colorizer, which we 
check now
+         * with the current color model used only as a fallback.
          */
         if (count == 0) {
             throw new 
IllegalArgumentException(Resources.format(Resources.Keys.SourceImagesDoNotIntersect));
         }
         final RenderedImage main = sources[0];
+        if (colorizer != null) {
+            colorModel = colorizer.apply(new Colorizer.Target(sampleModel, 
main)).orElse(colorModel);
+        }
         if (count == 1 && sampleModel.equals(main.getSampleModel())) {
             return (colorModel != null) ? RecoloredImage.apply(main, 
colorModel) : main;
         }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageProcessor.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageProcessor.java
index 0f54ef3828..3ea729e744 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageProcessor.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageProcessor.java
@@ -997,6 +997,7 @@ public class ImageProcessor implements Cloneable {
      *   <li>{@linkplain #getImageLayout() Image layout} for the desired 
sample model and color model.</li>
      *   <li>{@linkplain ImageLayout#isImageBoundsAdjustmentAllowed Image 
bounds adjustment flag} for deciding
      *       whether to use a modified image size if {@code bounds} is not 
divisible by a tile size.</li>
+     *   <li>{@linkplain #getColorizer() Colorizer} for customizing the 
rendered image color model.</li>
      * </ul>
      *
      * @param  sources  the images to overlay. Null array elements are ignored.
@@ -1011,12 +1012,14 @@ public class ImageProcessor implements Cloneable {
     public RenderedImage overlay(final RenderedImage[] sources, final 
Rectangle bounds) {
         ArgumentChecks.ensureNonEmpty("sources", sources);
         final ImageLayout layout;
+        final Colorizer colorizer;
         final boolean parallel;
         synchronized (this) {
             layout = this.layout;
+            colorizer = this.colorizer;
             parallel = executionMode != Mode.SEQUENTIAL;
         }
-        return ImageOverlay.create(sources, bounds, layout.sampleModel, 
layout.colorModel,
+        return ImageOverlay.create(sources, bounds, layout.sampleModel, 
colorizer,
                 layout.isTileSizeAdjustmentAllowed | (bounds != null), 
parallel);
     }
 

Reply via email to