Class ComputedImage

Object
PlanarImage
ComputedImage
All Implemented Interfaces:
Rendered­Image, Disposable
Direct Known Subclasses:
Resampled­Image

public abstract class ComputedImage extends PlanarImage implements Disposable
An image with tiles computed on-the-fly and cached for future reuse. Computations are performed on a tile-by-tile basis (potentially in different threads) and the results are stored in a cache shared by all images in the runtime environment. Tiles may be discarded at any time or may become dirty if a source has been modified, in which case those tiles will be recomputed when needed again.

Computed­Image may have an arbitrary number of source images, including zero. A Tile­Observer is automatically registered to all sources that are instances of Writable­Rendered­Image. If one of those sources sends a change event, then all Computed­Image tiles that may be impacted by that change are marked as dirty and will be computed again when needed.

When this Computed­Image is garbage collected, all cached tiles are discarded and the above-cited Tile­Observer is automatically removed from all sources. This cleanup can be requested without waiting for garbage collection by invoking the dispose() method, but that call should be done only if the caller is certain that this Computed­Image will not be used anymore.

Pixel coordinate system

Default implementation assumes that the pixel in upper-left left corner is located at coordinates (0,0). This assumption is consistent with Grid­Coverage​.render(Grid­Extent) contract, which produces an image located at (0,0) when the image region matches the Grid­Extent. However subclasses can use a non-zero origin by overriding the methods documented in the Sub-classing section below.

If this Computed­Image does not have any Writable­Rendered­Image source, then there is no other assumption on the pixel coordinate system. But if there is writable sources, then the default implementation assumes that source images occupy the same region as this Computed­Image: all pixels at coordinates (x, y) in this Computed­Image depend on pixels at the same (x, y) coordinates in the source images, possibly shifted or expanded to neighborhood pixels as described in SOURCE_PADDING_KEY. If this assumption does not hold, then subclasses should override the source­Tile­Changed(Rendered­Image, int, int) method.

Sub-classing

Subclasses need to implement at least the following methods:

If pixel coordinates or tile indices do not start at zero, then subclasses shall also override the following methods:

Writable computed images

Computed­Image can itself be a Writable­Rendered­Image if subclasses decide so. A writable computed image is an image which can retro-propagate sample value changes to the source images. This class provides has­Tile­Writers(), get­Writable­Tile­Indices(), is­Tile­Writable(int, int) and mark­Tile­Writable(int, int, boolean) methods for Writable­Rendered­Image implementations convenience.

Apache SIS does not yet define a synchronization policy between get­Tile(…) method and Writable­Rendered­Image​​.get­Writable­Tile/release­Writable­Tile(…) methods. For example if a call to get­Tile(tile­X, tile­Y) is followed by a call to get­Writable­Tile(tile­X, tile­Y) in another thread, there is no guarantees about whether or not the sample values seen in the Raster would be isolated from the write operations done concurrently in the Writable­Raster. A future SIS version may define a policy (possibly based on Read­Write­Lock).

Note that despite above-cited issue, all methods in this Computed­Image class are thread-safe. What is not thread-safe is writing into a Writable­Raster from outside the compute­Tile(…) method, or reading a Raster after it became dirty if the change to dirty state happened after the call to get­Tile(…).

Since:
1.1

