com.davemorrissey.labs.subscaleview.decoder

## Class SkiaPooledImageRegionDecoder

- java.lang.Object
- - com.davemorrissey.labs.subscaleview.decoder.SkiaPooledImageRegionDecoder

All Implemented Interfaces:
  ImageRegionDecoder

    public class SkiaPooledImageRegionDecoder
    extends Object
    implements ImageRegionDecoder

An implementation of `ImageRegionDecoder` using a pool of `BitmapRegionDecoder`s, to provide true parallel loading of tiles. This is only effective if parallel loading has been enabled in the view by calling `SubsamplingScaleImageView.setExecutor(Executor)` with a multi-threaded `Executor` instance.

One decoder is initialised when the class is initialised. This is enough to decode base layer tiles. Additional decoders are initialised when a subregion of the image is first requested, which indicates interaction with the view. Creation of additional encoders stops when `allowAdditionalDecoder(int, long)` returns false. The default implementation takes into account the file size, number of CPU cores, low memory status and a hard limit of 4. Extend this class to customise this.

WARNING: This class is highly experimental and not proven to be stable on a wide range of devices. You are advised to test it thoroughly on all available devices, and code your app to use `SkiaImageRegionDecoder` on old or low powered devices you could not test.

### Constructor Summary

Constructors
Constructor and Description
`SkiaPooledImageRegionDecoder()`
`SkiaPooledImageRegionDecoder(Bitmap.Config bitmapConfig)`

### Method Summary

All Methods Static Methods Instance Methods Concrete Methods
`protected boolean` `allowAdditionalDecoder(int numberOfDecoders, long fileLength)` Called before creating a new decoder.
`Bitmap` `decodeRegion(Rect sRect, int sampleSize)` Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still exists and acquire a decoder to load the requested region.
`Point` `init(Context context, Uri uri)` Initialises the decoder pool.
`boolean` `isReady()` Holding a read lock to avoid returning true while the pool is being recycled, this returns true if the pool has at least one decoder available.
`void` `recycle()` Wait until all read locks held by `decodeRegion(Rect, int)` are released, then recycle and destroy the pool.
`static void` `setDebug(boolean debug)` Controls logging of debug messages.

### Methods inherited from class java.lang.Object

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

### Constructor Detail

#### SkiaPooledImageRegionDecoder

    public SkiaPooledImageRegionDecoder()

#### SkiaPooledImageRegionDecoder

    public SkiaPooledImageRegionDecoder(@Nullable
                                        Bitmap.Config bitmapConfig)

### Method Detail

#### setDebug

    public static void setDebug(boolean debug)

Controls logging of debug messages. All instances are affected.

Parameters:
  `debug` - true to enable debug logging, false to disable.

#### init

    @NonNull
    public Point init(Context context,
                               @NonNull
                               Uri uri)
                        throws Exception

Initialises the decoder pool. This method creates one decoder on the current thread and uses it to decode the bounds, then spawns an independent thread to populate the pool with an additional three decoders. The thread will abort if `recycle()` is called.

Specified by:
  `init` in interface `ImageRegionDecoder`
Parameters:
  `context` - Application context. A reference may be held, but must be cleared on recycle.
  `uri` - URI of the image.
Returns:
  Dimensions of the image.
Throws:
  `Exception` - if initialisation fails.

#### decodeRegion

    @NonNull
    public Bitmap decodeRegion(@NonNull
                                        Rect sRect,
                                        int sampleSize)

Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still exists and acquire a decoder to load the requested region. There is no check whether the pool currently has decoders, because it's guaranteed to have one decoder after `init(Context, Uri)` is called and be null once `recycle()` is called. In practice the view can't call this method until after `init(Context, Uri)`, so there will be no blocking on an empty pool.

Specified by:
  `decodeRegion` in interface `ImageRegionDecoder`
Parameters:
  `sRect` - Source image rectangle to decode.
  `sampleSize` - Sample size.
Returns:
  The decoded region. It is safe to return null if decoding fails.

#### isReady

    public boolean isReady()

Holding a read lock to avoid returning true while the pool is being recycled, this returns true if the pool has at least one decoder available.

Specified by:
  `isReady` in interface `ImageRegionDecoder`
Returns:
  true if the decoder is ready to be used.

#### recycle

    public void recycle()

Wait until all read locks held by `decodeRegion(Rect, int)` are released, then recycle and destroy the pool. Elsewhere, when a read lock is acquired, we must check the pool is not null.

Specified by:
  `recycle` in interface `ImageRegionDecoder`

#### allowAdditionalDecoder

    protected boolean allowAdditionalDecoder(int numberOfDecoders,
                                             long fileLength)

Called before creating a new decoder. Based on number of CPU cores, available memory, and the size of the image file, determines whether another decoder can be created. Subclasses can override and customise this.

Parameters:
  `numberOfDecoders` - the number of decoders that have been created so far
  `fileLength` - the size of the image file in bytes. Creating another decoder will use approximately this much native memory.
Returns:
  true if another decoder can be created.