|  | /* | 
|  | * Copyright (C) 2017 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | package android.graphics; | 
|  |  | 
|  | import android.annotation.NonNull; | 
|  | import android.annotation.Nullable; | 
|  | import android.content.ContentResolver; | 
|  | import android.content.res.AssetManager.AssetInputStream; | 
|  | import android.content.res.Resources; | 
|  | import android.graphics.drawable.AnimatedImageDrawable; | 
|  | import android.graphics.drawable.Drawable; | 
|  | import android.graphics.drawable.BitmapDrawable; | 
|  | import android.net.Uri; | 
|  | import android.util.DisplayMetrics; | 
|  | import android.util.Size; | 
|  | import android.util.TypedValue; | 
|  |  | 
|  | import java.nio.ByteBuffer; | 
|  | import java.io.File; | 
|  | import java.io.IOException; | 
|  | import java.io.InputStream; | 
|  | import java.lang.ArrayIndexOutOfBoundsException; | 
|  | import java.lang.AutoCloseable; | 
|  | import java.lang.NullPointerException; | 
|  | import java.lang.annotation.Retention; | 
|  | import static java.lang.annotation.RetentionPolicy.SOURCE; | 
|  |  | 
|  | /** | 
|  | *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s. | 
|  | */ | 
|  | public final class ImageDecoder implements AutoCloseable { | 
|  |  | 
|  | /** | 
|  | *  Source of the encoded image data. | 
|  | */ | 
|  | public static abstract class Source { | 
|  | private Source() {} | 
|  |  | 
|  | /* @hide */ | 
|  | @Nullable | 
|  | Resources getResources() { return null; } | 
|  |  | 
|  | /* @hide */ | 
|  | int getDensity() { return Bitmap.DENSITY_NONE; } | 
|  |  | 
|  | /* @hide */ | 
|  | int computeDstDensity() { | 
|  | Resources res = getResources(); | 
|  | if (res == null) { | 
|  | return Bitmap.getDefaultDensity(); | 
|  | } | 
|  |  | 
|  | return res.getDisplayMetrics().densityDpi; | 
|  | } | 
|  |  | 
|  | /* @hide */ | 
|  | @NonNull | 
|  | abstract ImageDecoder createImageDecoder() throws IOException; | 
|  | }; | 
|  |  | 
|  | private static class ByteArraySource extends Source { | 
|  | ByteArraySource(@NonNull byte[] data, int offset, int length) { | 
|  | mData = data; | 
|  | mOffset = offset; | 
|  | mLength = length; | 
|  | }; | 
|  | private final byte[] mData; | 
|  | private final int    mOffset; | 
|  | private final int    mLength; | 
|  |  | 
|  | @Override | 
|  | public ImageDecoder createImageDecoder() throws IOException { | 
|  | return new ImageDecoder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static class ByteBufferSource extends Source { | 
|  | ByteBufferSource(@NonNull ByteBuffer buffer) { | 
|  | mBuffer = buffer; | 
|  | } | 
|  | private final ByteBuffer mBuffer; | 
|  |  | 
|  | @Override | 
|  | public ImageDecoder createImageDecoder() throws IOException { | 
|  | return new ImageDecoder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static class ContentResolverSource extends Source { | 
|  | ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) { | 
|  | mResolver = resolver; | 
|  | mUri = uri; | 
|  | } | 
|  |  | 
|  | private final ContentResolver mResolver; | 
|  | private final Uri mUri; | 
|  |  | 
|  | @Override | 
|  | public ImageDecoder createImageDecoder() throws IOException { | 
|  | return new ImageDecoder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * For backwards compatibility, this does *not* close the InputStream. | 
|  | */ | 
|  | private static class InputStreamSource extends Source { | 
|  | InputStreamSource(Resources res, InputStream is, int inputDensity) { | 
|  | if (is == null) { | 
|  | throw new IllegalArgumentException("The InputStream cannot be null"); | 
|  | } | 
|  | mResources = res; | 
|  | mInputStream = is; | 
|  | mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; | 
|  | } | 
|  |  | 
|  | final Resources mResources; | 
|  | InputStream mInputStream; | 
|  | final int mInputDensity; | 
|  |  | 
|  | @Override | 
|  | public Resources getResources() { return mResources; } | 
|  |  | 
|  | @Override | 
|  | public int getDensity() { return mInputDensity; } | 
|  |  | 
|  | @Override | 
|  | public ImageDecoder createImageDecoder() throws IOException { | 
|  | return new ImageDecoder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Takes ownership of the AssetInputStream. | 
|  | * | 
|  | * @hide | 
|  | */ | 
|  | public static class AssetInputStreamSource extends Source { | 
|  | public AssetInputStreamSource(@NonNull AssetInputStream ais, | 
|  | @NonNull Resources res, @NonNull TypedValue value) { | 
|  | mAssetInputStream = ais; | 
|  | mResources = res; | 
|  |  | 
|  | if (value.density == TypedValue.DENSITY_DEFAULT) { | 
|  | mDensity = DisplayMetrics.DENSITY_DEFAULT; | 
|  | } else if (value.density != TypedValue.DENSITY_NONE) { | 
|  | mDensity = value.density; | 
|  | } else { | 
|  | mDensity = Bitmap.DENSITY_NONE; | 
|  | } | 
|  | } | 
|  |  | 
|  | private AssetInputStream mAssetInputStream; | 
|  | private final Resources  mResources; | 
|  | private final int        mDensity; | 
|  |  | 
|  | @Override | 
|  | public Resources getResources() { return mResources; } | 
|  |  | 
|  | @Override | 
|  | public int getDensity() { | 
|  | return mDensity; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ImageDecoder createImageDecoder() throws IOException { | 
|  | return new ImageDecoder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static class ResourceSource extends Source { | 
|  | ResourceSource(@NonNull Resources res, int resId) { | 
|  | mResources = res; | 
|  | mResId = resId; | 
|  | mResDensity = Bitmap.DENSITY_NONE; | 
|  | } | 
|  |  | 
|  | final Resources mResources; | 
|  | final int       mResId; | 
|  | int             mResDensity; | 
|  |  | 
|  | @Override | 
|  | public Resources getResources() { return mResources; } | 
|  |  | 
|  | @Override | 
|  | public int getDensity() { return mResDensity; } | 
|  |  | 
|  | @Override | 
|  | public ImageDecoder createImageDecoder() throws IOException { | 
|  | return new ImageDecoder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static class FileSource extends Source { | 
|  | FileSource(@NonNull File file) { | 
|  | mFile = file; | 
|  | } | 
|  |  | 
|  | private final File mFile; | 
|  |  | 
|  | @Override | 
|  | public ImageDecoder createImageDecoder() throws IOException { | 
|  | return new ImageDecoder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Contains information about the encoded image. | 
|  | */ | 
|  | public static class ImageInfo { | 
|  | private ImageDecoder mDecoder; | 
|  |  | 
|  | private ImageInfo(@NonNull ImageDecoder decoder) { | 
|  | mDecoder = decoder; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Size of the image, without scaling or cropping. | 
|  | */ | 
|  | @NonNull | 
|  | public Size getSize() { | 
|  | return new Size(0, 0); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * The mimeType of the image. | 
|  | */ | 
|  | @NonNull | 
|  | public String getMimeType() { | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Whether the image is animated. | 
|  | * | 
|  | * <p>Calling {@link #decodeDrawable} will return an | 
|  | * {@link AnimatedImageDrawable}.</p> | 
|  | */ | 
|  | public boolean isAnimated() { | 
|  | return mDecoder.mAnimated; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | *  Thrown if the provided data is incomplete. | 
|  | */ | 
|  | public static class IncompleteException extends IOException {}; | 
|  |  | 
|  | /** | 
|  | *  Optional listener supplied to {@link #decodeDrawable} or | 
|  | *  {@link #decodeBitmap}. | 
|  | */ | 
|  | public interface OnHeaderDecodedListener { | 
|  | /** | 
|  | *  Called when the header is decoded and the size is known. | 
|  | * | 
|  | *  @param decoder allows changing the default settings of the decode. | 
|  | *  @param info Information about the encoded image. | 
|  | *  @param source that created the decoder. | 
|  | */ | 
|  | void onHeaderDecoded(@NonNull ImageDecoder decoder, | 
|  | @NonNull ImageInfo info, @NonNull Source source); | 
|  |  | 
|  | }; | 
|  |  | 
|  | /** | 
|  | *  An Exception was thrown reading the {@link Source}. | 
|  | */ | 
|  | public static final int ERROR_SOURCE_EXCEPTION  = 1; | 
|  |  | 
|  | /** | 
|  | *  The encoded data was incomplete. | 
|  | */ | 
|  | public static final int ERROR_SOURCE_INCOMPLETE = 2; | 
|  |  | 
|  | /** | 
|  | *  The encoded data contained an error. | 
|  | */ | 
|  | public static final int ERROR_SOURCE_ERROR      = 3; | 
|  |  | 
|  | @Retention(SOURCE) | 
|  | public @interface Error {} | 
|  |  | 
|  | /** | 
|  | *  Optional listener supplied to the ImageDecoder. | 
|  | * | 
|  | *  Without this listener, errors will throw {@link java.io.IOException}. | 
|  | */ | 
|  | public interface OnPartialImageListener { | 
|  | /** | 
|  | *  Called when there is only a partial image to display. | 
|  | * | 
|  | *  If decoding is interrupted after having decoded a partial image, | 
|  | *  this listener lets the client know that and allows them to | 
|  | *  optionally finish the rest of the decode/creation process to create | 
|  | *  a partial {@link Drawable}/{@link Bitmap}. | 
|  | * | 
|  | *  @param error indicating what interrupted the decode. | 
|  | *  @param source that had the error. | 
|  | *  @return True to create and return a {@link Drawable}/{@link Bitmap} | 
|  | *      with partial data. False (which is the default) to abort the | 
|  | *      decode and throw {@link java.io.IOException}. | 
|  | */ | 
|  | boolean onPartialImage(@Error int error, @NonNull Source source); | 
|  | } | 
|  |  | 
|  | private boolean mAnimated; | 
|  | private Rect mOutPaddingRect; | 
|  |  | 
|  | public ImageDecoder() { | 
|  | mAnimated = true; // This is too avoid throwing an exception in AnimatedImageDrawable | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Create a new {@link Source} from an asset. | 
|  | * @hide | 
|  | * | 
|  | * @param res the {@link Resources} object containing the image data. | 
|  | * @param resId resource ID of the image data. | 
|  | *      // FIXME: Can be an @DrawableRes? | 
|  | * @return a new Source object, which can be passed to | 
|  | *      {@link #decodeDrawable} or {@link #decodeBitmap}. | 
|  | */ | 
|  | @NonNull | 
|  | public static Source createSource(@NonNull Resources res, int resId) | 
|  | { | 
|  | return new ResourceSource(res, resId); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Create a new {@link Source} from a {@link android.net.Uri}. | 
|  | * | 
|  | * @param cr to retrieve from. | 
|  | * @param uri of the image file. | 
|  | * @return a new Source object, which can be passed to | 
|  | *      {@link #decodeDrawable} or {@link #decodeBitmap}. | 
|  | */ | 
|  | @NonNull | 
|  | public static Source createSource(@NonNull ContentResolver cr, | 
|  | @NonNull Uri uri) { | 
|  | return new ContentResolverSource(cr, uri); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Create a new {@link Source} from a byte array. | 
|  | * | 
|  | * @param data byte array of compressed image data. | 
|  | * @param offset offset into data for where the decoder should begin | 
|  | *      parsing. | 
|  | * @param length number of bytes, beginning at offset, to parse. | 
|  | * @throws NullPointerException if data is null. | 
|  | * @throws ArrayIndexOutOfBoundsException if offset and length are | 
|  | *      not within data. | 
|  | * @hide | 
|  | */ | 
|  | @NonNull | 
|  | public static Source createSource(@NonNull byte[] data, int offset, | 
|  | int length) throws ArrayIndexOutOfBoundsException { | 
|  | if (offset < 0 || length < 0 || offset >= data.length || | 
|  | offset + length > data.length) { | 
|  | throw new ArrayIndexOutOfBoundsException( | 
|  | "invalid offset/length!"); | 
|  | } | 
|  | return new ByteArraySource(data, offset, length); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * See {@link #createSource(byte[], int, int). | 
|  | * @hide | 
|  | */ | 
|  | @NonNull | 
|  | public static Source createSource(@NonNull byte[] data) { | 
|  | return createSource(data, 0, data.length); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Create a new {@link Source} from a {@link java.nio.ByteBuffer}. | 
|  | * | 
|  | * <p>The returned {@link Source} effectively takes ownership of the | 
|  | * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after | 
|  | * this call.</p> | 
|  | * | 
|  | * Decoding will start from {@link java.nio.ByteBuffer#position()}. The | 
|  | * position after decoding is undefined. | 
|  | */ | 
|  | @NonNull | 
|  | public static Source createSource(@NonNull ByteBuffer buffer) { | 
|  | return new ByteBufferSource(buffer); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) | 
|  | * @hide | 
|  | */ | 
|  | public static Source createSource(Resources res, InputStream is) { | 
|  | return new InputStreamSource(res, is, Bitmap.getDefaultDensity()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) | 
|  | * @hide | 
|  | */ | 
|  | public static Source createSource(Resources res, InputStream is, int density) { | 
|  | return new InputStreamSource(res, is, density); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Create a new {@link Source} from a {@link java.io.File}. | 
|  | */ | 
|  | @NonNull | 
|  | public static Source createSource(@NonNull File file) { | 
|  | return new FileSource(file); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Return the width and height of a given sample size. | 
|  | * | 
|  | *  <p>This takes an input that functions like | 
|  | *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and | 
|  | *  height that can be acheived by sampling the encoded image. Other widths | 
|  | *  and heights may be supported, but will require an additional (internal) | 
|  | *  scaling step. Such internal scaling is *not* supported with | 
|  | *  {@link #setRequireUnpremultiplied} set to {@code true}.</p> | 
|  | * | 
|  | *  @param sampleSize Sampling rate of the encoded image. | 
|  | *  @return {@link android.util.Size} of the width and height after | 
|  | *      sampling. | 
|  | */ | 
|  | @NonNull | 
|  | public Size getSampledSize(int sampleSize) { | 
|  | return new Size(0, 0); | 
|  | } | 
|  |  | 
|  | // Modifiers | 
|  | /** | 
|  | *  Resize the output to have the following size. | 
|  | * | 
|  | *  @param width must be greater than 0. | 
|  | *  @param height must be greater than 0. | 
|  | */ | 
|  | public void setResize(int width, int height) { | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Resize based on a sample size. | 
|  | * | 
|  | *  <p>This has the same effect as passing the result of | 
|  | *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p> | 
|  | * | 
|  | *  @param sampleSize Sampling rate of the encoded image. | 
|  | */ | 
|  | public void setResize(int sampleSize) { | 
|  | } | 
|  |  | 
|  | // These need to stay in sync with ImageDecoder.cpp's Allocator enum. | 
|  | /** | 
|  | *  Use the default allocation for the pixel memory. | 
|  | * | 
|  | *  Will typically result in a {@link Bitmap.Config#HARDWARE} | 
|  | *  allocation, but may be software for small images. In addition, this will | 
|  | *  switch to software when HARDWARE is incompatible, e.g. | 
|  | *  {@link #setMutable}, {@link #setAsAlphaMask}. | 
|  | */ | 
|  | public static final int ALLOCATOR_DEFAULT = 0; | 
|  |  | 
|  | /** | 
|  | *  Use a software allocation for the pixel memory. | 
|  | * | 
|  | *  Useful for drawing to a software {@link Canvas} or for | 
|  | *  accessing the pixels on the final output. | 
|  | */ | 
|  | public static final int ALLOCATOR_SOFTWARE = 1; | 
|  |  | 
|  | /** | 
|  | *  Use shared memory for the pixel memory. | 
|  | * | 
|  | *  Useful for sharing across processes. | 
|  | */ | 
|  | public static final int ALLOCATOR_SHARED_MEMORY = 2; | 
|  |  | 
|  | /** | 
|  | *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}. | 
|  | * | 
|  | *  When this is combined with incompatible options, like | 
|  | *  {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable} | 
|  | *  / {@link #decodeBitmap} will throw an | 
|  | *  {@link java.lang.IllegalStateException}. | 
|  | */ | 
|  | public static final int ALLOCATOR_HARDWARE = 3; | 
|  |  | 
|  | /** @hide **/ | 
|  | @Retention(SOURCE) | 
|  | public @interface Allocator {}; | 
|  |  | 
|  | /** | 
|  | *  Choose the backing for the pixel memory. | 
|  | * | 
|  | *  This is ignored for animated drawables. | 
|  | * | 
|  | *  @param allocator Type of allocator to use. | 
|  | */ | 
|  | public void setAllocator(@Allocator int allocator) { } | 
|  |  | 
|  | /** | 
|  | *  Specify whether the {@link Bitmap} should have unpremultiplied pixels. | 
|  | * | 
|  | *  By default, ImageDecoder will create a {@link Bitmap} with | 
|  | *  premultiplied pixels, which is required for drawing with the | 
|  | *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling | 
|  | *  this method with a value of {@code true} will result in | 
|  | *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied | 
|  | *  pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with | 
|  | *  {@link #decodeDrawable}; attempting to decode an unpremultiplied | 
|  | *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}. | 
|  | */ | 
|  | public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Modify the image after decoding and scaling. | 
|  | * | 
|  | *  <p>This allows adding effects prior to returning a {@link Drawable} or | 
|  | *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap}, | 
|  | *  this is the only way to process the image after decoding.</p> | 
|  | * | 
|  | *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p> | 
|  | * | 
|  | *  <p>For an animated image, the drawing commands drawn on the | 
|  | *  {@link Canvas} will be recorded immediately and then applied to each | 
|  | *  frame.</p> | 
|  | */ | 
|  | public void setPostProcessor(@Nullable PostProcessor p) { } | 
|  |  | 
|  | /** | 
|  | *  Set (replace) the {@link OnPartialImageListener} on this object. | 
|  | * | 
|  | *  Will be called if there is an error in the input. Without one, a | 
|  | *  partial {@link Bitmap} will be created. | 
|  | */ | 
|  | public void setOnPartialImageListener(@Nullable OnPartialImageListener l) { } | 
|  |  | 
|  | /** | 
|  | *  Crop the output to {@code subset} of the (possibly) scaled image. | 
|  | * | 
|  | *  <p>{@code subset} must be contained within the size set by | 
|  | *  {@link #setResize} or the bounds of the image if setResize was not | 
|  | *  called. Otherwise an {@link IllegalStateException} will be thrown by | 
|  | *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p> | 
|  | * | 
|  | *  <p>NOT intended as a replacement for | 
|  | *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats, | 
|  | *  but merely crops the output.</p> | 
|  | */ | 
|  | public void setCrop(@Nullable Rect subset) { } | 
|  |  | 
|  | /** | 
|  | *  Set a Rect for retrieving nine patch padding. | 
|  | * | 
|  | *  If the image is a nine patch, this Rect will be set to the padding | 
|  | *  rectangle during decode. Otherwise it will not be modified. | 
|  | * | 
|  | *  @hide | 
|  | */ | 
|  | public void setOutPaddingRect(@NonNull Rect outPadding) { | 
|  | mOutPaddingRect = outPadding; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Specify whether the {@link Bitmap} should be mutable. | 
|  | * | 
|  | *  <p>By default, a {@link Bitmap} created will be immutable, but that can | 
|  | *  be changed with this call.</p> | 
|  | * | 
|  | *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE}, | 
|  | *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable. | 
|  | *  Attempting to combine them will throw an | 
|  | *  {@link java.lang.IllegalStateException}.</p> | 
|  | * | 
|  | *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable}, | 
|  | *  which would require retrieving the Bitmap from the returned Drawable in | 
|  | *  order to modify. Attempting to decode a mutable {@link Drawable} will | 
|  | *  throw an {@link java.lang.IllegalStateException}.</p> | 
|  | */ | 
|  | public ImageDecoder setMutable(boolean mutable) { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Specify whether to potentially save RAM at the expense of quality. | 
|  | * | 
|  | *  Setting this to {@code true} may result in a {@link Bitmap} with a | 
|  | *  denser {@link Bitmap.Config}, depending on the image. For example, for | 
|  | *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config} | 
|  | *  with no alpha information. | 
|  | */ | 
|  | public ImageDecoder setPreferRamOverQuality(boolean preferRamOverQuality) { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Specify whether to potentially treat the output as an alpha mask. | 
|  | * | 
|  | *  <p>If this is set to {@code true} and the image is encoded in a format | 
|  | *  with only one channel, treat that channel as alpha. Otherwise this call has | 
|  | *  no effect.</p> | 
|  | * | 
|  | *  <p>setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to | 
|  | *  combine them will result in {@link #decodeDrawable}/ | 
|  | *  {@link #decodeBitmap} throwing an | 
|  | *  {@link java.lang.IllegalStateException}.</p> | 
|  | */ | 
|  | public ImageDecoder setAsAlphaMask(boolean asAlphaMask) { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void close() { | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Create a {@link Drawable} from a {@code Source}. | 
|  | * | 
|  | *  @param src representing the encoded image. | 
|  | *  @param listener for learning the {@link ImageInfo} and changing any | 
|  | *      default settings on the {@code ImageDecoder}. If not {@code null}, | 
|  | *      this will be called on the same thread as {@code decodeDrawable} | 
|  | *      before that method returns. | 
|  | *  @return Drawable for displaying the image. | 
|  | *  @throws IOException if {@code src} is not found, is an unsupported | 
|  | *      format, or cannot be decoded for any reason. | 
|  | */ | 
|  | @NonNull | 
|  | public static Drawable decodeDrawable(@NonNull Source src, | 
|  | @Nullable OnHeaderDecodedListener listener) throws IOException { | 
|  | Bitmap bitmap = decodeBitmap(src, listener); | 
|  | return new BitmapDrawable(src.getResources(), bitmap); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * See {@link #decodeDrawable(Source, OnHeaderDecodedListener)}. | 
|  | */ | 
|  | @NonNull | 
|  | public static Drawable decodeDrawable(@NonNull Source src) | 
|  | throws IOException { | 
|  | return decodeDrawable(src, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Create a {@link Bitmap} from a {@code Source}. | 
|  | * | 
|  | *  @param src representing the encoded image. | 
|  | *  @param listener for learning the {@link ImageInfo} and changing any | 
|  | *      default settings on the {@code ImageDecoder}. If not {@code null}, | 
|  | *      this will be called on the same thread as {@code decodeBitmap} | 
|  | *      before that method returns. | 
|  | *  @return Bitmap containing the image. | 
|  | *  @throws IOException if {@code src} is not found, is an unsupported | 
|  | *      format, or cannot be decoded for any reason. | 
|  | */ | 
|  | @NonNull | 
|  | public static Bitmap decodeBitmap(@NonNull Source src, | 
|  | @Nullable OnHeaderDecodedListener listener) throws IOException { | 
|  | TypedValue value = new TypedValue(); | 
|  | value.density = src.getDensity(); | 
|  | ImageDecoder decoder = src.createImageDecoder(); | 
|  | if (listener != null) { | 
|  | listener.onHeaderDecoded(decoder, new ImageInfo(decoder), src); | 
|  | } | 
|  | return BitmapFactory.decodeResourceStream(src.getResources(), value, | 
|  | ((InputStreamSource) src).mInputStream, decoder.mOutPaddingRect, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}. | 
|  | */ | 
|  | @NonNull | 
|  | public static Bitmap decodeBitmap(@NonNull Source src) throws IOException { | 
|  | return decodeBitmap(src, null); | 
|  | } | 
|  |  | 
|  | public static final class DecodeException extends IOException { | 
|  | /** | 
|  | *  An Exception was thrown reading the {@link Source}. | 
|  | */ | 
|  | public static final int SOURCE_EXCEPTION  = 1; | 
|  |  | 
|  | /** | 
|  | *  The encoded data was incomplete. | 
|  | */ | 
|  | public static final int SOURCE_INCOMPLETE = 2; | 
|  |  | 
|  | /** | 
|  | *  The encoded data contained an error. | 
|  | */ | 
|  | public static final int SOURCE_MALFORMED_DATA      = 3; | 
|  |  | 
|  | @Error final int mError; | 
|  | @NonNull final Source mSource; | 
|  |  | 
|  | DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) { | 
|  | super(errorMessage(error, cause), cause); | 
|  | mError = error; | 
|  | mSource = source; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Private method called by JNI. | 
|  | */ | 
|  | @SuppressWarnings("unused") | 
|  | DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause, | 
|  | @NonNull Source source) { | 
|  | super(msg + errorMessage(error, cause), cause); | 
|  | mError = error; | 
|  | mSource = source; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Retrieve the reason that decoding was interrupted. | 
|  | * | 
|  | *  <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying | 
|  | *  {@link java.lang.Throwable} can be retrieved with | 
|  | *  {@link java.lang.Throwable#getCause}.</p> | 
|  | */ | 
|  | @Error | 
|  | public int getError() { | 
|  | return mError; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Retrieve the {@link Source Source} that was interrupted. | 
|  | * | 
|  | *  <p>This can be used for equality checking to find the Source which | 
|  | *  failed to completely decode.</p> | 
|  | */ | 
|  | @NonNull | 
|  | public Source getSource() { | 
|  | return mSource; | 
|  | } | 
|  |  | 
|  | private static String errorMessage(@Error int error, @Nullable Throwable cause) { | 
|  | switch (error) { | 
|  | case SOURCE_EXCEPTION: | 
|  | return "Exception in input: " + cause; | 
|  | case SOURCE_INCOMPLETE: | 
|  | return "Input was incomplete."; | 
|  | case SOURCE_MALFORMED_DATA: | 
|  | return "Input contained an error."; | 
|  | default: | 
|  | return ""; | 
|  | } | 
|  | } | 
|  | } | 
|  | } |