sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 03/10: At image rendering time, convert floating point values to integer values that IndexColorModel can use.
Date Wed, 29 Jul 2020 16:18:56 GMT
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 e4c85f06f20cdfc53c3098e415e74c5bce1461ff
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Jul 28 13:01:15 2020 +0200

    At image rendering time, convert floating point values to integer values that IndexColorModel
can use.
---
 .../apache/sis/gui/coverage/CoverageCanvas.java    |  54 +++++------
 .../sis/gui/coverage/ImagePropertyExplorer.java    |   3 +-
 .../org/apache/sis/gui/coverage/RenderingData.java |  69 ++++++++++++--
 .../java/org/apache/sis/image/ImageProcessor.java  |   5 +-
 .../java/org/apache/sis/image/RecoloredImage.java  |  11 ++-
 .../sis/internal/coverage/j2d/ColorModelType.java  | 100 +++++++++++++++++++++
 .../sis/internal/coverage/j2d/Colorizer.java       |   1 +
 .../internal/coverage/j2d/ScaledColorModel.java    |  18 ++++
 8 files changed, 222 insertions(+), 39 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
index 8a0f55b..2e3fa64 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
@@ -18,6 +18,7 @@ package org.apache.sis.gui.coverage;
 
 import java.util.Map;
 import java.util.EnumMap;
+import java.util.List;
 import java.util.Locale;
 import java.awt.Graphics2D;
 import java.awt.Rectangle;
@@ -41,6 +42,7 @@ import org.opengis.geometry.DirectPosition;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
@@ -373,7 +375,7 @@ public class CoverageCanvas extends MapCanvasAWT {
                  * Invoked in JavaFX thread for setting the image to the instance we just
fetched.
                  */
                 @Override protected void succeeded() {
-                    setRawImage(getValue(), imageGeometry);
+                    setRawImage(getValue(), imageGeometry, coverage.getSampleDimensions());
                 }
             });
         }
