# Class ImageRenderer

Object
ImageRenderer

public class ImageRenderer extends Object
A builder for the rendered image to be returned by `GridCoverage​.render(GridExtent)`. This builder does not copy any sample values. Instead, it wraps existing data arrays into `Raster` objects by computing required information such as pixel stride, scanline stride and band offsets. Different `setData(…)` methods are provided for allowing to specify the data arrays from different objects such as Java2D `DataBuffer` or NIO `Buffer`.

All `setData(…)` methods assume that the first valid element in each array is the value located at `GridCoverage​.getGridGeometry().getExtent().getLow()`. This `ImageRenderer` class computes automatically the offsets from that position to the position of the first value included in the `sliceExtent` given to the constructor.

## Usage example

    class MyResource extends GridCoverage {
        @Override
        public RenderedImage render(GridExtent sliceExtent) {
            ImageRenderer renderer = new ImageRenderer(this, sliceExtent);
            try {
                renderer.setData(data);
                return renderer.createImage();
            } catch (IllegalArgumentException | ArithmeticException | RasterFormatException e) {
                throw new CannotEvaluateException("Cannot create an image.", e);
            }
        }
    }

## Limitations

Current implementation constructs only images made of a single tile. Support for tiled images will be added in a future version.

Since:
  1.0
See Also:
  - `GridCoverage​.render(GridExtent)`

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `ImageRenderer(GridCoverage coverage, GridExtent sliceExtent)`
  Creates a new image renderer for the given slice extent.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `void`
  `addProperty(String key, Object value)`
  Adds a value associated to a property.
  `RenderedImage`
  `createImage()`
  Creates an image with the data specified by the last call to a `setData(…)` method.
  `Raster`
  `createRaster()`
  Creates a raster with the data specified by the last call to a `setData(…)` method.
  `final Rectangle`
  `getBounds()`
  Returns the location of the image upper-left corner together with the image size.
  `GridGeometry`
  `getImageGeometry(int dimCRS)`
  Computes the conversion from pixel coordinates to CRS, together with the geospatial envelope of the image.
  `final int`
  `getNumBands()`
  Returns the number of bands that the image will have.
  `Object`
  `getProperty(String key)`
  Returns the value associated to the given property.
  `final int[]`
  `getXYDimensions()`
  The dimensions to select in the grid coverage for producing an image.
  `void`
  `setCategoryColors(Function<Category,Color[]> colors)`
  Specifies the colors to apply for each category in a sample dimension.
  `void`
  `setData(DataBuffer data)`
  Sets the data as a Java2D buffer.
  `void`
  `setData(DataType dataType, Buffer... data)`
  Sets the data as NIO buffers.
  `void`
  `setData(Vector... data)`
  Sets the data as vectors.
  `void`
  `setInterleavedPixelOffsets(int pixelStride, int[] bandOffsets)`
  Specifies the offsets to add to sample index in each band in order to reach the sample value in the `DataBuffer` bank.
  `void`
  `setVisibleBand(int band)`
  Specifies the band to use for defining pixel colors when the image is displayed on screen.

  ### Methods inherited from class Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### ImageRenderer

    public ImageRenderer(GridCoverage coverage, GridExtent sliceExtent)
    Creates a new image renderer for the given slice extent.
    Parameters:
      `coverage` - the source coverage for which to build an image.
      `sliceExtent` - the domain from which to create an image, or `null` for the `coverage` extent.
    Throws:
      `SubspaceNotSpecifiedException` - if this method cannot infer a two-dimensional slice from `sliceExtent`.
      `DisjointExtentException` - if the given extent does not intersect the given coverage.
      `ArithmeticException` - if a stride calculation overflows the 32 bits integer capacity.

