You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2024/12/25 15:29:48 UTC
(sis) 01/03: Remove the `ColorModel` property from `ImageLayout` for avoiding an overlap with `Colorizer`.
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 <ma...@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);
}