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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 4049ee5d82 Add a DataType.UINT type for distinguishing whether Java2D 
`DataBuffer.TYPE_INT` should be interpreted as signed or unsigned. The sign of 
the integer type is ambiguous in Java2D, as it depends on the context. Methods 
were added in `DataType` for handling this ambiguity.
4049ee5d82 is described below

commit 4049ee5d8216fac74b93cd1ff7a2b71ac7404d99
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Dec 27 16:57:55 2024 +0100

    Add a DataType.UINT type for distinguishing whether Java2D 
`DataBuffer.TYPE_INT` should be interpreted as signed or unsigned.
    The sign of the integer type is ambiguous in Java2D, as it depends on the 
context.
    Methods were added in `DataType` for handling this ambiguity.
---
 .../org/apache/sis/image/BandAggregateLayout.java  |   8 +-
 .../apache/sis/image/BandedSampleConverter.java    |   2 +-
 .../main/org/apache/sis/image/DataType.java        | 217 ++++++++++++++++-----
 .../main/org/apache/sis/image/PixelIterator.java   |  12 +-
 .../main/org/apache/sis/image/ResampledImage.java  |   2 +-
 .../main/org/apache/sis/image/Transferer.java      |   6 +-
 .../apache/sis/image/privy/ColorModelBuilder.java  |   3 +-
 .../apache/sis/image/privy/ColorModelFactory.java  |   6 +-
 .../apache/sis/image/privy/ColorScaleBuilder.java  |   8 +-
 .../org/apache/sis/image/privy/ImageUtilities.java |  84 +-------
 .../org/apache/sis/image/privy/RasterFactory.java  |   7 +-
 .../apache/sis/image/privy/TilePlaceholder.java    |   5 +-
 .../test/org/apache/sis/image/DataTypeTest.java    |  28 ++-
 .../apache/sis/image/privy/ImageUtilitiesTest.java |  16 --
 .../org/apache/sis/storage/geotiff/DataSubset.java |   2 +-
 .../sis/storage/geotiff/ImageFileDirectory.java    |   2 +-
 .../org/apache/sis/storage/geotiff/Writer.java     |   4 +-
 .../storage/geotiff/inflater/CopyFromBytes.java    |   3 +-
 .../geotiff/inflater/HorizontalPredictor.java      |   5 +-
 .../geotiff/writer/HorizontalPredictor.java        |   5 +-
 .../storage/geotiff/writer/ReformattedImage.java   |   5 +-
 .../sis/storage/geotiff/writer/TileMatrix.java     |   1 +
 .../apache/sis/storage/netcdf/base/DataType.java   |   2 +-
 .../apache/sis/storage/base/TiledGridResource.java |   2 +-
 .../org/apache/sis/storage/esri/RasterStore.java   |   8 +-
 .../main/org/apache/sis/gui/coverage/GridView.java |   4 +-
 26 files changed, 247 insertions(+), 200 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 4d700e5aba..fdadc5e8cf 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
@@ -22,7 +22,6 @@ import java.awt.Point;
 import java.awt.Dimension;
 import java.awt.Rectangle;
 import java.awt.image.ColorModel;
-import java.awt.image.DataBuffer;
 import java.awt.image.RenderedImage;
 import java.awt.image.SampleModel;
 import java.awt.image.BandedSampleModel;