- ## Method Details

  - ### getNumBands

    public final int getNumBands()
    Returns the number of bands that the image will have. By default, this is the number of sample dimensions in the grid coverage.
    Returns:
      the number of bands in the rendered image.

  - ### getBounds

    public final Rectangle getBounds()
    Returns the location of the image upper-left corner together with the image size. The image coordinate system is relative to the `sliceExtent` specified at construction time: the (0,0) pixel coordinates correspond to the `sliceExtent` low coordinates. Consequently, the rectangle x and y coordinates are (0,0) if the image is located exactly in the area requested by `sliceExtent`, or is shifted as below otherwise:

    > (x, y) = (grid coordinates of actually provided region) − (grid coordinates of requested region)

    Returns:
      the rendered image location and size (never null).

  - ### getXYDimensions

    public final int[] getXYDimensions()
    The dimensions to select in the grid coverage for producing an image. This is the array obtained by ``GridExtent​.getSubspaceDimensions(2)``. The array content is almost always {0,1}, i.e. the 2 first dimensions in a coordinate tuple.
    Returns:
      indices of x and y coordinate values in a grid coordinate tuple.
    Since:
      1.3
    See Also:
      - `PlanarImage​.XY_DIMENSIONS_KEY`

  - ### getImageGeometry

    public GridGeometry getImageGeometry(int dimCRS)
    Computes the conversion from pixel coordinates to CRS, together with the geospatial envelope of the image. The `GridGeometry` returned by this method is derived from the coverage grid geometry with the following changes:
    - The number of grid dimensions is always 2.
    - The number of CRS dimensions is specified by `dimCRS` (usually 2).
    - The envelope may be a sub-region of the coverage envelope.
    - The grid extent is the image bounds.
    - The grid to CRS transform is derived from the coverage transform with a translation for mapping the `sliceExtent` low coordinates to (0,0) pixel coordinates.
    Parameters:
      `dimCRS` - desired number of dimensions in the CRS. This is usually 2.
    Returns:
      conversion from pixel coordinates to CRS of the given number of dimensions, together with image bounds and geospatial envelope if possible.
    Since:
      1.1
    See Also:
      - `PlanarImage​.GRID_GEOMETRY_KEY`

  - ### getProperty

    public Object getProperty(String key)
    Returns the value associated to the given property. The properties recognized by current implementation are:
    - "org.apache.sis.XYDimensions".
    - "org.apache.sis.GridGeometry".
    - "org.apache.sis.SampleDimensions".
    - Any property added by calls to `addProperty(String, Object)`.
    Parameters:
      `key` - the property for which to get a value.
    Returns:
      value associated to the given property, or `null` if none.
    Since:
      1.1

  - ### addProperty

    public void addProperty(String key, Object value)
    Adds a value associated to a property. This method can be invoked only once for each `key`. Those properties will be given to the image created by the `createImage()` method.
    Parameters:
      `key` - key of the property to set.
      `value` - value to associate to the given key.
    Throws:
      `IllegalArgumentException` - if a value is already associated to the given key.
    Since:
      1.1

  - ### setData

    public void setData(DataBuffer data)
    Sets the data as a Java2D buffer. The number of banks in the given buffer must be equal to the expected number of bands. In each bank, the value located at the bank offset is the value located at `GridCoverage​.getGridGeometry().getExtent().getLow()`, as specified in class javadoc.
    Parameters:
      `data` - the Java2D buffer containing data for all bands.
    Throws:
      `NullPointerException` - if `data` is null.
      `MismatchedCoverageRangeException` - if the given data buffer does not have the expected number of banks.

  - ### setData

    public void setData(DataType dataType, Buffer... data)
    Sets the data as NIO buffers. The number of buffers must be equal to the expected number of bands. All buffers must be backed by arrays of the type specified by the `dataType` argument and have the same number of remaining elements. This method wraps the underlying arrays of a primitive type into a Java2D buffer; data are not copied. For each buffer, the grid coverage data (not only the slice data) starts at buffer position and ends at that position + remaining.

    The data type must be specified in order to distinguish between the signed and unsigned types. `DataType​.BYTE` and `DataType​.USHORT` are unsigned, all other supported types are signed.

    Implementation note: the Java2D buffer is set by a call to `setData(DataBuffer)`, which can be overridden by subclasses if desired.

    Parameters:
      `dataType` - type of data.
      `data` - the buffers wrapping arrays of primitive type.
    Throws:
      `NullPointerException` - if `data` is null or one of `data` element is null.
      `MismatchedCoverageRangeException` - if the number of specified buffers is not equal to the number of bands.
      `UnsupportedOperationException` - if a buffer is not backed by an accessible array or is read-only.
      `ArrayStoreException` - if a buffer type is incompatible with `dataType`.
      `RasterFormatException` - if buffers do not have the same number of remaining values.
      `ArithmeticException` - if a buffer position overflows the 32 bits integer capacity.
    Since:
      1.1

  - ### setData

    public void setData(Vector... data)
    Sets the data as vectors. The number of vectors must be equal to the expected number of bands. All vectors must be backed by arrays (indirectly, through buffers backed by arrays) and have the same size. This method wraps the underlying arrays of a primitive type into a Java2D buffer; data are not copied.

    Implementation note: the NIO buffers are set by a call to `setData(DataType, Buffer...)`, which can be overridden by subclasses if desired.

    Parameters:
      `data` - the vectors wrapping arrays of primitive type.
    Throws:
      `NullPointerException` - if `data` is null or one of `data` element is null.
      `MismatchedCoverageRangeException` - if the number of specified vectors is not equal to the number of bands.
      `UnsupportedOperationException` - if a vector is not backed by an accessible array or is read-only.
      `RasterFormatException` - if vectors do not have the same size.
      `ArithmeticException` - if a buffer position overflows the 32 bits integer capacity.

  - ### setInterleavedPixelOffsets

    public void setInterleavedPixelOffsets(int pixelStride, int[] bandOffsets)
    Specifies the offsets to add to sample index in each band in order to reach the sample value in the `DataBuffer` bank. This method should be invoked when the data given to `setData(…)` contains only one `Vector`, `Buffer` or `DataBuffer` bank, and the bands in that unique bank are interleaved.

    #### Example

    For an image having three bands named Red (R), Green (G) and Blue (B), if the sample values are stored in a single bank in a R₀,G₀,B₀, R₁,G₁,B₁, R₂,G₂,B₂, R₃,G₃,B₃, etc. fashion, then this method should be invoked as below:

        setInterleavedPixelOffsets(3, new int[] {0, 1, 2});

    Parameters:
      `pixelStride` - the number of data elements between each pixel in the data vector or buffer.
      `bandOffsets` - offsets to add to sample index in each band. This is typically {0, 1, 2, …}. The length of this array shall be equal to `getNumBands()`.

  - ### setVisibleBand

    public void setVisibleBand(int band)
    Specifies the band to use for defining pixel colors when the image is displayed on screen. All other bands, if any, will exist in the raster but be ignored at display time. The default value is 0, the first (and often only) band.

    #### Implementation note

    An `IndexColorModel` will be used for displaying the image.
    Parameters:
      `band` - the band to use for display purpose.
    Throws:
      `IllegalArgumentException` - if the given band is not between 0 (inclusive) and `getNumBands()` (exclusive).
    Since:
      1.2
    See Also:
      - `Colorizer​.Target​.getVisibleBand()`

  - ### setCategoryColors

    public void setCategoryColors(Function<Category,Color[]> colors)
    Specifies the colors to apply for each category in a sample dimension. The given function can return `null` for unrecognized categories. If this method is never invoked, or if a category is unrecognized, then the default is a grayscale for quantitative categories and transparent for qualitative categories (typically "no data" values).

    #### Example

    The following code specifies a color palette from blue to red with white in the middle. This is useful for data with a clear 0 (white) in the middle of the range, with a minimal value equals to the negative of the maximal value.

        setCategoryColors((category) -> category.isQuantitative() ? new Color[] {
                Color.BLUE, Color.CYAN, Color.WHITE, Color.YELLOW, Color.RED} : null);

    Parameters:
      `colors` - the colors to use for each category. The `colors` argument cannot be null, but `colors​.apply(Category)` can return null.
    Since:
      1.2

  - ### createRaster

    public Raster createRaster()
    Creates a raster with the data specified by the last call to a `setData(…)` method. The raster upper-left corner is located at the position given by `getBounds()`. The returned raster is often an instance of `WritableRaster`, but read-only rasters are also allowed.
    Returns:
      the raster, usually (but not necessarily) an instance of `WritableRaster`.
    Throws:
      `IllegalStateException` - if no `setData(…)` method has been invoked before this method call.
      `RasterFormatException` - if a call to a `Raster` factory method failed.
      `ArithmeticException` - if a property of the raster to construct exceeds the capacity of 32 bits integers.
    Since:
      1.1

  - ### createImage

    public RenderedImage createImage()
    Creates an image with the data specified by the last call to a `setData(…)` method. The image upper-left corner is located at the position given by `getBounds()`. The two-dimensional image geometry is stored as a property associated to the "org.apache.sis.GridGeometry" key. The dimensions of the source grid that are represented in the image are associated to the "org.apache.sis.XYDimensions" key. The sample dimensions are stored as a property associated to the "org.apache.sis.SampleDimensions" key.

    The default implementation returns an instance of `WritableRenderedImage` if the `createRaster()` return value is an instance of `WritableRaster`, or a read-only `RenderedImage` otherwise.

    Returns:
      the image.
    Throws:
      `IllegalStateException` - if no `setData(…)` method has been invoked before this method call.
      `RasterFormatException` - if a call to a `Raster` factory method failed.
      `ArithmeticException` - if a property of the image to construct exceeds the capacity of 32 bits integers.
    Since:
      1.1