Defined in the sis-feature module

  • Field Details

    • SOURCE_PADDING_KEY

      public static final String SOURCE_PADDING_KEY
      The property for declaring the amount of additional source pixels needed on each side of a destination pixel. This property can be used for calculations that require only a fixed rectangular source region around a source pixel in order to compute each destination pixel. A given destination pixel (x, y) may be computed from the neighborhood of source pixels beginning at (x - Insets​.left, y - Insets​.top) and extending to (x + Insets​.right, y + Insets​.bottom) inclusive. Those left, top, right and bottom attributes can be positive, zero or negative, but their sums shall be positive with (left + right) ≥ 0 and (top + bottom) ≥ 0.

      The property value shall be an instance of Insets or Insets[]. The array form can be used when a different padding is required for each source image. In that case, the image source index is used as the index for accessing the Insets element in the array. Null or undefined elements mean that no padding is applied. If the array length is shorter than the number of source images, missing elements are considered as null.

      See Also:
    • sampleModel

      protected final SampleModel sampleModel
      The sample model shared by all tiles in this image. The sample model width determines this image tile width, and the sample model height determines this image tile height.
      Design note: Computed­Image requires the sample model to have exactly the desired tile size otherwise tiles created by create­Tile(int, int) will consume more memory than needed.
  • Constructor Details

    • ComputedImage

      protected ComputedImage(SampleModel sampleModel, RenderedImage... sources)
      Creates an initially empty image with the given sample model. The default tile size will be the width and height of the given sample model (this default setting minimizes the amount of memory consumed by create­Tile(int, int)). This constructor automatically registers a Tile­Observer for all sources that are Writable­Rendered­Image instances.
      Parameters:
      sample­Model - the sample model shared by all tiles in this image.
      sources - sources of this image (may be an empty array), or a null array if unknown.
  • Method Details

    • getSource

      protected final RenderedImage getSource(int index)
      Returns the source at the given index.
      Parameters:
      index - index of the desired source.
      Returns:
      source at the given index.
      Throws:
      Index­Out­Of­Bounds­Exception - if the given index is out of bounds.
    • getSources

      public Vector<RenderedImage> getSources()
      Returns the immediate sources of image data for this image (may be null). This method returns all sources specified at construction time.
      Specified by:
      get­Sources in interface Rendered­Image
      Overrides:
      get­Sources in class Planar­Image
      Returns:
      the immediate sources, or an empty vector is none, or null if unknown.
    • getSampleModel

      public SampleModel getSampleModel()
      Returns the sample model associated with this image. All rasters returned from this image will have this sample model. In Computed­Image implementation, the sample model determines the tile size (this is not necessarily true for all Rendered­Image implementations).
      Specified by:
      get­Sample­Model in interface Rendered­Image
      Returns:
      the sample model of this image.
    • getTileWidth

      public int getTileWidth()
      Returns the width of tiles in this image. The default implementation returns Sample­Model​.get­Width().
      Note: a raster can have a smaller width than its sample model, for example when a raster is a view over a subregion of another raster. But this is not recommended in the particular case of this Computed­Image class, because it would cause create­Tile(int, int) to consume more memory than necessary.
      Specified by:
      get­Tile­Width in interface Rendered­Image
      Returns:
      the width of this image in pixels.
    • getTileHeight

      public int getTileHeight()
      Returns the height of tiles in this image. The default implementation returns Sample­Model​.get­Height().
      Note: a raster can have a smaller height than its sample model, for example when a raster is a view over a subregion of another raster. But this is not recommended in the particular case of this Computed­Image class, because it would cause create­Tile(int, int) to consume more memory than necessary.
      Specified by:
      get­Tile­Height in interface Rendered­Image
      Returns:
      the height of this image in pixels.
    • getTile

      public final Raster getTile(int tileX, int tileY)
      Returns a tile of this image, computing it when needed. This method performs the first of the following actions that apply:
      1. If the requested tile is present in the cache and is not dirty, then that tile is returned immediately.
      2. Otherwise if the requested tile is being computed in another thread, then this method blocks until the other thread completed its work and returns its result. If the other thread failed to compute the tile, an Imaging­Op­Exception is thrown.
      3. Otherwise this method computes the tile and caches the result before to return it. If an error occurred, an Imaging­Op­Exception is thrown.

      Race conditions with write operations

      If this image implements the Writable­Rendered­Image interface, then a user may acquire the same tile for a write operation after this method returned. In such case there is no consistency guarantees on sample values: the tile returned by this method may show data in an unspecified stage during the write operation. A synchronization policy may be defined in a future Apache SIS version.
      Specified by:
      get­Tile in interface Rendered­Image
      Parameters:
      tile­X - the column index of the tile to get.
      tile­Y - the row index of the tile to get.
      Returns:
      the tile at the given index (never null).
      Throws:
      Illegal­Argument­Exception - if a given tile index is out of bounds.
      Imaging­Op­Exception - if an error occurred while computing the image.
    • computeTile

      protected abstract Raster computeTile(int tileX, int tileY, WritableRaster previous) throws Exception
      Invoked when a tile need to be computed or updated. This method is invoked by get­Tile(int, int) when the requested tile is not in the cache, or when a writable source notified us that its data changed. The returned tile will be automatically cached.

      A typical implementation is as below:

      @Override
      protected Raster computeTile(int tileX, int tileY, WritableRaster tile) {
          if (tile == null) {
              tile = createTile(tileX, tileY);
          }
          // Do calculation here and write results in tile.
          return tile;
      }

      Error handling

      If this method throws an exception or returns null, then get­Tile(…) will set an error flag on the tile and throw an Imaging­Op­Exception with the exception thrown by compute­Tile(…) as its cause. Future invocations of get­Tile(tile­X, tile­Y) with the same tile indices will cause an Imaging­Op­Exception to be thrown immediately without invocation of compute(tile­X, tile­Y). If the error has been fixed, then users can invoke clear­Error­Flags(Rectangle) for allowing the tile to be computed again.
      Parameters:
      tile­X - the column index of the tile to compute.
      tile­Y - the row index of the tile to compute.
      previous - if the tile already exists but needs to be updated, the tile to update. Otherwise null.
      Returns:
      computed tile for the given indices. May be the previous tile after update but can not be null.
      Throws:
      Exception - if an error occurred while computing the tile.
    • createTile

      protected WritableRaster createTile(int tileX, int tileY)
      Creates an initially empty tile at the given tile grid position. This is a helper method for compute­Tile(int, int, Writable­Raster) implementations.
      Parameters:
      tile­X - the column index of the tile to create.
      tile­Y - the row index of the tile to create.
      Returns:
      initially empty tile for the given indices (can not be null).
    • hasTileWriters

      public boolean hasTileWriters()
      Returns whether any tile is under computation or is checked out for writing. There is two reasons why this method may return true:
      Returns:
      whether any tiles are under computation or checked out for writing.
      See Also:
    • isTileWritable

      public boolean isTileWritable(int tileX, int tileY)
      Returns whether the specified tile is currently under computation or checked out for writing. There is two reasons why this method may return true:
      • compute­Tile(tile­X, tile­Y, …) is running in another thread.
      • There is at least one call to mark­Tile­Writable(tile­X, tile­Y, true) call without matching call to mark­Tile­Writable(tile­X, tile­Y, false). This second case may happen if this Computed­Image is also a Writable­Rendered­Image.
      Parameters:
      tile­X - the X index of the tile to check.
      tile­Y - the Y index of the tile to check.
      Returns:
      whether the specified tile is under computation or checked out for writing.
      See Also:
    • getWritableTileIndices

      public Point[] getWritableTileIndices()
      Returns the indices of all tiles under computation or checked out for writing, or null if none. This method lists all tiles for which the condition documented in is­Tile­Writable(int, int) is true.
      Returns:
      an array containing the indices of tiles that are under computation or checked out for writing, or null if none.
      See Also:
    • markTileWritable

      protected boolean markTileWritable(int tileX, int tileY, boolean writing)
      Sets or clears whether a tile is checked out for writing. This method is provided for subclasses that implement the Writable­Rendered­Image interface. This method can be used as below:
      class MyImage extends ComputedImage implements WritableRenderedImage {
          // Constructor omitted for brevity.
      
          @Override
          public WritableRaster getWritableTile(int tileX, int tileY) {
              WritableRaster raster = ...;             // Get the writable tile here.
              markTileWritable(tileX, tileY, true);
              return raster;
          }
      
          @Override
          public void releaseWritableTile(int tileX, int tileY) {
              markTileWritable(tileX, tileY, false);
              // Release the raster here.
          }
      }
      Parameters:
      tile­X - the x index of the tile to acquire or release.
      tile­Y - the y index of the tile to acquire or release.
      writing - true for acquiring the tile, or false for releasing it.
      Returns:
      true if the tile goes from having no writers to having one writer (may happen if writing is true), or goes from having one writer to no writers (may happen if writing is false).
      See Also:
    • markDirtyTiles

      protected boolean markDirtyTiles(Rectangle tiles)
      Marks all tiles in the given range of indices as in need of being recomputed. The tiles will not be recomputed immediately, but only on next invocation of get­Tile(tile­X, tile­Y) if the (tile­X, tile­Y) indices are contained if the specified rectangle.

      Subclasses can invoke this method when the tiles in the given range depend on source data that changed, typically (but not necessarily) source images. Note that there is no need to invoke this method if the source images are instances of Writable­Rendered­Image, because Computed­Image already has Tile­Observer for them.

      Parameters:
      tiles - indices of tiles to mark as dirty.
      Returns:
      true if at least one tile has been marked dirty.
    • clearErrorFlags

      protected boolean clearErrorFlags(Rectangle tiles)
      Clears the error status of all tiles in the given range of indices. Those tiles will be marked as dirty and recomputed next time the the get­Tile(int, int) method is invoked. The status of valid tiles is unchanged by this method call.
      Parameters:
      tiles - indices of tiles for which to clear the error status.
      Returns:
      true if at least one tile got its error flag cleared.
      See Also:
    • sourceTileChanged

      protected void sourceTileChanged(RenderedImage source, int tileX, int tileY)
      Invoked when a tile of a source image has been updated. This method should mark as dirty all tiles of this Computed­Image that depend on the updated tile.

      The default implementation assumes that source images use pixel coordinate systems aligned with this Computed­Image in such a way that all pixels at coordinates (x, y) in the source image are used for calculation of pixels at the same (x, y) coordinates in this Computed­Image, possibly expanded to neighborhood pixels if the "org.apache.sis.SourcePadding" property is defined. If this assumption does not hold, then subclasses should override this method and invoke mark­Dirty­Tiles(Rectangle) themselves.

      Parameters:
      source - the image that own the tile which has been updated.
      tile­X - the x index of the tile that has been updated.
      tile­Y - the y index of the tile that has been updated.
    • dispose

      public void dispose()
      Advises this image that its tiles will no longer be requested. This method removes all tiles from the cache and stops observation of Writable­Rendered­Image sources. This image should not be used anymore after this method call.

      Note: keep in mind that this image may be referenced as a source of other images. In case of doubt, it may be safer to rely on the garbage collector instead of invoking this method.

      Specified by:
      dispose in interface Disposable