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 34aaff134e0fc33dfc14f84ed7b61627784cd5ce
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sat Mar 25 16:04:38 2023 +0100

    `CombinedImageLayout.createColorModel()` should preserve the visible band 
of source image if possible.
    This commit also remove some `java.util.Optional` from internal API because 
all usages of it where invoking `orElse(null)`.
---
 .../java/org/apache/sis/image/BandSelectImage.java |  2 +-
 .../org/apache/sis/image/CombinedImageLayout.java  | 53 +++++++++-------
 .../sis/internal/coverage/RangeArgument.java       |  8 +--
 .../internal/coverage/j2d/ColorModelFactory.java   | 73 +++++++++++++++++-----
 .../coverage/j2d/MultiBandsIndexColorModel.java    | 23 +++----
 .../internal/coverage/j2d/ScaledColorModel.java    | 15 +++--
 .../internal/coverage/j2d/ScaledColorSpace.java    | 31 +++++----
 .../sis/internal/storage/TiledGridResource.java    |  3 +-
 .../sis/internal/storage/esri/RasterStore.java     |  6 +-
 9 files changed, 132 insertions(+), 82 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java 
b/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
index 5fb07dbb69..712c910420 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
@@ -85,7 +85,7 @@ final class BandSelectImage extends SourceAlignedImage {
             return source;
         }
         ArgumentChecks.ensureNonEmptyBounded("bands", false, 0, numBands - 1, 
bands);
-        final ColorModel cm = 
ColorModelFactory.createSubset(source.getColorModel(), bands).orElse(null);
+        final ColorModel cm = 
ColorModelFactory.createSubset(source.getColorModel(), bands);
         /*
          * If the image is an instance of `BufferedImage`, create the subset 
immediately
          * (reminder: this operation will not copy pixel data). It allows us 
to return a
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/image/CombinedImageLayout.java 
b/core/sis-feature/src/main/java/org/apache/sis/image/CombinedImageLayout.java
index fe9e5a8939..8cacace734 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/image/CombinedImageLayout.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/image/CombinedImageLayout.java
@@ -310,40 +310,45 @@ final class CombinedImageLayout extends ImageLayout {
     }
 
     /**
-     * Returns the index of the first band which is flagged as a visible band.
-     * The concept of visible bands is used when an image contains more than 
one band,
-     * but only one of those bands us used by an {@code IndexColorModel}.
+     * Builds a default color model with RGB(A) colors or the colors of the 
first visible band.
+     * If the combined image has 3 or 4 bands and the data type is 8 bits 
integer (bytes),
+     * then this method returns a RGB or RGBA color model depending if there 
is 3 or 4 bands.
+     * Otherwise if {@link ImageUtilities#getVisibleBand(RenderedImage)} finds 
that a source image
+     * declares a visible band, then the returned color model will reuse the 
colors of that band.
+     * Otherwise a grayscale color model is built with a value range inferred 
from the data-type.
      */
