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 0a3d3792e96f5f065f6104ec8a94bed81665c963
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Aug 3 12:17:17 2020 +0200
Make `PixelIterator.getSampleRange()` method implementation more generic (no more test
for specific `SampleModel` subclasses).
---
.../java/org/apache/sis/image/PixelIterator.java | 86 ++++++++++++----------
.../org/apache/sis/image/DefaultIteratorTest.java | 31 ++++++++
2 files changed, 78 insertions(+), 39 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
index 6cdcca8..7940213 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
@@ -30,14 +30,14 @@ import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.image.WritableRenderedImage;
import java.awt.image.SampleModel;
-import java.awt.image.SinglePixelPackedSampleModel;
-import java.awt.image.MultiPixelPackedSampleModel;
import java.util.NoSuchElementException;
import org.opengis.coverage.grid.SequenceType;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.measure.NumberRange;
+import org.apache.sis.internal.util.Numerics;
import org.apache.sis.internal.feature.Resources;
+import org.apache.sis.internal.coverage.j2d.ImageUtilities;
import static java.lang.Math.floorDiv;
import static org.apache.sis.internal.util.Numerics.ceilDiv;
@@ -459,51 +459,59 @@ public abstract class PixelIterator {
* If the samples are stored as floating point values, then the ranges are infinite (unbounded).
*
* <p>Usually, the range is the same for all bands. A situation where the ranges
may differ is when an
- * image uses {@link SinglePixelPackedSampleModel}, in which case the number of bits
per pixel may vary
- * for different bands.</p>
+ * image uses {@link java.awt.image.SinglePixelPackedSampleModel}, in which case the
number of bits per
+ * pixel may vary for different bands.</p>
*
* @return the ranges of valid sample values for each band. Ranges may be {@linkplain
NumberRange#isBounded() unbounded}.
*/
public NumberRange<?>[] getSampleRanges() {
+ /*
+ * Take the sample model of current tile if possible. This details should be irrelevant
(so we do not mention
+ * it in above javadoc) because the sample model shall be the same for all tiles.
But if inconsistency happens,
+ * stay consistent at least with the fact that all getter methods in PixelIterator
return information relative
+ * to current iterator position.
+ */
final SampleModel model = (currentRaster != null) ? currentRaster.getSampleModel()
: image.getSampleModel();
final NumberRange<?>[] ranges = new NumberRange<?>[model.getNumBands()];
- final NumberRange<?> range;
- if (model instanceof MultiPixelPackedSampleModel) {
- /*
- * This model supports only unsigned integer types: DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT
- * or DataBuffer.TYPE_INT (considered unsigned in the context of this sample
model). The number
- * of bits per sample is defined by the "pixel bit stride".
- */
- final int numBits = ((MultiPixelPackedSampleModel) model).getPixelBitStride();
- range = NumberRange.create(0, true, (1 << numBits) - 1, true);
- } else if (model instanceof SinglePixelPackedSampleModel) {
- /*
- * This model supports only unsigned integer types: TYPE_BYTE, TYPE_USHORT, TYPE_INT
(considered
- * unsigned in the context of this sample model). The number of bits may vary
for each band.
- */
- final int[] masks = ((SinglePixelPackedSampleModel) model).getBitMasks();
- for (int i=0; i<masks.length; i++) {
- final int numBits = Integer.bitCount(masks[i]);
- ranges[i] = NumberRange.create(0, true, (1 << numBits) - 1, true);
- }
- return ranges;
- } else {
- /*
- * For all other sample models, the range is determined by the data type.
- * The following cases invoke the NumberRange constructor which best fit the
data type.
- */
- final int type = model.getDataType();
- switch (type) {
- case DataBuffer.TYPE_BYTE: range = NumberRange.create((short) 0,
true, (short) 0xFF, true); break;
- case DataBuffer.TYPE_USHORT: range = NumberRange.create( 0,
true, 0xFFFF, true); break;
- case DataBuffer.TYPE_SHORT: range = NumberRange.create(Short. MIN_VALUE,
true, Short. MAX_VALUE, true); break;
- case DataBuffer.TYPE_INT: range = NumberRange.create(Integer.MIN_VALUE,
true, Integer.MAX_VALUE, true); break;
- case DataBuffer.TYPE_FLOAT: range = NumberRange.create(Float. NEGATIVE_INFINITY,
false, Float. POSITIVE_INFINITY, false); break;
- case DataBuffer.TYPE_DOUBLE: range = NumberRange.create(Double. NEGATIVE_INFINITY,
false, Double. POSITIVE_INFINITY, false); break;
- default: throw new IllegalStateException(Errors.format(Errors.Keys.UnknownType_1,
type));
+ if (ranges.length != 0) {
+ final int dataType = model.getDataType();
+ if (ImageUtilities.isIntegerType(dataType)) {
+ int bandToDefine = 0, lastDefinedBand;
+ do {
+ final int size = model.getSampleSize(bandToDefine);
+ long minimum = 0;
+ long maximum = Numerics.bitmask(size) - 1;
+ if (dataType >= DataBuffer.TYPE_SHORT) {
+ maximum >>>= 1; // Convert unsigned
range to signed range.
+ minimum = ~maximum;
+ }
+ final NumberRange<?> range = (dataType == DataBuffer.TYPE_BYTE
|| dataType == DataBuffer.TYPE_SHORT)
+ ? NumberRange.create((short) minimum, true,
(short) maximum, true)
+ : NumberRange.create((int) minimum, true,
(int) maximum, true);
+ ranges[bandToDefine] = range;
+ /*
+ * Usually all bands have the same number of bits, and consequently the
same range of values.
+ * For handling this common case, loop below shares the same `NumberRange`
instance with all
+ * bands having same characteristic. If at least one band has a different
number of bits, the
+ * `bandToDefine` index will point to the first occurrence and the computation
is repeated.
+ */
+ lastDefinedBand = bandToDefine;
+ for (int band = ranges.length; --band > lastDefinedBand;) {
+ if (ranges[band] == null) {
+ if (model.getSampleSize(band) == size) {
+ ranges[band] = range;
+ } else {
+ bandToDefine = band;
+ }
+ }
+ }
+ } while (bandToDefine > lastDefinedBand);
+ } else {
+ Arrays.fill(ranges, (dataType == DataBuffer.TYPE_FLOAT)
+ ? NumberRange.create(Float. NEGATIVE_INFINITY, false, Float. POSITIVE_INFINITY,
false)
+ : NumberRange.create(Double.NEGATIVE_INFINITY, false, Double.POSITIVE_INFINITY,
false));
}
}
- Arrays.fill(ranges, range);
return ranges;
}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/DefaultIteratorTest.java
b/core/sis-feature/src/test/java/org/apache/sis/image/DefaultIteratorTest.java
index d0b6236..012302e 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/DefaultIteratorTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/DefaultIteratorTest.java
@@ -21,6 +21,7 @@ import java.awt.Point;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.DataBuffer;
+import java.awt.image.BandedSampleModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
@@ -28,6 +29,7 @@ import java.awt.image.WritableRenderedImage;
import java.nio.FloatBuffer;
import org.opengis.coverage.grid.SequenceType;
import org.apache.sis.util.ArraysExt;
+import org.apache.sis.measure.NumberRange;
import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.TestCase;
import org.junit.After;
@@ -370,6 +372,35 @@ public strictfp class DefaultIteratorTest extends TestCase {
}
/**
+ * Tests {@link PixelIterator#getSampleRanges()}.
+ */
+ @Test
+ public void testGetSampleRanges() {
+ for (int type = DataBuffer.TYPE_BYTE; type <= DataBuffer.TYPE_DOUBLE; type++)
{
+ createPixelIterator(WritableRaster.createWritableRaster(new BandedSampleModel(type,
1, 1, 3), null), null);
+ final NumberRange<?>[] ranges = iterator.getSampleRanges();
+ final Number min, max;
+ final Class<?> c;
+ switch (type) {
+ case DataBuffer.TYPE_BYTE: c = Short.class; min = (short) 0;
max = (short) 0xFF; break;
+ case DataBuffer.TYPE_USHORT: c = Integer.class; min = 0;
max = 0xFFFF; break;
+ case DataBuffer.TYPE_SHORT: c = Short.class; min = Short.MIN_VALUE;
max = Short.MAX_VALUE; break;
+ case DataBuffer.TYPE_INT: c = Integer.class; min = Integer.MIN_VALUE;
max = Integer.MAX_VALUE; break;
+ case DataBuffer.TYPE_FLOAT: c = Float.class; min = null;
max = null; break;
+ case DataBuffer.TYPE_DOUBLE: c = Double.class; min = null;
max = null; break;
+ default: throw new AssertionError(type);
+ }
+ assertEquals(ranges.length, 3);
+ for (final NumberRange<?> r : ranges) {
+ assertEquals(c, r.getElementType());
+ assertEquals(min, r.getMinValue());
+ assertEquals(max, r.getMaxValue());
+ }
+ iterator.close();
+ }
+ }
+
+ /**
* Verifies the sample value at current iterator position.
* If the iterator is writable, tests also setting a value.
*
|