@@ -154,7 +153,7 @@ final class BandAggregateLayout {
         int tileHeight     = 0;
         int tileAlignX     = 0;
         int tileAlignY     = 0;
-        int commonDataType = DataBuffer.TYPE_UNDEFINED;
+        DataType commonDataType = null;
         for (final RenderedImage source : sources) {
             /*
              * Ensure that all images use the same data type. This is 
mandatory.
@@ -173,7 +172,7 @@ final class BandAggregateLayout {
                     allowSharing |= (domain == null);
                 }
             }
-            final int dataType = sm.getDataType();
+            final var dataType = DataType.forBands(sm);
             if (domain == null) {
                 domain = ImageUtilities.getBounds(source);
                 commonDataType = dataType;
@@ -234,9 +233,8 @@ final class BandAggregateLayout {
             final boolean exactTileSize = ((cx | cy) >>> Integer.SIZE) == 0;
             allowSharing &= exactTileSize;
             if (!allowSharing) scanlineStride = 0;      // Means to force the 
use of tile width.
-            final var dataType = DataType.forDataBufferType(commonDataType);
             final var layout = new ImageLayout(null, preferredTileSize, 
!exactTileSize, false, false, minTile);
-            sampleModel = layout.createBandedSampleModel(null, domain, 
dataType, numBands, scanlineStride);
+            sampleModel = layout.createBandedSampleModel(null, domain, 
commonDataType, numBands, scanlineStride);
         }
         this.bandsPerSource = bandsPerSource;
         this.sources        = sources;
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandedSampleConverter.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandedSampleConverter.java
index 25d5ae161c..348e3f4a27 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandedSampleConverter.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandedSampleConverter.java
@@ -148,7 +148,7 @@ class BandedSampleConverter extends WritableComputedImage {
             }
             if (!Double.isFinite(middle)) {
                 final SampleModel sm = source.getSampleModel();
-                if (ImageUtilities.isUnsignedType(sm)) {
+                if (DataType.isUnsigned(sm)) {
                     middle = Math.scalb(0.5, sm.getSampleSize(i));
                 } else {
                     middle = 0;
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java
index 0bb49fc4bb..caea60edb2 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java
@@ -16,62 +16,84 @@
  */
 package org.apache.sis.image;
 
+import java.awt.image.Raster;
 import java.awt.image.DataBuffer;
 import java.awt.image.RenderedImage;
 import java.awt.image.RasterFormatException;
+import java.awt.image.SampleModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
 import org.apache.sis.util.Numbers;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.image.privy.ImageUtilities;
 import org.apache.sis.feature.internal.Resources;
-import org.apache.sis.util.resources.Errors;
 import static 
org.apache.sis.util.privy.Numerics.MAX_INTEGER_CONVERTIBLE_TO_FLOAT;
 
 
 /**
  * Identification of the primitive type used for storing sample values in an 
image.
- * This is a type-safe version of the {@code TYPE_*} constants defined in 
{@link DataBuffer}.
+ * This is a type-safe version of the {@code TYPE_*} constants defined in 
{@link DataBuffer},
+ * except that this enumeration distinguishes signed and unsigned 32 bits 
integers.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.5
  * @since   1.1
  */
 public enum DataType {
-    /*
-     * Enumeration values must be declared in order of increasing 
`DataBuffer.TYPE_*` constant values,
-     * without skiping values. This requirement allow us to get `FOO.ordinal() 
== DataBuffer.TYPE_FOO`.
-     * This matching is verified by DataTypeTest.verifyOrdinalValues().
-     */
-
     /**
      * Unsigned 8-bits data.
+     * Mapped to {@link DataBuffer#TYPE_BYTE} in Java2D <abbr>API</abbr>.
      */
-    BYTE((byte) 0),
+    BYTE(DataBuffer.TYPE_BYTE, (byte) 0),
 
     /**
      * Unsigned 16-bits data.
+     * Mapped to {@link DataBuffer#TYPE_USHORT} in Java2D <abbr>API</abbr>.
      */
-    USHORT((short) 0),
+    USHORT(DataBuffer.TYPE_USHORT, (short) 0),
 
     /**
      * Signed 16-bits data.
+     * Mapped to {@link DataBuffer#TYPE_SHORT} in Java2D <abbr>API</abbr>.
+     */
+    SHORT(DataBuffer.TYPE_SHORT, (short) 0),
+
+    /**
+     * Signed 32-bits data.
+     * Mapped to {@link DataBuffer#TYPE_INT} in Java2D <abbr>API</abbr>.
+     * Note that the sign of the latter Java2D type is ambiguous.
+     * See {@link #forDataBufferType(int)} for more information.
      */
-    SHORT((short) 0),
+    INT(DataBuffer.TYPE_INT, 0),
 
     /**
-     * Signed 32-bits data. Also used for storing unsigned data; the Java2D 
API such as
-     * {@link java.awt.image.Raster#getSample(int, int, int)} cannot 
distinguish the two cases.
+     * Unsigned 32-bits data.
+     * Mapped to {@link DataBuffer#TYPE_INT} in Java2D <abbr>API</abbr>.
+     * Note that the sign of the latter Java2D type is ambiguous.
+     * See {@link #forDataBufferType(int)} for more information.
+     *
+     * @since 1.5
      */
-    INT(0),
+    UINT(DataBuffer.TYPE_INT, 0),
 
     /**
      * Single precision (32-bits) floating point data.
+     * Mapped to {@link DataBuffer#TYPE_FLOAT} in Java2D <abbr>API</abbr>.
      */
-    FLOAT(Float.NaN),
+    FLOAT(DataBuffer.TYPE_FLOAT, Float.NaN),
 
     /**
      * Double precision (64-bits) floating point data.
+     * Mapped to {@link DataBuffer#TYPE_DOUBLE} in Java2D <abbr>API</abbr>.
+     */
+    DOUBLE(DataBuffer.TYPE_DOUBLE, Double.NaN);
+
+    /**
+     * The data buffer type as one of the {@code DataBuffer.TYPE_*} constants.
      */
-    DOUBLE(Double.NaN);
+    private final int dataType;
 
     /**
      * The default fill value, which is 0 for integer types and NaN for 
floating point types.
@@ -82,32 +104,63 @@ public enum DataType {
     private final Number fillValue;
 
     /**
-     * All enumeration values, cached for avoiding to recreate this array
-     * on every {@link #forDataBufferType(int)} call.
+     * Enumeration values for {@code DataBuffer.TYPE_*} constants.
+     * The unsigned variant of {@code TYPE_INT} is discarded, keeping the 
signed variant.
+     *
+     * @see #forDataBufferType(int)
      */
-    private static final DataType[] VALUES = values();
+    private static final DataType[] VALUES = ArraysExt.remove(values(), 
DataBuffer.TYPE_INT + 1, 1);
 
     /**
-     * Creates a new enumeration.
+     * Creates a new enumeration value.
      */
-    private DataType(final Number fillValue) {
+    private DataType(final int dataType, final Number fillValue) {
+        this.dataType  = dataType;
         this.fillValue = fillValue;
     }
 
     /**
-     * Returns the data type of the bands in the given image. This is often the
-     * {@linkplain java.awt.image.SampleModel#getDataType() storage data 
type}, but not necessarily.
-     * For example if an ARGB image uses a storage mode where the sample 
values in the 4 bands are
-     * {@linkplain java.awt.image.SinglePixelPackedSampleModel packed in a 
single 32-bits integer},
-     * then this method returns {@link #BYTE} (the type of a single band), not 
{@link #INT}
-     * (the type of a whole pixel).
+     * Returns the data type of the bands in the given image.
+     * This is often the {@linkplain SampleModel#getDataType() storage data 
type}, but not necessarily.
+     * See {@link #forBands(SampleModel)} for more information.
      *
-     * @param  image  the image for which to get the type.
-     * @return type of the given image (never {@code null}).
+     * @param  image  the image for which to get the band data type.
+     * @return type of sample values in the given image (never {@code null}).
      * @throws RasterFormatException if the image does not use a recognized 
data type.
      */
     public static DataType forBands(final RenderedImage image) {
-        return 
forDataBufferType(ImageUtilities.getBandType(image.getSampleModel()));
+        return forBands(image.getSampleModel());
+    }
+
+    /**
+     * Returns the data type of the bands managed by the given the sample 
model.
+     * This is often the {@linkplain SampleModel#getDataType() storage data 
type}, but not necessarily.
+     * For example, if an <abbr>ARGB</abbr> image uses a storage mode where 
the sample values in the
+     * four bands are {@linkplain SinglePixelPackedSampleModel packed in a 
single 32-bits integer},
+     * then this method returns {@link #BYTE} (the type of a single band) 
rather than {@link #INT}
+     * (the type of a whole pixel).
+     *
+     * @param  sm  the sample model for which to get the band data type.
+     * @return type of sample values in the bands managed by the given sample 
model (never {@code null}).
+     * @throws RasterFormatException if the sample model does not use a 
recognized data type.
+     *
+     * @since 1.5
+     */
+    public static DataType forBands(final SampleModel sm) {
+        final int type = sm.getDataType();
+        if (type > DataBuffer.TYPE_BYTE && type <= DataBuffer.TYPE_INT) {
+            int numBits = 0;
+            for (int i=sm.getNumBands(); --i >= 0;) {
+                numBits = Math.max(numBits, sm.getSampleSize(i));
+            }
+            if (isUnsigned(sm)) {
+                return (numBits <= Byte.SIZE) ? BYTE :
+                       (numBits <= Short.SIZE) ? USHORT : UINT;
+            } else {
+                return (numBits <= Short.SIZE) ? SHORT : INT;
+            }
+        }
+        return forDataBufferType(type);
     }
 
     /**
@@ -117,7 +170,7 @@ public enum DataType {
      * <ul>
      *   <li>If {@code asInteger} is {@code false}, then this method returns
      *       {@link #FLOAT} or {@link #DOUBLE} depending on the range 
type.</li>
-     *   <li>Otherwise this method treats the floating point values as if they
+     *   <li>Otherwise, this method treats the floating point values as if they
      *       were integers, with minimum value rounded toward negative infinity
      *       and maximum value rounded toward positive infinity.</li>
      * </ul>
@@ -147,13 +200,13 @@ public enum DataType {
             }
         }
         /*
-         * Check most common types first. If the range could be both signed 
and unsigned short,
-         * give precedence to unsigned short because it works better with 
IndexColorModel.
+         * Check most common types first. If the range could be both signed 
and unsigned,
+         * give precedence to unsigned types because it works better with 
IndexColorModel.
          * If a bounds is NaN, fallback on TYPE_FLOAT.
          */
         final DataType type;
-        if (min >= -0.5 && max < 0xFFFF + 0.5) {
-            type = (max < 0xFF + 0.5) ? BYTE : USHORT;
+        if (min >= -0.5 && max < 0xFFFFFFFF + 0.5) {
+            type = (max < 0xFF + 0.5) ? BYTE : (max < 0xFFFF + 0.5) ? USHORT : 
UINT;
         } else if (min >= Short.MIN_VALUE - 0.5 && max < Short.MAX_VALUE + 
0.5) {
             type = SHORT;
         } else if (min >= Integer.MIN_VALUE - 0.5 && max < Integer.MAX_VALUE + 
0.5) {
@@ -177,7 +230,7 @@ public enum DataType {
         switch (Numbers.getEnumConstant(type)) {
             case Numbers.BYTE:    return unsigned ? BYTE   : SHORT;
             case Numbers.SHORT:   return unsigned ? USHORT : SHORT;
-            case Numbers.INTEGER: if (unsigned) break; else return INT;
+            case Numbers.INTEGER: return unsigned ? UINT   : INT;
             case Numbers.FLOAT:   return FLOAT;
             case Numbers.DOUBLE:  return DOUBLE;
         }
@@ -189,6 +242,24 @@ public enum DataType {
      * This method is the converse of {@link #toDataBufferType()}.
      * It is provided for interoperability with Java2D.
      *
+     * <h4>32 bit integers</h4>
+     * The case of {@link DataBuffer#TYPE_INT} is ambiguous.
+     * Whether this type is considered as signed or unsigned depends on the 
context:
+     * <ul>
+     *   <li>The sign is indistinguishable when using Java2D <abbr>API</abbr> 
working on integer values
+     *     such as the {@link Raster#getSample(int, int, int) 
Raster.getSample(…)} method.</li>
+     *   <li>When converted to floating point values by Java2D 
<abbr>API</abbr> such as
+     *     {@link Raster#getSampleFloat(int, int, int) 
Raster.getSampleFloat(…)}, sample values are:
+     *     <ul>
+     *       <li>Unsigned when the raster uses {@link 
SinglePixelPackedSampleModel} or {@link MultiPixelPackedSampleModel}.</li>
+     *       <li>Signed when the raster uses any other type of sample 
model.</li>
+     *     </ul>
+     *   </li>
+     *   <li>The type is <em>unsigned</em> when converted to a color by {@link 
java.awt.image.ComponentColorModel}.</li>
+     * </ul>
+     *
+     * For resolving above ambiguities, consider using {@link 
#forBands(SampleModel)} instead of this method.
+     *
      * @param  type  one of {@code DataBuffer.TYPE_*} constants.
      * @return the data type (never {@code null}) for the given data buffer 
type.
      * @throws RasterFormatException if the given type is not a recognized 
{@code DataBuffer.TYPE_*} constant.
@@ -213,7 +284,7 @@ public enum DataType {
      *   <tr><td>4</td>                     <td>{@code false}</td>  <td>{@code 
false}</td></tr>
      *   <tr><td>{@value Byte#SIZE}</td>    <td>{@code false}</td>  <td>{@code 
false}</td></tr>
      *   <tr><td>{@value Short#SIZE}</td>   <td>{@code false}</td>  <td>{@code 
false} or {@code true}</td></tr>
-     *   <tr><td>{@value Integer#SIZE}</td> <td>{@code false}</td>  <td>{@code 
true}</td></tr>
+     *   <tr><td>{@value Integer#SIZE}</td> <td>{@code false}</td>  <td>{@code 
false} or {@code true}</td></tr>
      *   <tr><td>{@value Float#SIZE}</td>   <td>{@code true}</td>   
<td>ignored</td></tr>
      *   <tr><td>{@value Double#SIZE}</td>  <td>{@code true}</td>   
<td>ignored</td></tr>
      * </table>
@@ -238,14 +309,12 @@ public enum DataType {
                 case Double.SIZE: return DOUBLE;
             }
         } else {
-            if (size == Short.SIZE) {
-                return signed ? SHORT : USHORT;
+            switch (size) {
+                case Short.SIZE:   return signed ? SHORT : USHORT;
+                case Integer.SIZE: return signed ? INT   : UINT;
             }
-            final boolean isInt = (size == Integer.SIZE);
-            if (isInt || size <= Byte.SIZE) {
-                if (isInt == signed) {
-                    return isInt ? INT : BYTE;
-                }
+            if (size <= Byte.SIZE) {
+                if (!signed) return BYTE;
                 argument = "signed";
                 value    =  signed;
             }
@@ -259,7 +328,7 @@ public enum DataType {
      * @return size in bits of this data type.
      */
     public final int size() {
-        return DataBuffer.getDataTypeSize(ordinal());
+        return DataBuffer.getDataTypeSize(dataType);
     }
 
     /**
@@ -276,22 +345,65 @@ public enum DataType {
 
     /**
      * Returns whether this type is an unsigned integer type.
-     * Unsigned types are {@link #BYTE} and {@link #USHORT}.
+     * Unsigned types are {@link #BYTE}, {@link #USHORT} and {@link #UINT}.
      *
      * @return {@code true} if this type is an unsigned integer type.
      */
     public final boolean isUnsigned() {
-        return ordinal() <= DataBuffer.TYPE_USHORT;
+        return dataType <= DataBuffer.TYPE_USHORT || this == UINT;
+    }
+
+    /**
+     * Returns {@code true} if the type of sample values is an unsigned 
integer type.
+     * Returns {@code false} if the type is a floating point type or in case 
of doubt
+     * (e.g. for {@link DataBuffer#TYPE_UNDEFINED}).
+     *
+     * @param  sm  the sample model, or {@code null}.
+     * @return whether the given sample model provides unsigned sample values.
+     *
+     * @since 1.5
+     */
+    public static boolean isUnsigned(final SampleModel sm) {
+        if (sm != null) {
+            final int dataType = sm.getDataType();
+            if (dataType >= DataBuffer.TYPE_BYTE) {
+                if (dataType <= DataBuffer.TYPE_USHORT) return true;
+                if (dataType <= DataBuffer.TYPE_INT) {
+                    /*
+                     * Typical case: 4 bands (ARGB) stored in a single data 
element of type `int`.
+                     * The javadoc of those classes explain how to unpack the 
sample values,
+                     * and the result is always unsigned.
+                     */
+                    return (sm instanceof SinglePixelPackedSampleModel) ||
+                           (sm instanceof MultiPixelPackedSampleModel);
+                }
+            }
+        }
+        return false;
     }
 
     /**
      * Returns whether this type is an integer type, signed or not.
-     * Integer types are {@link #BYTE}, {@link #USHORT}, {@link #SHORT} and 
{@link #INT}.
+     * Integer types are {@link #BYTE}, {@link #USHORT}, {@link #SHORT}, 
{@link #INT} and {@link #UINT}.
      *
      * @return {@code true} if this type is an integer type.
      */
     public final boolean isInteger() {
-        return ordinal() <= DataBuffer.TYPE_INT;
+        return dataType <= DataBuffer.TYPE_INT;
+    }
+
+    /**
+     * Returns {@code true} if the given sample model uses an integer type.
+     * Returns {@code false} if the type is a floating point type or in case
+     * of doubt (e.g. for {@link DataBuffer#TYPE_UNDEFINED}).
+     *
+     * @param  sm  the sample model, or {@code null}.
+     * @return whether the given sample model works on integer values.
+     *
+     * @since 1.5
+     */
+    public static boolean isInteger(final SampleModel sm) {
+        return (sm != null) && ImageUtilities.isIntegerType(sm.getDataType());
     }
 
     /**
@@ -299,7 +411,7 @@ public enum DataType {
      * without precision lost. This method returns:
      *
      * <ul>
-     *   <li>{@link #DOUBLE} if this data type is {@link #DOUBLE} or {@link 
#INT}.</li>
+     *   <li>{@link #DOUBLE} if this data type is {@link #DOUBLE}, {@link 
#INT} or {@link #UINT}.</li>
      *   <li>{@link #FLOAT} for all other types.</li>
      * </ul>
      *
@@ -310,8 +422,7 @@ public enum DataType {
      *         which can store all values of this type without any lost.
      */
     public final DataType toFloat() {
-        final int type = ordinal();
-        return (type < DataBuffer.TYPE_INT || type == DataBuffer.TYPE_FLOAT) ? 
FLOAT : DOUBLE;
+        return (dataType < DataBuffer.TYPE_INT || dataType == 
DataBuffer.TYPE_FLOAT) ? FLOAT : DOUBLE;
     }
 
     /**
@@ -322,7 +433,7 @@ public enum DataType {
      * @return one of the {@link DataBuffer} constants.
      */
     public final int toDataBufferType() {
-        return ordinal();
+        return dataType;
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
index cb5bd3c4cd..4155780ff0 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
@@ -551,17 +551,17 @@ public class PixelIterator {
     }
 
     /**
-     * Returns the type used for storing data in the raster buffer.
-     * The data type identifies the {@link DataBuffer} subclass used for 
storage.
+     * Returns the type of sample values. This is usually the {@link 
DataBuffer} type,
+     * but may be a smaller type if many values are packed in each data buffer 
element.
      *
-     * @return the type used for storing data in the raster buffer.
+     * @return the type of sample values.
      *
-     * @see SampleModel#getDataType()
+     * @see DataType#forBands(SampleModel)
      *
      * @since 1.2
      */
     public DataType getDataType() {
-        return DataType.forDataBufferType(getSampleModel().getDataType());
+        return DataType.forBands(getSampleModel());
     }
 
     /**
@@ -618,7 +618,7 @@ public class PixelIterator {
                     final int size = model.getSampleSize(bandToDefine);
                     long minimum = 0;
                     long maximum = Numerics.bitmask(size) - 1;
-                    if (!ImageUtilities.isUnsignedType(model)) {
+                    if (!DataType.isUnsigned(model)) {
                         maximum >>>= 1;                                 // 
Convert unsigned range to signed range.
                         minimum = ~maximum;
                     }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ResampledImage.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ResampledImage.java
index 2ecc0bb5dd..8d0b9d4624 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ResampledImage.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ResampledImage.java
@@ -754,7 +754,7 @@ public class ResampledImage extends ComputedImage {
                 for (int b=0; b<numBands; b++) {
                     maxValues[b] = Numerics.bitmask(sm.getSampleSize(b)) - 1;
                 }
-                if (!ImageUtilities.isUnsignedType(sm)) {
+                if (!DataType.isUnsigned(sm)) {
                     for (int b=0; b<numBands; b++) {
                         minValues[b] = ~(maxValues[b] >>>= 1);      // Convert 
unsigned type to signed type range.
                     }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Transferer.java 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Transferer.java
index 6c953309cb..fc6a002d5a 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Transferer.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/Transferer.java
@@ -562,7 +562,7 @@ abstract class Transferer {
      * @return object to use for applying the operation.
      */
     static Transferer create(final Raster source, final WritableRaster target, 
final Rectangle aoi) {
-        switch (ImageUtilities.getBandType(target.getSampleModel())) {
+        switch (DataType.forBands(target.getSampleModel()).toDataBufferType()) 
{
             case DataBuffer.TYPE_DOUBLE: {
                 if (isDirect(target, aoi)) {
                     return new DoubleToDirect(source, target, aoi);
@@ -570,7 +570,7 @@ abstract class Transferer {
                 break;
             }
             case DataBuffer.TYPE_FLOAT: {
-                switch (ImageUtilities.getBandType(source.getSampleModel())) {
+                switch 
(DataType.forBands(source.getSampleModel()).toDataBufferType()) {
                     case DataBuffer.TYPE_BYTE:
                     case DataBuffer.TYPE_SHORT:
                     case DataBuffer.TYPE_USHORT:        // TODO: consider 
using IntegerToDirect here.
@@ -606,7 +606,7 @@ abstract class Transferer {
      * to the {@code float} type may lost precision digits.
      */
     private static boolean singlePrecision(final Raster source) {
-        switch (ImageUtilities.getBandType(source.getSampleModel())) {
+        switch (DataType.forBands(source.getSampleModel()).toDataBufferType()) 
{
             case DataBuffer.TYPE_BYTE:
             case DataBuffer.TYPE_USHORT:
             case DataBuffer.TYPE_SHORT:
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelBuilder.java
index e970e8780a..89988e5d4d 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelBuilder.java
@@ -28,6 +28,7 @@ import java.awt.image.DataBuffer;
 import java.awt.image.SampleModel;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ArraysExt;
+import org.apache.sis.image.DataType;
 
 
 /**
@@ -279,7 +280,7 @@ public final class ColorModelBuilder {
      * @throws IllegalArgumentException if any argument specified to the 
builder is invalid.
      */
     public ColorModel createRGB(final SampleModel targetModel) {
-check:  if (ImageUtilities.isIntegerType(targetModel)) {
+check:  if (DataType.isInteger(targetModel)) {
             final int numBands = targetModel.getNumBands();
             switch (numBands) {
                 case 3:  alphaBand = -1; break;
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
index e497dc2482..0e4322ef10 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
@@ -579,9 +579,9 @@ public final class ColorModelFactory {
         } else {
             minimum = 0;
             maximum = 1;
-            if (ImageUtilities.isIntegerType(model)) {
+            if (DataType.isInteger(model)) {
                 long max = Numerics.bitmask(model.getSampleSize(visibleBand)) 
- 1;
-                if (!ImageUtilities.isUnsignedType(model)) {
+                if (!DataType.isUnsigned(model)) {
                     max >>>= 1;
                     minimum = ~max;         // Tild operator, not minus.
                 }
@@ -792,7 +792,7 @@ public final class ColorModelFactory {
      */
     @Override
     public String toString() {
-        final StringBuilder buffer = new 
StringBuilder(Strings.toString(getClass(),
+        final var buffer = new StringBuilder(Strings.toString(getClass(),
                 "dataType", DataType.forDataBufferType(dataType), "numBands", 
numBands, "visibleBand", visibleBand,
                 "range", NumberRange.create(minimum, true, maximum, false)));
         final int n = (pieceStarts != null) ? pieceStarts.length : 0;
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorScaleBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorScaleBuilder.java
index 2e83936957..f813f2a9da 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorScaleBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorScaleBuilder.java
@@ -276,7 +276,7 @@ public final class ColorScaleBuilder {
                      * If the model uses floating point values and there is no 
"no data" category, add one.
                      * We force a "no data" category because floating point 
values may be NaN.
                      */
-                    if (missingNodata && (model == null || 
!ImageUtilities.isIntegerType(model))) {
+                    if (missingNodata && (model == null || 
!DataType.isInteger(model))) {
                         final int count = entries.length;
                         entries = Arrays.copyOf(entries, count + 1);
                         entries[count] = new ColorsForRange(TRANSPARENT,
@@ -303,10 +303,10 @@ public final class ColorScaleBuilder {
      */
     public boolean initialize(final SampleModel source, final int band) {
         checkInitializationStatus(false);
-        if (ImageUtilities.isIntegerType(source)) {
+        if (DataType.isInteger(source)) {
             long minimum = 0;
             long maximum = Numerics.bitmask(source.getSampleSize(band)) - 1;
-            if (!ImageUtilities.isUnsignedType(source)) {
+            if (!DataType.isUnsigned(source)) {
                 maximum >>>= 1;
                 minimum = ~maximum;
             }
@@ -700,7 +700,7 @@ reuse:  if (source != null) {
      * @return a color model using a data type inferred from the given sample 
model.
      */
     public ColorModel createColorModel(final SampleModel target, final int 
numBands, final int visibleBand) {
-        return 
createColorModel(DataType.forDataBufferType(ImageUtilities.getBandType(target)),
 numBands, visibleBand);
+        return createColorModel(DataType.forBands(target), numBands, 
visibleBand);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
index c2e6dbd489..18f6eeea7b 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
@@ -30,13 +30,13 @@ import java.awt.image.RenderedImage;
 import java.awt.image.Raster;
 import java.awt.image.SampleModel;
 import java.awt.image.SinglePixelPackedSampleModel;
-import java.awt.image.MultiPixelPackedSampleModel;
 import static java.lang.Math.abs;
 import static java.lang.Math.rint;
 import static java.lang.Math.floorDiv;
 import static java.lang.Math.toIntExact;
 import static java.lang.Math.multiplyFull;
 import org.apache.sis.feature.internal.Resources;
+import org.apache.sis.image.DataType;
 import org.apache.sis.image.PlanarImage;
 import org.apache.sis.system.Modules;
 import org.apache.sis.util.Numbers;
@@ -176,47 +176,6 @@ public final class ImageUtilities extends Static {
         return 0;
     }
 
-    /**
-     * Returns the data type of bands in rasters that use the given sample 
model.
-     * If each band is stored in its own {@link DataBuffer} element, then this 
method returns the same value
-     * as {@link SampleModel#getDataType()}. But if multiple sample values are 
packed in a single data element
-     * ({@link SinglePixelPackedSampleModel} or {@link 
MultiPixelPackedSampleModel}), then this method returns
-     * a smaller data type. As a general rule, this method returns the 
smallest data type capable to store all
-     * sample values with a {@link java.awt.image.BandedSampleModel}.
-     *
-     * @param  sm  the sample model for which to get the band type, or {@code 
null}.
-     * @return the data type, or {@link DataBuffer#TYPE_UNDEFINED} if unknown.
-     *
-     * @see #isIntegerType(int)
-     * @see #isUnsignedType(SampleModel)
-     */
-    public static int getBandType(final SampleModel sm) {
-        if (sm == null) {
-            return DataBuffer.TYPE_UNDEFINED;
-        }
-        final int type = sm.getDataType();
-        if (!isIntegerType(type)) {
-            return type;
-        }
-        final int maxBits = Math.min(DataBuffer.getDataTypeSize(type), 
Short.SIZE + 1);
-        int numBits = 0;
-        for (int i=sm.getNumBands(); --i >= 0;) {
-            final int n = sm.getSampleSize(i);
-            if (n > numBits) {
-                if (n >= maxBits) {
-                    return type;
-                }
-                numBits = n;
-            }
-        }
-        final boolean isUnsignedType = (type <= DataBuffer.TYPE_USHORT)
-                        || (sm instanceof SinglePixelPackedSampleModel)
-                        || (sm instanceof MultiPixelPackedSampleModel);
-
-        return isUnsignedType ? (numBits <= Byte.SIZE ? DataBuffer.TYPE_BYTE : 
DataBuffer.TYPE_USHORT)
-                              : DataBuffer.TYPE_SHORT;
-    }
-
     /**
      * Names of {@link DataBuffer} types.
      */
@@ -419,45 +378,6 @@ public final class ImageUtilities extends Static {
         return dataType >= DataBuffer.TYPE_BYTE && dataType <= 
DataBuffer.TYPE_INT;
     }
 
-    /**
-     * Returns {@code true} if the given sample model uses an integer type.
-     * Returns {@code false} if the type is a floating point type or in case
-     * of doubt (e.g. for {@link DataBuffer#TYPE_UNDEFINED}).
-     *
-     * @param  sm  the sample model, or {@code null}.
-     * @return whether the given sample model is for integer values.
-     */
-    public static boolean isIntegerType(final SampleModel sm) {
-        return (sm != null) && isIntegerType(sm.getDataType());
-    }
-
-    /**
-     * Returns {@code true} if the type of sample values is an unsigned 
integer type.
-     * Returns {@code false} if the type is a floating point type or in case 
of doubt
-     * (e.g. for {@link DataBuffer#TYPE_UNDEFINED}).
-     *
-     * @param  sm  the sample model, or {@code null}.
-     * @return whether the given sample model provides unsigned sample values.
-     */
-    public static boolean isUnsignedType(final SampleModel sm) {
-        if (sm != null) {
-            final int dataType = sm.getDataType();
-            if (dataType >= DataBuffer.TYPE_BYTE) {
-                if (dataType <= DataBuffer.TYPE_USHORT) return true;
-                if (dataType <= DataBuffer.TYPE_INT) {
-                    /*
-                     * Typical case: 4 bands (ARGB) stored in a single data 
element of type `int`.
-                     * The javadoc of those classes explain how to unpack the 
sample values,
-                     * and the result is always unsigned.
-                     */
-                    return (sm instanceof SinglePixelPackedSampleModel) ||
-                           (sm instanceof MultiPixelPackedSampleModel);
-                }
-            }
-        }
-        return false;
-    }
-
     /**
      * Returns whether samples values stored using {@code source} model can be 
converted to {@code target} model
      * without data lost. This method verifies the number of bands and the 
size of data in each band.
@@ -491,7 +411,7 @@ public final class ImageUtilities extends Static {
                  *   - Conversion from `int` to `float` can loose significant 
digits.
                  *   - Conversion from signed short to unsigned short (or 
conversely) can change values.
                  */
-                if (sourceIsInteger != targetIsInteger || 
isUnsignedType(source) != isUnsignedType(target)) {
+                if (sourceIsInteger != targetIsInteger || 
DataType.isUnsigned(source) != DataType.isUnsigned(target)) {
                     return false;
                 }
             }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/RasterFactory.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/RasterFactory.java
index caa5b1edaf..1fb7e06a37 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/RasterFactory.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/RasterFactory.java
@@ -211,9 +211,10 @@ public final class RasterFactory extends Static {
      */
     public static Buffer createBuffer(final DataType dataType, final int 
capacity) {
         switch (dataType) {
+            case BYTE:   return ByteBuffer  .allocate(capacity);
             case USHORT: // Fallthrough
             case SHORT:  return ShortBuffer .allocate(capacity);
-            case BYTE:   return ByteBuffer  .allocate(capacity);
+            case UINT:   // Fallthrough
             case INT:    return IntBuffer   .allocate(capacity);
             case FLOAT:  return FloatBuffer .allocate(capacity);
             case DOUBLE: return DoubleBuffer.allocate(capacity);
@@ -268,10 +269,11 @@ public final class RasterFactory extends Static {
         final int numBands = data.length;
         final Object[] arrays;
         switch (dataType) {
+            case BYTE:   arrays = new byte  [numBands][]; break;
             case USHORT: // fall through
             case SHORT:  arrays = new short [numBands][]; break;
+            case UINT:   // fall through
             case INT:    arrays = new int   [numBands][]; break;
-            case BYTE:   arrays = new byte  [numBands][]; break;
             case FLOAT:  arrays = new float [numBands][]; break;
             case DOUBLE: arrays = new double[numBands][]; break;
             default: throw new AssertionError(dataType);
@@ -293,6 +295,7 @@ public final class RasterFactory extends Static {
             case BYTE:   return new DataBufferByte  (  (byte[][]) arrays, 
length, offsets);
             case SHORT:  return new DataBufferShort ( (short[][]) arrays, 
length, offsets);
             case USHORT: return new DataBufferUShort( (short[][]) arrays, 
length, offsets);
+            case UINT:   // Fall through
             case INT:    return new DataBufferInt   (   (int[][]) arrays, 
length, offsets);
             case FLOAT:  return new DataBufferFloat ( (float[][]) arrays, 
length, offsets);
             case DOUBLE: return new DataBufferDouble((double[][]) arrays, 
length, offsets);
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/TilePlaceholder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/TilePlaceholder.java
index 66e60730df..65b4963e1f 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/TilePlaceholder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/TilePlaceholder.java
@@ -34,6 +34,7 @@ import org.apache.sis.util.Workaround;
 import org.apache.sis.util.collection.WeakHashSet;
 import org.apache.sis.util.privy.Numerics;
 import org.apache.sis.system.ReferenceQueueConsumer;
+import org.apache.sis.image.DataType;
 
 
 /**
@@ -286,8 +287,8 @@ public class TilePlaceholder {
         WithCross(final RenderedImage image) {
             super(image.getSampleModel());
             samples = new double[model.getNumBands()];
-            if (ImageUtilities.isIntegerType(model)) {
-                final boolean isUnsigned = 
ImageUtilities.isUnsignedType(model);
+            if (DataType.isInteger(model)) {
+                final boolean isUnsigned = DataType.isUnsigned(model);
                 for (int i=0; i<samples.length; i++) {
                     int size = model.getSampleSize(i);
                     if (!isUnsigned) size--;
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/DataTypeTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/DataTypeTest.java
index ae429149a0..ae0728b148 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/DataTypeTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/DataTypeTest.java
@@ -18,6 +18,9 @@ package org.apache.sis.image;
 
 import java.awt.image.DataBuffer;
 import java.awt.image.RasterFormatException;
+import java.awt.image.SampleModel;
+import java.awt.image.BandedSampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
 
 // Test dependencies
 import org.junit.jupiter.api.Test;
@@ -39,14 +42,15 @@ public final class DataTypeTest extends TestCase {
     }
 
     /**
-     * Verifies that {@link DataType} ordinal values match {@link DataBuffer} 
constant values.
+     * Verifies {@link DataType#toDataBufferType()}.
      */
     @Test
-    public void verifyOrdinalValues() {
+    public void verifyToDataBufferType() {
         assertEquals(DataBuffer.TYPE_BYTE  , DataType.BYTE  
.toDataBufferType());
         assertEquals(DataBuffer.TYPE_USHORT, 
DataType.USHORT.toDataBufferType());
         assertEquals(DataBuffer.TYPE_SHORT , DataType.SHORT 
.toDataBufferType());
         assertEquals(DataBuffer.TYPE_INT   , DataType.INT   
.toDataBufferType());
+        assertEquals(DataBuffer.TYPE_INT   , DataType.UINT  
.toDataBufferType());
         assertEquals(DataBuffer.TYPE_FLOAT , DataType.FLOAT 
.toDataBufferType());
         assertEquals(DataBuffer.TYPE_DOUBLE, 
DataType.DOUBLE.toDataBufferType());
     }
@@ -60,6 +64,7 @@ public final class DataTypeTest extends TestCase {
         assertEquals(Short  .SIZE, DataType.USHORT.size());
         assertEquals(Short  .SIZE, DataType.SHORT .size());
         assertEquals(Integer.SIZE, DataType.INT   .size());
+        assertEquals(Integer.SIZE, DataType.UINT  .size());
         assertEquals(Float  .SIZE, DataType.FLOAT .size());
         assertEquals(Double .SIZE, DataType.DOUBLE.size());
     }
@@ -73,6 +78,7 @@ public final class DataTypeTest extends TestCase {
         assertTrue (DataType.USHORT.isUnsigned());
         assertFalse(DataType.SHORT .isUnsigned());
         assertFalse(DataType.INT   .isUnsigned());
+        assertTrue (DataType.UINT  .isUnsigned());
         assertFalse(DataType.FLOAT .isUnsigned());
         assertFalse(DataType.DOUBLE.isUnsigned());
     }
@@ -86,6 +92,7 @@ public final class DataTypeTest extends TestCase {
         assertTrue (DataType.USHORT.isInteger());
         assertTrue (DataType.SHORT .isInteger());
         assertTrue (DataType.INT   .isInteger());
+        assertTrue (DataType.UINT  .isInteger());
         assertFalse(DataType.FLOAT .isInteger());
         assertFalse(DataType.DOUBLE.isInteger());
     }
@@ -99,6 +106,7 @@ public final class DataTypeTest extends TestCase {
         assertEquals(DataType.FLOAT,  DataType.USHORT.toFloat());
         assertEquals(DataType.FLOAT,  DataType.SHORT .toFloat());
         assertEquals(DataType.DOUBLE, DataType.INT   .toFloat());
+        assertEquals(DataType.DOUBLE, DataType.UINT  .toFloat());
         assertEquals(DataType.FLOAT,  DataType.FLOAT .toFloat());
         assertEquals(DataType.DOUBLE, DataType.DOUBLE.toFloat());
     }
@@ -113,6 +121,7 @@ public final class DataTypeTest extends TestCase {
         assertEquals(DataType.USHORT, DataType.forNumberOfBits(Short.SIZE,   
false, false));
         assertEquals(DataType.SHORT,  DataType.forNumberOfBits(Short.SIZE,   
false, true));
         assertEquals(DataType.INT,    DataType.forNumberOfBits(Integer.SIZE, 
false, true));
+        assertEquals(DataType.UINT,   DataType.forNumberOfBits(Integer.SIZE, 
false, false));
         assertEquals(DataType.FLOAT,  DataType.forNumberOfBits(Float.SIZE,   
true,  true));
         assertEquals(DataType.DOUBLE, DataType.forNumberOfBits(Double.SIZE,  
true,  true));
 
@@ -121,4 +130,19 @@ public final class DataTypeTest extends TestCase {
                 "Signed bytes should be invalid.");
         assertMessageContains(e, "signed", "true");
     }
+
+    /**
+     * Tests {@link DataType#forBands(SampleModel)}.
+     */
+    @Test
+    public void testForBands() {
+        SampleModel sm = new BandedSampleModel(DataBuffer.TYPE_INT, 1, 1, 3);
+        assertEquals(DataType.INT, DataType.forBands(sm));
+
+        sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, 1, 1, new 
int[] {0x7F0000, 0x00FF00, 0x00007F});
+        assertEquals(DataType.BYTE, DataType.forBands(sm));
+
+        sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, 1, 1, new 
int[] {0x7F0000, 0x00FF80, 0x00007F});
+        assertEquals(DataType.USHORT, DataType.forBands(sm));
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
index 5e18396cbe..b8b00c3b7b 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
@@ -24,7 +24,6 @@ import java.awt.image.RenderedImage;
 import java.awt.image.BufferedImage;
 import java.awt.image.SampleModel;
 import java.awt.image.BandedSampleModel;
-import java.awt.image.SinglePixelPackedSampleModel;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.resources.Vocabulary;
 import static org.apache.sis.util.privy.Numerics.COMPARISON_THRESHOLD;
@@ -70,21 +69,6 @@ public final class ImageUtilitiesTest extends TestCase {
         assertEquals(3, bounds.height);
     }
 
-    /**
-     * Tests {@link ImageUtilities#getBandType(SampleModel)}.
-     */
-    @Test
-    public void testGetBandType() {
-        SampleModel sm = new BandedSampleModel(DataBuffer.TYPE_INT, 1, 1, 3);
-        assertEquals(DataBuffer.TYPE_INT, ImageUtilities.getBandType(sm));
-
-        sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, 1, 1, new 
int[] {0x7F0000, 0x00FF00, 0x00007F});
-        assertEquals(DataBuffer.TYPE_BYTE, ImageUtilities.getBandType(sm));
-
-        sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, 1, 1, new 
int[] {0x7F0000, 0x00FF80, 0x00007F});
-        assertEquals(DataBuffer.TYPE_USHORT, ImageUtilities.getBandType(sm));
-    }
-
     /**
      * Tests {@link ImageUtilities#getDataTypeName(SampleModel)}.
      */
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
index a041e4109c..75d66ebe5d 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
@@ -546,7 +546,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
             final int limit    = bank.limit();
             final int capacity = bank.capacity();   // Equals `this.capacity` 
except for packed sample model.
             if (limit != capacity) {
-                final Vector v = Vector.create(bank.limit(capacity), 
ImageUtilities.isUnsignedType(model));
+                final Vector v = Vector.create(bank.limit(capacity), 
DataType.isUnsigned(model));
                 final Number f = fillValues[band];
                 /*
                  * If all values are the same, we can delegate (indirectly) to 
an `Arrays.fill(…)` method.
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index 8eae81ae1a..2249101ef6 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -1682,7 +1682,7 @@ final class ImageFileDirectory extends DataCube {
             case UNSIGNED: {
                 if (bitsPerSample <= Byte   .SIZE) return DataType.BYTE;
                 if (bitsPerSample <= Short  .SIZE) return DataType.USHORT;
-                if (bitsPerSample <= Integer.SIZE) return DataType.INT;
+                if (bitsPerSample <= Integer.SIZE) return DataType.UINT;
                 break;
             }
             case FLOAT: {
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
index 495927a0e1..d657f0ab60 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
@@ -43,7 +43,7 @@ import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.image.ImageProcessor;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.IncompleteGridGeometryException;
-import org.apache.sis.image.privy.ImageUtilities;
+import org.apache.sis.image.DataType;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreReferencingException;
 import org.apache.sis.storage.IncompatibleResourceException;
@@ -363,7 +363,7 @@ final class Writer extends IOBase implements Flushable {
     {
         final SampleModel sm = image.exportable.getSampleModel();
         Compression compression = 
store.getCompression().orElse(Compression.DEFLATE);
-        if (!ImageUtilities.isIntegerType(sm)) {
+        if (!DataType.isInteger(sm)) {
             compression = compression.withPredictor(PREDICTOR_NONE);
         }
         /*
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/CopyFromBytes.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/CopyFromBytes.java
index 9e0565af5f..2d47dc5615 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/CopyFromBytes.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/CopyFromBytes.java
@@ -110,9 +110,10 @@ abstract class CopyFromBytes extends Inflater {
             throws UnsupportedEncodingException
     {
         switch (dataType) {
+            case BYTE:   return new Bytes  (input, chunksPerRow, 
samplesPerChunk, skipAfterChunks, pixelsPerElement);
             case USHORT: // Fall through
             case SHORT:  return new Shorts (input, chunksPerRow, 
samplesPerChunk, skipAfterChunks, pixelsPerElement);
-            case BYTE:   return new Bytes  (input, chunksPerRow, 
samplesPerChunk, skipAfterChunks, pixelsPerElement);
+            case UINT:   // Fall through
             case INT:    return new Ints   (input, chunksPerRow, 
samplesPerChunk, skipAfterChunks, pixelsPerElement);
             case FLOAT:  return new Floats (input, chunksPerRow, 
samplesPerChunk, skipAfterChunks, pixelsPerElement);
             case DOUBLE: return new Doubles(input, chunksPerRow, 
samplesPerChunk, skipAfterChunks, pixelsPerElement);
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/HorizontalPredictor.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/HorizontalPredictor.java
index 40e6b87276..82e5a12c89 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/HorizontalPredictor.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/HorizontalPredictor.java
@@ -105,9 +105,10 @@ abstract class HorizontalPredictor extends 
PredictorChannel {
             final int pixelStride, final int width)
     {
         switch (dataType) {
-            case USHORT:
-            case SHORT:  return new Shorts  (input, pixelStride, width);
             case BYTE:   return new Bytes   (input, pixelStride, width);
+            case USHORT: // Fall through
+            case SHORT:  return new Shorts  (input, pixelStride, width);
+            case UINT:   // Fall through
             case INT:    return new Integers(input, pixelStride, width);
             case FLOAT:  return new Floats  (input, pixelStride, width);
             case DOUBLE: return new Doubles (input, pixelStride, width);
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/HorizontalPredictor.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/HorizontalPredictor.java
index b6c174ec3b..2ec643fac2 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/HorizontalPredictor.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/HorizontalPredictor.java
@@ -72,9 +72,10 @@ abstract class HorizontalPredictor extends PredictorChannel {
             final int pixelStride, final int scanlineStride)
     {
         switch (dataType) {
-            case USHORT:
-            case SHORT:  return new Shorts  (output, pixelStride, 
scanlineStride);
             case BYTE:   return new Bytes   (output, pixelStride, 
scanlineStride);
+            case USHORT: // Fall through
+            case SHORT:  return new Shorts  (output, pixelStride, 
scanlineStride);
+            case UINT:   // Fall through
             case INT:    return new Integers(output, pixelStride, 
scanlineStride);
             case FLOAT:  return new Floats  (output, pixelStride, 
scanlineStride);
             case DOUBLE: return new Doubles (output, pixelStride, 
scanlineStride);
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/ReformattedImage.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/ReformattedImage.java
index 0a9e64a32c..cc55c779ef 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/ReformattedImage.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/ReformattedImage.java
@@ -27,6 +27,7 @@ import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.*;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.math.Statistics;
 import org.apache.sis.pending.jdk.JDK18;
+import org.apache.sis.image.DataType;
 import org.apache.sis.image.ImageLayout;
 import org.apache.sis.image.PlanarImage;
 import org.apache.sis.image.ImageProcessor;
@@ -208,8 +209,8 @@ found:  if (property instanceof Statistics[]) {
      */
     public int getSampleFormat() {
         final SampleModel sm = exportable.getSampleModel();
-        if (ImageUtilities.isUnsignedType(sm)) return 
SAMPLE_FORMAT_UNSIGNED_INTEGER;
-        if (ImageUtilities.isIntegerType(sm))  return 
SAMPLE_FORMAT_SIGNED_INTEGER;
+        if (DataType.isUnsigned(sm)) return SAMPLE_FORMAT_UNSIGNED_INTEGER;
+        if (DataType.isInteger(sm))  return SAMPLE_FORMAT_SIGNED_INTEGER;
         return SAMPLE_FORMAT_FLOATING_POINT;
     }
 
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
index 0292e39dbd..e38be04eb6 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
@@ -284,6 +284,7 @@ public final class TileMatrix {
                         case BYTE:   rect.write(compOutput, ((DataBufferByte)  
 buffer).getData(b), offset, direct); break;
                         case USHORT: rect.write(compOutput, 
((DataBufferUShort) buffer).getData(b), offset); break;
                         case SHORT:  rect.write(compOutput, ((DataBufferShort) 
 buffer).getData(b), offset); break;
+                        case UINT:   // Fall through
                         case INT:    rect.write(compOutput, ((DataBufferInt)   
 buffer).getData(b), offset); break;
                         case FLOAT:  rect.write(compOutput, ((DataBufferFloat) 
 buffer).getData(b), offset); break;
                         case DOUBLE: rect.write(compOutput, 
((DataBufferDouble) buffer).getData(b), offset); break;
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/DataType.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/DataType.java
index f527627edc..d41f1d36a5 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/DataType.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/DataType.java
@@ -95,7 +95,7 @@ public enum DataType {
      * 32 bits unsigned integer (netCDF type 9).
      * Not available in netCDF classic format.
      */
-    UINT(Numbers.INTEGER, Long.class, true, true, (byte) 4, 
org.apache.sis.image.DataType.INT),
+    UINT(Numbers.INTEGER, Long.class, true, true, (byte) 4, 
org.apache.sis.image.DataType.UINT),
 
     /**
      * 64 bits signed integer (netCDF type 10).
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
index f62048a07d..c71bc419bd 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
@@ -377,7 +377,7 @@ public abstract class TiledGridResource extends 
AbstractGridCoverageResource {
      */
     protected Number[] getFillValues(final int[] bands) throws 
DataStoreException {
         final SampleModel model = getSampleModel(bands);
-        final var dataType = DataType.forDataBufferType(model.getDataType());
+        final var dataType = DataType.forBands(model);
         IndexColorModel icm = null;
 check:  if (dataType.isInteger()) {
             final ColorModel colors = getColorModel(bands);
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java
index ba0a60df87..f3f1234c5c 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java
@@ -42,9 +42,9 @@ import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.base.PRJDataStore;
 import org.apache.sis.storage.base.MetadataBuilder;
+import org.apache.sis.image.DataType;
 import org.apache.sis.image.privy.ColorModelFactory;
 import org.apache.sis.image.privy.ColorModelBuilder;
-import org.apache.sis.image.privy.ImageUtilities;
 import org.apache.sis.image.privy.ObservableImage;
 import org.apache.sis.coverage.privy.RangeArgument;
 import org.apache.sis.util.CharSequences;
@@ -339,9 +339,8 @@ abstract class RasterStore extends PRJDataStore implements 
GridCoverageResource
          * Build the sample dimensions and the color model.
          * Some minimum/maximum values will be used as fallback if no 
statistics were found.
          */
-        final int     dataType   = sm.getDataType();
-        final boolean isInteger  = ImageUtilities.isIntegerType(dataType);
-        final boolean isUnsigned = isInteger && 
ImageUtilities.isUnsignedType(sm);
+        final boolean isInteger  = DataType.isInteger(sm);
+        final boolean isUnsigned = isInteger && DataType.isUnsigned(sm);
         final boolean isRGB      = isInteger && (bands.length == 3 || 
bands.length == 4);
         final var     builder    = new SampleDimension.Builder();
         for (int band=0; band < bands.length; band++) {
@@ -396,6 +395,7 @@ abstract class RasterStore extends PRJDataStore implements 
GridCoverageResource
              * The color file is optional and will be used if present.
              */
             if (band == VISIBLE_BAND) {
+                final int dataType = sm.getDataType();
                 try {
                     if (isRGB) {
                         colorModel = new ColorModelBuilder().createRGB(sm);
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridView.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridView.java
index 496cf9bec2..a2670c23d8 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridView.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/GridView.java
@@ -45,7 +45,7 @@ import org.apache.sis.gui.internal.BackgroundThreads;
 import org.apache.sis.gui.internal.LogHandler;
 import org.apache.sis.gui.internal.Styles;
 import org.apache.sis.gui.internal.ExceptionReporter;
-import org.apache.sis.image.privy.ImageUtilities;
+import org.apache.sis.image.DataType;
 
 
 /**
@@ -492,7 +492,7 @@ public class GridView extends Control {
                 if (getBand() >= numBands) {
                     ((BandProperty) bandProperty).setNoCheck(numBands - 1);
                 }
-                cellFormat.dataTypeIsInteger = 
ImageUtilities.isIntegerType(sm);
+                cellFormat.dataTypeIsInteger = DataType.isInteger(sm);
             }
             cellFormat.configure(image, getBand());
         }

Reply via email to