-    private int getVisibleBand() {
-        int band = 0;
-        for (int i=0; i < sources.length; i++) {
+    final ColorModel createColorModel() {
+        ColorModel colors = ColorModelFactory.createRGB(sampleModel);
+        if (colors != null) {
+            return colors;
+        }
+        int visibleBand = ColorModelFactory.DEFAULT_VISIBLE_BAND;
+        int base = 0;
+search: for (int i=0; i < sources.length; i++) {
             final RenderedImage source = sources[i];
             final int[] bands = bandsPerSource[i];
-            final int visibleBand = ImageUtilities.getVisibleBand(source);
-            if (visibleBand >= 0) {
+            final int vb = ImageUtilities.getVisibleBand(source);
+            if (vb >= 0) {
                 if (bands == null) {
-                    return band + visibleBand;
+                    visibleBand = base + vb;
+                    colors = source.getColorModel();
+                    break;
                 }
                 for (int j=0; j<bands.length; j++) {
-                    if (bands[j] == visibleBand) {
-                        return band + j;
+                    if (bands[j] == vb) {
+                        visibleBand = base + j;
+                        colors = source.getColorModel();
+                        break search;
                     }
                 }
             }
-            band += (bands != null) ? bands.length : 
ImageUtilities.getNumBands(source);
+            base += (bands != null) ? bands.length : 
ImageUtilities.getNumBands(source);
         }
-        return 0;
-    }
-
-    /**
-     * Builds a default color model.
-     * If the combined image has 3 or 4 bands and the data type is an integer 
storing values on 8 bits,
-     * then this method creates a RGB color model. If there is 4 bands, an 
RGBA color model is defined.
-     * Otherwise a grayscale color model is built with a value range inferred 
from the data-type.
-     */
-    final ColorModel createColorModel() {
-        return ColorModelFactory.createRGB(sampleModel)
-                .orElseGet(() -> 
ColorModelFactory.createGrayScale(sampleModel, getVisibleBand(), null));
+        colors = ColorModelFactory.derive(colors, sampleModel.getNumBands(), 
visibleBand);
+        if (colors != null) {
+            return colors;
+        }
+        return ColorModelFactory.createGrayScale(sampleModel, visibleBand, 
null);
     }
 
     /**
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/RangeArgument.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/RangeArgument.java
index ee72fbac4b..e2ddedbda0 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/RangeArgument.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/RangeArgument.java
@@ -18,7 +18,6 @@ package org.apache.sis.internal.coverage;
 
 import java.util.List;
 import java.util.Arrays;
-import java.util.Optional;
 import java.awt.image.ColorModel;
 import java.awt.image.SampleModel;
 import java.awt.image.RasterFormatException;
@@ -367,13 +366,14 @@ public final class RangeArgument {
 
     /**
      * Returns a color model for the bands specified by the user.
+     * This method may return {@code null} if the color model can not be 
created.
      *
      * @param  colors  the original color model with all bands. Can be {@code 
null}.
-     * @return the color model for a subset of bands, or empty if the given 
color model was null.
+     * @return the color model for a subset of bands, or null if the given 
color model was null.
      */
-    public Optional<ColorModel> select(final ColorModel colors) {
+    public ColorModel select(final ColorModel colors) {
         if (colors == null || isIdentity()) {
-            return Optional.ofNullable(colors);
+            return colors;
         }
         return ColorModelFactory.createSubset(colors, getSelectedBands());
     }
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 3a942df66d..0fc2398eed 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
@@ -19,7 +19,6 @@ package org.apache.sis.internal.coverage.j2d;
 import java.util.Map;
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.Optional;
 import java.awt.Transparency;
 import java.awt.Color;
 import java.awt.color.ColorSpace;
@@ -52,6 +51,12 @@ import org.apache.sis.util.Debug;
  * @since   1.0
  */
 public final class ColorModelFactory {
+    /**
+     * Band to make visible if an image contains many bands
+     * but a color map is defined for only one band.
+     */
+    public static final int DEFAULT_VISIBLE_BAND = 0;
+
     /**
      * The fully transparent color.
      */
@@ -525,11 +530,12 @@ public final class ColorModelFactory {
     /**
      * Creates a RGB color model for the given sample model.
      * The sample model shall use integer type and have 3 or 4 bands.
+     * This method may return {@code null} if the color model can not be 
created.
      *
      * @param  model  the sample model for which to create a color model.
-     * @return the color model, or empty if a precondition does not hold.
+     * @return the color model, or null if a precondition does not hold.
      */
-    public static Optional<ColorModel> createRGB(final SampleModel model) {
+    public static ColorModel createRGB(final SampleModel model) {
         final int numBands = model.getNumBands();
         if (numBands >= 3 && numBands <= 4 && 
ImageUtilities.isIntegerType(model)) {
             int bitsPerSample = 0;
@@ -537,10 +543,10 @@ public final class ColorModelFactory {
                 bitsPerSample = Math.max(bitsPerSample, 
model.getSampleSize(i));
             }
             if (bitsPerSample <= Byte.SIZE) {
-                return Optional.of(createRGB(bitsPerSample, 
model.getNumDataElements() == 1, numBands > 3));
+                return createRGB(bitsPerSample, model.getNumDataElements() == 
1, numBands > 3);
             }
         }
-        return Optional.empty();
+        return null;
     }
 
     /**
@@ -585,7 +591,7 @@ public final class ColorModelFactory {
      *   <li>Input color model is null.</li>
      *   <li>Given color model is not assignable from the following types:
      *     <ul>
-     *       <li>{@link ComponentColorModel}</li>
+     *       <li>{@link ComponentColorModel} with only one band</li>
      *       <li>{@link MultiBandsIndexColorModel}</li>
      *       <li>{@link ScaledColorModel}</li>
      *     </ul>
@@ -593,35 +599,68 @@ public final class ColorModelFactory {
      *   <li>Input color model is recognized, but we cannot infer a proper 
color interpretation for given number of bands.</li>
      * </ul>
      *
-     * <em>Note about {@link PackedColorModel} and {@link 
DirectColorModel}</em>: they're not managed for now, because
-     * they're really designed for a "rigid" case where data buffer values are 
interpreted directly by the color model.
+     * This method may return {@code null} if the color model can not be 
created.
+     *
+     * <p><em>Note about {@link PackedColorModel} and {@link 
DirectColorModel}</em>:
+     * those color models not managed for now, because they are really 
designed for
+     * a "rigid" case where data buffer values are interpreted directly by the 
color model.</p>
      *
      * @param  cm     the color model, or {@code null}.
      * @param  bands  the bands to select. Must neither be null nor empty.
-     * @return the subset color model, or empty if input was null or if a 
subset cannot be deduced.
+     * @return the subset color model, or null if input was null or if a 
subset cannot be deduced.
      */
-    public static Optional<ColorModel> createSubset(final ColorModel cm, final 
int[] bands) {
+    public static ColorModel createSubset(final ColorModel cm, final int[] 
bands) {
         assert (bands != null) && bands.length > 0 : bands;
+        int visibleBand = DEFAULT_VISIBLE_BAND;
+        if (cm instanceof MultiBandsIndexColorModel) {
+            visibleBand = ((MultiBandsIndexColorModel) cm).visibleBand;
+        } else if (cm != null) {
+            final ColorSpace cs = cm.getColorSpace();
+            if (cs instanceof ScaledColorSpace) {
+                visibleBand = ((ScaledColorSpace) cs).visibleBand;
+            }
+        }
+        for (int i=0; i<bands.length; i++) {
+            if (bands[i] == visibleBand) {
+                visibleBand = i;
+                break;
+            }
+        }
+        return derive(cm, bands.length, visibleBand);
+    }
+
+    /**
+     * Returns a color model with with the same colors but a different number 
of bands.
+     * Current implementation supports {@link ComponentColorModel} with only 
one band,
+     * {@link MultiBandsIndexColorModel} and {@link ScaledColorModel}.
+     * This method may return {@code null} if the color model can not be 
created.
+     *
+     * @param  cm           the color model, or {@code null}.
+     * @param  numBands     new number of bands.
+     * @param  visibleBand  new visible band.
+     * @return the derived color model, or null if this method does not 
support the given color model.
+     */
+    public static ColorModel derive(final ColorModel cm, final int numBands, 
final int visibleBand) {
         if (cm == null) {
-            return Optional.empty();
+            return null;
         }
         final ColorModel subset;
         if (cm instanceof MultiBandsIndexColorModel) {
-            subset = ((MultiBandsIndexColorModel) 
cm).createSubsetColorModel(bands);
+            subset = ((MultiBandsIndexColorModel) cm).derive(numBands, 
visibleBand);
         } else if (cm instanceof ScaledColorModel) {
-            subset = ((ScaledColorModel) cm).createSubsetColorModel(bands);
-        } else if (bands.length == 1 && cm instanceof ComponentColorModel) {
+            subset = ((ScaledColorModel) cm).derive(numBands, visibleBand);
+        } else if (numBands == 1 && cm instanceof ComponentColorModel) {
             final int dataType = cm.getTransferType();
             if (dataType < DataBuffer.TYPE_BYTE || dataType > 
DataBuffer.TYPE_USHORT) {
-                return Optional.empty();
+                return null;
             }
             final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
             subset = new ComponentColorModel(cs, false, true, 
Transparency.OPAQUE, dataType);
         } else {
             // TODO: handle other color models.
-            return Optional.empty();
+            return null;
         }
-        return Optional.of(CACHE.unique(subset));
+        return CACHE.unique(subset);
     }
 
     /**
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 9ec0bd6be9..75f71d90fa 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
@@ -88,26 +88,27 @@ final class MultiBandsIndexColorModel extends 
IndexColorModel {
     }
 
     /**
-     * Creates a new color model with only a subset of the bands in this color 
model.
+     * Returns a color model with a different number of bands and a different 
visible band.
      *
      * <p><b>Note:</b> the new color model will use a copy of the color map of 
this color model.
      * There is no way to share the {@code int[]} array of ARGB values between 
two {@link IndexColorModel}s.</p>
+     *
+     * @param  numBands     new number of bands.
+     * @param  visibleBand  new visible band.
+     * @return a color model with the same colors but the specified number of 
bands.
      */
-    final IndexColorModel createSubsetColorModel(final int[] bands) {
+    final IndexColorModel derive(final int numBands, final int visibleBand) {
+        if (numBands == this.numBands && visibleBand == this.visibleBand) {
+            return this;
+        }
         final int[]   cmap        = getARGB();
         final boolean hasAlpha    = hasAlpha();
         final int     transparent = getTransparentPixel();
-        if (bands.length == 1) {
+        if (numBands == 1) {
             return new IndexColorModel(pixel_bits, cmap.length, cmap, 0, 
hasAlpha, transparent, transferType);
         }
-        int vb = 0;
-        for (int i=0; i<bands.length; i++) {
-            if (bands[i] == visibleBand) {
-                vb = i;
-                break;
-            }
-        }
-        return new MultiBandsIndexColorModel(pixel_bits, cmap.length, cmap, 0, 
hasAlpha, transparent, transferType, bands.length, vb);
+        return new MultiBandsIndexColorModel(pixel_bits, cmap.length, cmap, 0,
+                    hasAlpha, transparent, transferType, numBands, 
visibleBand);
     }
 
     /**
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
index 4028239e67..1da6d2dc07 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ScaledColorModel.java
@@ -31,7 +31,7 @@ import org.apache.sis.internal.feature.Resources;
  * In addition, this class renders the {@link Float#NaN} values as transparent.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   1.1
  */
 final class ScaledColorModel extends ComponentColorModel {
@@ -66,10 +66,17 @@ final class ScaledColorModel extends ComponentColorModel {
     }
 
     /**
-     * Creates a new color model with only a subset of the bands in this color 
model.
+     * Returns a color model with a different number of bands and a different 
visible band.
+     *
+     * @param  numBands     new number of bands.
+     * @param  visibleBand  new visible band.
+     * @return a color model with the same colors but the specified number of 
bands.
      */
-    final ScaledColorModel createSubsetColorModel(final int[] bands) {
-        return new ScaledColorModel(new ScaledColorSpace(cs, bands), 
transferType);
+    final ScaledColorModel derive(final int numBands, final int visibleBand) {
+        if (numBands == cs.getNumComponents() && visibleBand == 
cs.visibleBand) {
+            return this;
+        }
+        return new ScaledColorModel(new ScaledColorSpace(cs, numBands, 
visibleBand), transferType);
     }
 
     /**
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 b197f91468..c3990c1c2a 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
@@ -29,7 +29,7 @@ import org.apache.sis.util.Debug;
  * It should be used only when no standard color space can be used.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.1
+ * @version 1.4
  *
  * @see ScaledColorModel
  * @see ColorModelFactory#createGrayScale(int, int, int, double, double)
@@ -76,26 +76,23 @@ final class ScaledColorSpace extends ColorSpace {
     ScaledColorSpace(final int numComponents, final int visibleBand, final 
double minimum, final double maximum) {
         super(TYPE_GRAY, numComponents);
         this.visibleBand = visibleBand;
-        this.maximum = maximum;
-        scale  = ScaledColorModel.RANGE / (maximum - minimum);
-        offset = minimum;
+        this.maximum     = maximum;
+        this.scale       = ScaledColorModel.RANGE / (maximum - minimum);
+        this.offset      = minimum;
     }
 
     /**
-     * Creates a color space for the same range of values than the given 
space, but a subset of the bands.
+     * Creates a color space for the same range of values than the given 
space, but a different number of bands.
+     *
+     * @param  numComponents  the new number of components.
+     * @param  visibleBand    the new band to select as the visible band.
      */
-    ScaledColorSpace(final ScaledColorSpace parent, final int[] bands) {
-        super(TYPE_GRAY, bands.length);
-        scale   = parent.scale;
-        offset  = parent.offset;
-        maximum = parent.maximum;
-        for (int i=0; i<bands.length; i++) {
-            if (bands[i] == parent.visibleBand) {
-                visibleBand = i;
-                return;
-            }
-        }
-        visibleBand = 0;
+    ScaledColorSpace(final ScaledColorSpace parent, final int numComponents, 
final int visibleBand) {
+        super(TYPE_GRAY, numComponents);
+        this.scale       = parent.scale;
+        this.offset      = parent.offset;
+        this.maximum     = parent.maximum;
+        this.visibleBand = visibleBand;
     }
 
     /**
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
index 86213d27b4..b065ce9b73 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
@@ -300,6 +300,7 @@ public abstract class TiledGridResource extends 
AbstractGridCoverageResource {
 
         /**
          * The color model for the bands to read (not the full set of bands in 
the resource).
+         * May be {@code null} if the color model could not be created.
          */
         final ColorModel colorsForBandSubset;
 
@@ -410,7 +411,7 @@ public abstract class TiledGridResource extends 
AbstractGridCoverageResource {
             this.ranges              = bands;
             this.includedBands       = includedBands;
             this.modelForBandSubset  = rangeIndices.select(getSampleModel(), 
loadAllBands);
-            this.colorsForBandSubset = 
rangeIndices.select(getColorModel()).orElse(null);
+            this.colorsForBandSubset = rangeIndices.select(getColorModel());
             this.fillValue           = getFillValue();
             /*
              * All `TiledGridCoverage` instances can share the same cache if 
they read all tiles fully.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
index d61a2ce50d..497a3b8b0e 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
@@ -77,7 +77,7 @@ abstract class RasterStore extends PRJDataStore implements 
GridCoverageResource
      * Band to make visible if an image contains many bands
      * but a color map is defined for only one band.
      */
-    private static final int VISIBLE_BAND = 0;
+    private static final int VISIBLE_BAND = 
ColorModelFactory.DEFAULT_VISIBLE_BAND;
 
     /**
      * Keyword for the number of rows in the image.
@@ -419,7 +419,7 @@ abstract class RasterStore extends PRJDataStore implements 
GridCoverageResource
              */
             if (band == VISIBLE_BAND) {
                 if (isRGB) {
-                    colorModel = ColorModelFactory.createRGB(sm).get();     // 
Should not be empty.
+                    colorModel = ColorModelFactory.createRGB(sm);       // 
Should never be null.
                 } else {
                     try {
                         colorModel = readColorMap(dataType, (int) (maximum + 
1), bands.length);
@@ -484,7 +484,7 @@ abstract class RasterStore extends PRJDataStore implements 
GridCoverageResource
         ColorModel cm = colorModel;
         if (!range.isIdentity()) {
             bands = Arrays.asList(range.select(sampleDimensions));
-            cm = range.select(cm).orElse(null);
+            cm = range.select(cm);
             if (cm == null) {
                 final SampleDimension band = bands.get(VISIBLE_BAND);
                 cm = ColorModelFactory.createGrayScale(data.getSampleModel(), 
VISIBLE_BAND, band.getSampleRange().orElse(null));

Reply via email to