@@ -383,13 +385,15 @@ public class CoverageCanvas extends MapCanvasAWT {
      * Invoked when a new image has been successfully loaded. The given image must be the
"raw" image,
      * without resampling and without color ramp stretching. The call to this method is followed
by a
      * a repaint event, which will cause the image to be resampled in a background thread.
+     *
+     * <p>All arguments can be {@code null} for clearing the canvas.</p>
      */
-    private void setRawImage(final RenderedImage image, final GridGeometry imageGeometry)
{
+    private void setRawImage(final RenderedImage image, final GridGeometry domain, final
List<SampleDimension> ranges) {
         resampledImages.clear();
-        data.setImage(image, imageGeometry);
+        data.setImage(image, domain, ranges);
         Envelope bounds = null;
-        if (imageGeometry != null && imageGeometry.isDefined(GridGeometry.ENVELOPE))
{
-            bounds = imageGeometry.getEnvelope();
+        if (domain != null && domain.isDefined(GridGeometry.ENVELOPE)) {
+            bounds = domain.getEnvelope();
         }
         setObjectiveBounds(bounds);
         requestRepaint();                       // Cause `Worker` class to be executed.
@@ -442,9 +446,12 @@ public class CoverageCanvas extends MapCanvasAWT {
         private final LinearTransform objectiveToDisplay;
 
         /**
-         * The source image after resampling.
+         * Whether the {@linkplain RenderingData#resample resampling operation} applied is
different
+         * than the one used the last time that the image has been rendered, ignoring translations.
+         * Translations do not require new resampling operations because we can manage translations
+         * by changing {@link RenderedImage} coordinates.
          */
-        private RenderedImage resampledImage;
+        private boolean resamplingChanged;
 
         /**
          * The resampled image after color ramp stretching and/or index color model applied.
@@ -459,8 +466,7 @@ public class CoverageCanvas extends MapCanvasAWT {
         private RenderedImage prefetchedImage;
 
         /**
-         * Conversion from {@link #resampledImage} (also {@link #prefetchedImage})
-         * pixel coordinates to display coordinates.
+         * Conversion from {@link #prefetchedImage} pixel coordinates to display coordinates.
          */
         private AffineTransform resampledToDisplay;
 
@@ -485,7 +491,6 @@ public class CoverageCanvas extends MapCanvasAWT {
             objectiveToDisplay = canvas.getObjectiveToDisplay();
             displayBounds      = canvas.getDisplayBounds();
             if (data.validateCRS(objectiveCRS)) {
-                resampledImage = canvas.resampledImages.get(Stretching.NONE);
                 recoloredImage = canvas.resampledImages.get(data.selectedDerivative);
             }
         }
@@ -506,7 +511,7 @@ public class CoverageCanvas extends MapCanvasAWT {
         }
 
         /**
-         * Invoked in background thread for resampling the image or stretching the color
ramp.
+         * Invoked in background thread for resampling the image and stretching the color
ramp.
          * This method performs some of the steps documented in class Javadoc, with possibility
          * to skip the first step if the required source image is already resampled.
          */
@@ -515,19 +520,19 @@ public class CoverageCanvas extends MapCanvasAWT {
         protected void render() throws TransformException {
             final Long id = LogHandler.loadingStart(originator);
             try {
-                boolean isResampled = (resampledImage != null);
-                if (isResampled) {
+                /*
+                 * A new resampling is needed if the change compared to last rendering
+                 * is anything else than identity or translation.
+                 */
+                resamplingChanged = (recoloredImage == null);
+                if (!resamplingChanged) {
                     resampledToDisplay = data.getTransform(objectiveToDisplay);
-                    // Recompute if anything else than identity or translation.
-                    isResampled = (resampledToDisplay.getType()
-                            & ~(AffineTransform.TYPE_IDENTITY | AffineTransform.TYPE_TRANSLATION))
== 0;
+                    resamplingChanged = (resampledToDisplay.getType() &
+                            ~(AffineTransform.TYPE_IDENTITY | AffineTransform.TYPE_TRANSLATION))
!= 0;
                 }
-                if (!isResampled) {
-                    recoloredImage = null;
-                    resampledImage = data.resample(objectiveCRS, objectiveToDisplay);
+                if (resamplingChanged) {
+                    final RenderedImage resampledImage = data.resample(objectiveCRS, objectiveToDisplay);
                     resampledToDisplay = data.getTransform(objectiveToDisplay);
-                }
-                if (recoloredImage == null) {
                     recoloredImage = data.recolor(resampledImage);
                 }
                 prefetchedImage = data.prefetch(recoloredImage, resampledToDisplay, displayBounds);
@@ -561,15 +566,12 @@ public class CoverageCanvas extends MapCanvasAWT {
      */
     private void cacheRenderingData(final Worker worker) {
         data = worker.data;
-        final RenderedImage newValue = worker.resampledImage;
-        final RenderedImage oldValue = resampledImages.put(Stretching.NONE, newValue);
-        if (oldValue != newValue && oldValue != null) {
+        if (worker.resamplingChanged) {
             /*
              * If resampled image changed, then all derivative images (with stretched color
ramp
              * or other operation applied) are not valid anymore. We need to empty the cache.
              */
             resampledImages.clear();
-            resampledImages.put(Stretching.NONE, newValue);
         }
         resampledImages.put(data.selectedDerivative, worker.recoloredImage);
         /*
@@ -636,7 +638,7 @@ public class CoverageCanvas extends MapCanvasAWT {
      */
     @Override
     protected void clear() {
-        setRawImage(null, null);
+        setRawImage(null, null, null);
         super.clear();
     }
 }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
index a57ffb0..9fa669c 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
@@ -385,7 +385,8 @@ public class ImagePropertyExplorer extends Widget {
             selectedImage = sources.getSelectionModel().selectedItemProperty();
             sources.setCellFactory(SourceCell::new);
             selectedImage.addListener((p,o,n) -> {
-                final RenderedImage selected = n.getValue();
+                RenderedImage selected = null;
+                if (n != null) selected = n.getValue();
                 imageSelected(selected != null ? selected : image.get());
             });
         }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index cb41d64..4d41337 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -18,12 +18,17 @@ package org.apache.sis.gui.coverage;
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.List;
+import java.util.function.Function;
+import java.awt.Color;
 import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.image.BufferedImage;
 import java.awt.image.RenderedImage;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.NoninvertibleTransformException;
+import org.apache.sis.coverage.Category;
+import org.apache.sis.coverage.SampleDimension;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.datum.PixelInCell;
 import org.opengis.referencing.operation.CoordinateOperation;
@@ -36,6 +41,8 @@ import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.geometry.Shapes2D;
 import org.apache.sis.image.ImageProcessor;
+import org.apache.sis.internal.coverage.j2d.Colorizer;
+import org.apache.sis.internal.coverage.j2d.ColorModelType;
 import org.apache.sis.internal.coverage.j2d.ImageUtilities;
 import org.apache.sis.internal.coverage.j2d.PreferredSize;
 import org.apache.sis.internal.system.Modules;
@@ -83,6 +90,13 @@ import org.apache.sis.util.logging.Logging;
  */
 final class RenderingData implements Cloneable {
     /**
+     * Whether to allow the creation of {@link java.awt.image.IndexColorModel}. This flag
may be temporarily set
+     * to {@code false} for testing or debugging. If {@code false}, images may be only grayscale
and may be much
+     * slower to render, but should still be visible.
+     */
+    private static final boolean CREATE_INDEX_COLOR_MODEL = false;
+
+    /**
      * The data fetched from {@link GridCoverage#render(GridExtent)} for current {@code sliceExtent}.
      * This rendered image may be tiled and fetching those tiles may require computations
to be performed
      * in background threads. Pixels in this {@code data} image are mapped to pixels in the
display
@@ -93,6 +107,10 @@ final class RenderingData implements Cloneable {
      *   <li><code>{@linkplain #changeOfCRS}.getMathTransform()</code></li>
      *   <li>{@link CoverageCanvas#getObjectiveToDisplay()}</li>
      * </ol>
+     *
+     * @see #dataGeometry
+     * @see #dataRanges
+     * @see #setImage(RenderedImage, GridGeometry, List)
      */
     private RenderedImage data;
 
@@ -102,10 +120,24 @@ final class RenderingData implements Cloneable {
      * to two dimensions and with a translation added for taking in account the requested
{@code sliceExtent}.
      * The coverage CRS is initially the same as the {@linkplain CoverageCanvas#getObjectiveCRS()
objective CRS},
      * but may become different later if user selects a different objective CRS.
+     *
+     * @see #data
+     * @see #dataRanges
+     * @see #setImage(RenderedImage, GridGeometry, List)
      */
     private GridGeometry dataGeometry;
 
     /**
+     * Ranges of sample values in each band of {@link #data}. This is used for determining
on which sample values
+     * to apply colors when user asked to apply a color ramp. May be {@code null}.
+     *
+     * @see #data
+     * @see #dataGeometry
+     * @see #setImage(RenderedImage, GridGeometry, List)
+     */
+    private List<SampleDimension> dataRanges;
+
+    /**
      * Conversion or transformation from {@linkplain #data} CRS to {@linkplain CoverageCanvas#getObjectiveCRS()
      * objective CRS}, or {@code null} if not yet computed. This is an identity operation
if the user did not
      * selected a different CRS after the coverage has been shown.
@@ -139,6 +171,14 @@ final class RenderingData implements Cloneable {
     private Statistics[] statistics;
 
     /**
+     * The colors to apply for given categories, or {@code null} if none.
+     * A null value means that the original colors specified in the {@link #data} image are
used unmodified.
+     *
+     * @see Colorizer#GRAYSCALE
+     */
+    private Function<Category,Color[]> colors;
+
+    /**
      * The processor that we use for resampling image and stretching their color ramps.
      */
     final ImageProcessor processor;
@@ -187,13 +227,16 @@ final class RenderingData implements Cloneable {
 
     /**
      * Sets the data to given image, which can be {@code null}.
+     * This method does not reset the {@link #colors} to null.
      */
-    final void setImage(final RenderedImage data, final GridGeometry dataGeometry) {
+    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
+    final void setImage(final RenderedImage data, final GridGeometry domain, final List<SampleDimension>
ranges) {
         clearCRS();
         displayToObjective = null;
         statistics         = null;
         this.data          = data;
-        this.dataGeometry  = dataGeometry;
+        this.dataGeometry  = domain;
+        this.dataRanges    = ranges;        // Not cloned because already an unmodifiable
list.
     }
 
     /**
@@ -253,13 +296,12 @@ final class RenderingData implements Cloneable {
     }
 
     /**
-     * Applies image operation on the given resampled image.
-     * In current implementation, the only operations are stretching the color ramp.
+     * Optionally stretches the color map, then optionally applies an index color model.
      *
      * @param  resampledImage  the image computed by {@link #resample(CoordinateReferenceSystem,
LinearTransform)}.
      * @return image with operation applied and color ramp stretched. May be the same instance
than given image.
      */
-    final RenderedImage recolor(final RenderedImage resampledImage) {
+    final RenderedImage recolor(RenderedImage resampledImage) {
         if (selectedDerivative != Stretching.NONE) {
             final Map<String,Object> modifiers = new HashMap<>(4);
             /*
@@ -274,7 +316,22 @@ final class RenderingData implements Cloneable {
             if (selectedDerivative == Stretching.AUTOMATIC) {
                 modifiers.put("multStdDev", 3);
             }
-            return processor.stretchColorRamp(resampledImage, modifiers);
+            resampledImage = processor.stretchColorRamp(resampledImage, modifiers);
+        }
+        /*
+         * Converts images of floating point values to integer values that we can use with
IndexColorModel.
+         *
+         * TODO: if `colors` is null, instead than defaulting to `Colorizer.GRAYSCALE` we
should get the colors
+         *       from the current ColorModel. This work should be done in Colorizer by converting
the ranges of
+         *       sample values in source image to ranges of sample values in destination
image, then query
+         *       ColorModel.getRGB(Object) for increasing integer values in that range.
+         */
+        if (CREATE_INDEX_COLOR_MODEL) {
+            final ColorModelType ct = ColorModelType.find(resampledImage.getColorModel());
+            if (ct.isSlow || (colors != null && ct.useColorRamp)) {
+                resampledImage = processor.toIndexedColors(resampledImage,
+                        dataRanges, (colors != null) ? colors : Colorizer.GRAYSCALE);
+            }
         }
         return resampledImage;
     }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
index 4e27f41..7b8c5b2 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
@@ -734,10 +734,7 @@ public class ImageProcessor implements Cloneable {
                     interpolation, fillValues, positionalAccuracyHints));
             break;
         }
-        if (cm != null && !cm.equals(resampled.getColorModel())) {
-            resampled = unique(new RecoloredImage(resampled, cm));
-        }
-        return resampled;                           // Do not cache user-provided image.
+        return RecoloredImage.create(resampled, cm);
     }
 
     /**
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/RecoloredImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/RecoloredImage.java
index 7c5ac5c..fb91497 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/RecoloredImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/RecoloredImage.java
@@ -64,7 +64,7 @@ final class RecoloredImage extends ImageAdapter {
     /**
      * Creates a new recolored image with the given colors.
      */
-    RecoloredImage(final RenderedImage source, final ColorModel colors) {
+    private RecoloredImage(final RenderedImage source, final ColorModel colors) {
         super(source);
         this.colors = colors;
     }
@@ -73,7 +73,10 @@ final class RecoloredImage extends ImageAdapter {
      * Returns a recolored image with the given colors. This method may return
      * an existing ancestor if one is found with the specified color model.
      */
-    private static RenderedImage create(RenderedImage source, final ColorModel colors) {
+    static RenderedImage create(RenderedImage source, final ColorModel colors) {
+        if (colors == null) {
+            return source;
+        }
         for (;;) {
             if (colors.equals(source.getColorModel())) {
                 return source;
@@ -264,7 +267,11 @@ final class RecoloredImage extends ImageAdapter {
         }
         /*
          * Sample values can not be reused as-is; we need to convert them to integers in
[0 … 255] range.
+         * Skip any previous `RecoloredImage` since we are replacing the `ColorModel` by
a new one.
          */
+        while (source instanceof RecoloredImage) {
+            source = ((RecoloredImage) source).source;
+        }
         final ColorModel      colorModel = colorizer.compactColorModel(1, 0);           //
Must be first.
         final MathTransform1D converter  = colorizer.getSampleToIndexValues();
         final NumberRange<?>  range      = colorizer.getRepresentativeRange();
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelType.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelType.java
new file mode 100644
index 0000000..4c53565
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelType.java
@@ -0,0 +1,100 @@
+/*
+ * 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.ColorModel;
+import java.awt.image.DirectColorModel;
+import java.awt.image.IndexColorModel;
+
+
+/**
+ * Information about the color model. This enumeration provides an easier way of determining
+ * whether the color ramp <em>can</em> be replaced, and whether it <em>should</em>
be replaced
+ * for performance reasons.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public enum ColorModelType {
+    /**
+     * Color model uses directly RGB colors.
+     * This model is efficient and should not be changed.
+     * Color palette can not be changed.
+     */
+    DIRECT(false, false),
+
+    /**
+     * Color model uses indexed colors.
+     * This model is efficient and does not need to be changed.
+     * Color palette can be changed.
+     */
+    INDEXED(true, false),
+
+    /**
+     * Color model use colors computed on the fly from floating point values.
+     * This model is inefficient and should be changed if possible.
+     */
+    SCALED(true, true),
+
+    /**
+     * Unrecognized color model.
+     */
+    OTHER(false, false);
+
+    /**
+     * Whether the color model uses a color palette.
+     * A {@code true} value implies that the color ramp is replaceable.
+     */
+    public final boolean useColorRamp;
+
+    /**
+     * Whether rendering with this color model is slow.
+     * In such case, the color model may need to be changed using {@link Colorizer}.
+     */
+    public final boolean isSlow;
+
+    /**
+     * Creates a new enumeration value.
+     */
+    private ColorModelType(final boolean useColorRamp, final boolean isSlow) {
+        this.useColorRamp = useColorRamp;
+        this.isSlow = isSlow;
+    }
+
+    /**
+     * Gets the type of given color model.
+     *
+     * @param  model  the color model (may be {@code null}).
+     * @return type of given color model (never {@code null}).
+     */
+    public static ColorModelType find(final ColorModel model) {
+        if (model != null) {
+            if (model instanceof DirectColorModel) {
+                return DIRECT;
+            }
+            if (model instanceof IndexColorModel) {
+                return INDEXED;
+            }
+            if (model.getColorSpace() instanceof ScaledColorSpace) {
+                return SCALED;
+            }
+        }
+        return OTHER;
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Colorizer.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Colorizer.java
index 2161f3d..a8c7fce 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Colorizer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Colorizer.java
@@ -62,6 +62,7 @@ import org.apache.sis.util.resources.Vocabulary;
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
  *
+ * @see ColorModelType
  * @see ColorModelFactory#createColorModel(int, int, int, Collection)
  *
  * @since 1.1
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 4ec32ab..082535b 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
@@ -137,4 +137,22 @@ final class ScaledColorModel extends ComponentColorModel {
         final int c = Math.max(0, Math.min(MASK, (int) ((value - cs.offset) * cs.scale)));
         return c | (c << Byte.SIZE) | (c << 2*Byte.SIZE) | (MASK << 3*Byte.SIZE);
     }
+
+    /**
+     * Returns {@code true} if the given object is also an instance of {@link ScaledColorModel}
+     * with equals color space and same transfer type.
+     *
+     * <div class="note"><b>Note:</b>
+     * we have to override this method because the {@link ComponentColorModel#equals(Object)}
implementation
+     * is confused by our overriding of {@link #getTransparency()} method. However we do
not need to override
+     * {@link #hashCode()}.</div>
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj instanceof ScaledColorModel) {
+            final ScaledColorModel other = (ScaledColorModel) obj;
+            return transferType == other.getTransferType() && cs.equals(other.cs);
+        }
+        return false;
+    }
 }


Mime
View raw message