1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import static android.system.OsConstants.SEEK_CUR;
20 import static android.system.OsConstants.SEEK_SET;
21 
22 import static java.lang.annotation.RetentionPolicy.SOURCE;
23 
24 import android.annotation.AnyThread;
25 import android.annotation.IntDef;
26 import android.annotation.IntRange;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.Px;
30 import android.annotation.TestApi;
31 import android.annotation.WorkerThread;
32 import android.content.ContentResolver;
33 import android.content.res.AssetFileDescriptor;
34 import android.content.res.AssetManager;
35 import android.content.res.AssetManager.AssetInputStream;
36 import android.content.res.Resources;
37 import android.graphics.drawable.AnimatedImageDrawable;
38 import android.graphics.drawable.BitmapDrawable;
39 import android.graphics.drawable.Drawable;
40 import android.graphics.drawable.NinePatchDrawable;
41 import android.net.Uri;
42 import android.os.Build;
43 import android.system.ErrnoException;
44 import android.system.Os;
45 import android.util.DisplayMetrics;
46 import android.util.Size;
47 import android.util.TypedValue;
48 
49 import dalvik.system.CloseGuard;
50 
51 import libcore.io.IoUtils;
52 
53 import java.io.File;
54 import java.io.FileDescriptor;
55 import java.io.FileInputStream;
56 import java.io.FileNotFoundException;
57 import java.io.IOException;
58 import java.io.InputStream;
59 import java.lang.annotation.Retention;
60 import java.nio.ByteBuffer;
61 import java.util.Locale;
62 import java.util.Objects;
63 import java.util.concurrent.Callable;
64 import java.util.concurrent.atomic.AtomicBoolean;
65 
66 /**
67  *  <p>A class for converting encoded images (like {@code PNG}, {@code JPEG},
68  *  {@code WEBP}, {@code GIF}, or {@code HEIF}) into {@link Drawable} or
69  *  {@link Bitmap} objects.
70  *
71  *  <p>To use it, first create a {@link Source Source} using one of the
72  *  {@code createSource} overloads. For example, to decode from a {@link Uri}, call
73  *  {@link #createSource(ContentResolver, Uri)} and pass the result to
74  *  {@link #decodeDrawable(Source)} or {@link #decodeBitmap(Source)}:
75  *
76  *  <pre class="prettyprint">
77  *  File file = new File(...);
78  *  ImageDecoder.Source source = ImageDecoder.createSource(file);
79  *  Drawable drawable = ImageDecoder.decodeDrawable(source);
80  *  </pre>
81  *
82  *  <p>To change the default settings, pass the {@link Source Source} and an
83  *  {@link OnHeaderDecodedListener OnHeaderDecodedListener} to
84  *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} or
85  *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)}. For example, to
86  *  create a sampled image with half the width and height of the original image,
87  *  call {@link #setTargetSampleSize setTargetSampleSize(2)} inside
88  *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}:
89  *
90  *  <pre class="prettyprint">
91  *  OnHeaderDecodedListener listener = new OnHeaderDecodedListener() {
92  *      public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) {
93  *          decoder.setTargetSampleSize(2);
94  *      }
95  *  };
96  *  Drawable drawable = ImageDecoder.decodeDrawable(source, listener);
97  *  </pre>
98  *
99  *  <p>The {@link ImageInfo ImageInfo} contains information about the encoded image, like
100  *  its width and height, and the {@link Source Source} can be used to match to a particular
101  *  {@link Source Source} if a single {@link OnHeaderDecodedListener OnHeaderDecodedListener}
102  *  is used with multiple {@link Source Source} objects.
103  *
104  *  <p>The {@link OnHeaderDecodedListener OnHeaderDecodedListener} can also be implemented
105  *  as a lambda:
106  *
107  *  <pre class="prettyprint">
108  *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
109  *      decoder.setTargetSampleSize(2);
110  *  });
111  *  </pre>
112  *
113  *  <p>If the encoded image is an animated {@code GIF} or {@code WEBP},
114  *  {@link #decodeDrawable decodeDrawable} will return an {@link AnimatedImageDrawable}. To
115  *  start its animation, call {@link AnimatedImageDrawable#start AnimatedImageDrawable.start()}:
116  *
117  *  <pre class="prettyprint">
118  *  Drawable drawable = ImageDecoder.decodeDrawable(source);
119  *  if (drawable instanceof AnimatedImageDrawable) {
120  *      ((AnimatedImageDrawable) drawable).start();
121  *  }
122  *  </pre>
123  *
124  *  <p>By default, a {@link Bitmap} created by {@link ImageDecoder} (including
125  *  one that is inside a {@link Drawable}) will be immutable (i.e.
126  *  {@link Bitmap#isMutable Bitmap.isMutable()} returns {@code false}), and it
127  *  will typically have {@code Config} {@link Bitmap.Config#HARDWARE}. Although
128  *  these properties can be changed with {@link #setMutableRequired setMutableRequired(true)}
129  *  (which is only compatible with {@link #decodeBitmap(Source)} and
130  *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)}) and {@link #setAllocator},
131  *  it is also possible to apply custom effects regardless of the mutability of
132  *  the final returned object by passing a {@link PostProcessor} to
133  *  {@link #setPostProcessor setPostProcessor}. A {@link PostProcessor} can also be a lambda:
134  *
135  *  <pre class="prettyprint">
136  *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
137  *      decoder.setPostProcessor((canvas) -&gt; {
138  *              // This will create rounded corners.
139  *              Path path = new Path();
140  *              path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
141  *              int width = canvas.getWidth();
142  *              int height = canvas.getHeight();
143  *              path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
144  *              Paint paint = new Paint();
145  *              paint.setAntiAlias(true);
146  *              paint.setColor(Color.TRANSPARENT);
147  *              paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
148  *              canvas.drawPath(path, paint);
149  *              return PixelFormat.TRANSLUCENT;
150  *      });
151  *  });
152  *  </pre>
153  *
154  *  <p>If the encoded image is incomplete or contains an error, or if an
155  *  {@link Exception} occurs during decoding, a {@link DecodeException DecodeException}
156  *  will be thrown. In some cases, the {@link ImageDecoder} may have decoded part of
157  *  the image. In order to display the partial image, an
158  *  {@link OnPartialImageListener OnPartialImageListener} must be passed to
159  *  {@link #setOnPartialImageListener setOnPartialImageListener}. For example:
160  *
161  *  <pre class="prettyprint">
162  *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
163  *      decoder.setOnPartialImageListener((DecodeException e) -&gt; {
164  *              // Returning true indicates to create a Drawable or Bitmap even
165  *              // if the whole image could not be decoded. Any remaining lines
166  *              // will be blank.
167  *              return true;
168  *      });
169  *  });
170  *  </pre>
171  */
172 public final class ImageDecoder implements AutoCloseable {
173     /**
174      *  Source of encoded image data.
175      *
176      *  <p>References the data that will be used to decode a {@link Drawable}
177      *  or {@link Bitmap} in {@link #decodeDrawable decodeDrawable} or
178      *  {@link #decodeBitmap decodeBitmap}. Constructing a {@code Source} (with
179      *  one of the overloads of {@code createSource}) can be done on any thread
180      *  because the construction simply captures values. The real work is done
181      *  in {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}.
182      *
183      *  <p>A {@code Source} object can be reused to create multiple versions of the
184      *  same image. For example, to decode a full size image and its thumbnail,
185      *  the same {@code Source} can be used once with no
186      *  {@link OnHeaderDecodedListener OnHeaderDecodedListener} and once with an
187      *  implementation of {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}
188      *  that calls {@link #setTargetSize} with smaller dimensions. One {@code Source}
189      *  can even be used simultaneously in multiple threads.</p>
190      */
191     public static abstract class Source {
Source()192         private Source() {}
193 
194         @Nullable
getResources()195         Resources getResources() { return null; }
196 
getDensity()197         int getDensity() { return Bitmap.DENSITY_NONE; }
198 
computeDstDensity()199         final int computeDstDensity() {
200             Resources res = getResources();
201             if (res == null) {
202                 return Bitmap.getDefaultDensity();
203             }
204 
205             return res.getDisplayMetrics().densityDpi;
206         }
207 
208         @NonNull
createImageDecoder(boolean preferAnimation)209         abstract ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException;
210     };
211 
212     private static class ByteArraySource extends Source {
ByteArraySource(@onNull byte[] data, int offset, int length)213         ByteArraySource(@NonNull byte[] data, int offset, int length) {
214             mData = data;
215             mOffset = offset;
216             mLength = length;
217         };
218         private final byte[] mData;
219         private final int    mOffset;
220         private final int    mLength;
221 
222         @Override
createImageDecoder(boolean preferAnimation)223         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
224             return nCreate(mData, mOffset, mLength, preferAnimation, this);
225         }
226     }
227 
228     private static class ByteBufferSource extends Source {
ByteBufferSource(@onNull ByteBuffer buffer)229         ByteBufferSource(@NonNull ByteBuffer buffer) {
230             mBuffer = buffer;
231         }
232         private final ByteBuffer mBuffer;
233 
234         @Override
createImageDecoder(boolean preferAnimation)235         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
236             if (!mBuffer.isDirect() && mBuffer.hasArray()) {
237                 int offset = mBuffer.arrayOffset() + mBuffer.position();
238                 int length = mBuffer.limit() - mBuffer.position();
239                 return nCreate(mBuffer.array(), offset, length, preferAnimation, this);
240             }
241             ByteBuffer buffer = mBuffer.slice();
242             return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
243         }
244     }
245 
246     private static class ContentResolverSource extends Source {
ContentResolverSource(@onNull ContentResolver resolver, @NonNull Uri uri, @Nullable Resources res)247         ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri,
248                 @Nullable Resources res) {
249             mResolver = resolver;
250             mUri = uri;
251             mResources = res;
252         }
253 
254         private final ContentResolver mResolver;
255         private final Uri mUri;
256         private final Resources mResources;
257 
258         @Nullable
getResources()259         Resources getResources() { return mResources; }
260 
261         @Override
createImageDecoder(boolean preferAnimation)262         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
263             AssetFileDescriptor assetFd = null;
264             try {
265                 if (ContentResolver.SCHEME_CONTENT.equals(mUri.getScheme())) {
266                     assetFd = mResolver.openTypedAssetFileDescriptor(mUri,
267                             "image/*", null);
268                 } else {
269                     assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
270                 }
271             } catch (FileNotFoundException e) {
272                 // Handled below, along with the case where assetFd was set to null.
273             }
274 
275             if (assetFd == null) {
276                 // Some images cannot be opened as AssetFileDescriptors (e.g.
277                 // bmp, ico). Open them as InputStreams.
278                 InputStream is = mResolver.openInputStream(mUri);
279                 if (is == null) {
280                     throw new FileNotFoundException(mUri.toString());
281                 }
282 
283                 return createFromStream(is, true, preferAnimation, this);
284             }
285 
286             return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
287         }
288     }
289 
290     @NonNull
createFromFile(@onNull File file, boolean preferAnimation, @NonNull Source source)291     private static ImageDecoder createFromFile(@NonNull File file,
292             boolean preferAnimation, @NonNull Source source) throws IOException {
293         FileInputStream stream = new FileInputStream(file);
294         FileDescriptor fd = stream.getFD();
295         try {
296             Os.lseek(fd, 0, SEEK_CUR);
297         } catch (ErrnoException e) {
298             return createFromStream(stream, true, preferAnimation, source);
299         }
300 
301         ImageDecoder decoder = null;
302         try {
303             decoder = nCreate(fd, AssetFileDescriptor.UNKNOWN_LENGTH, preferAnimation, source);
304         } finally {
305             if (decoder == null) {
306                 IoUtils.closeQuietly(stream);
307             } else {
308                 decoder.mInputStream = stream;
309                 decoder.mOwnsInputStream = true;
310             }
311         }
312         return decoder;
313     }
314 
315     @NonNull
createFromStream(@onNull InputStream is, boolean closeInputStream, boolean preferAnimation, Source source)316     private static ImageDecoder createFromStream(@NonNull InputStream is,
317             boolean closeInputStream, boolean preferAnimation, Source source) throws IOException {
318         // Arbitrary size matches BitmapFactory.
319         byte[] storage = new byte[16 * 1024];
320         ImageDecoder decoder = null;
321         try {
322             decoder = nCreate(is, storage, preferAnimation, source);
323         } finally {
324             if (decoder == null) {
325                 if (closeInputStream) {
326                     IoUtils.closeQuietly(is);
327                 }
328             } else {
329                 decoder.mInputStream = is;
330                 decoder.mOwnsInputStream = closeInputStream;
331                 decoder.mTempStorage = storage;
332             }
333         }
334 
335         return decoder;
336     }
337 
338     @NonNull
createFromAssetFileDescriptor(@onNull AssetFileDescriptor assetFd, boolean preferAnimation, Source source)339     private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
340             boolean preferAnimation, Source source) throws IOException {
341         if (assetFd == null) {
342             throw new FileNotFoundException();
343         }
344         final FileDescriptor fd = assetFd.getFileDescriptor();
345         final long offset = assetFd.getStartOffset();
346 
347         ImageDecoder decoder = null;
348         try {
349             try {
350                 Os.lseek(fd, offset, SEEK_SET);
351                 decoder = nCreate(fd, assetFd.getDeclaredLength(), preferAnimation, source);
352             } catch (ErrnoException e) {
353                 decoder = createFromStream(new FileInputStream(fd), true, preferAnimation, source);
354             }
355         } finally {
356             if (decoder == null) {
357                 IoUtils.closeQuietly(assetFd);
358             } else {
359                 decoder.mAssetFd = assetFd;
360             }
361         }
362         return decoder;
363     }
364 
365     /**
366      * For backwards compatibility, this does *not* close the InputStream.
367      *
368      * Further, unlike other Sources, this one is not reusable.
369      */
370     private static class InputStreamSource extends Source {
InputStreamSource(Resources res, InputStream is, int inputDensity)371         InputStreamSource(Resources res, InputStream is, int inputDensity) {
372             if (is == null) {
373                 throw new IllegalArgumentException("The InputStream cannot be null");
374             }
375             mResources = res;
376             mInputStream = is;
377             mInputDensity = inputDensity;
378         }
379 
380         final Resources mResources;
381         InputStream mInputStream;
382         final int mInputDensity;
383 
384         @Override
getResources()385         public Resources getResources() { return mResources; }
386 
387         @Override
getDensity()388         public int getDensity() { return mInputDensity; }
389 
390         @Override
createImageDecoder(boolean preferAnimation)391         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
392 
393             synchronized (this) {
394                 if (mInputStream == null) {
395                     throw new IOException("Cannot reuse InputStreamSource");
396                 }
397                 InputStream is = mInputStream;
398                 mInputStream = null;
399                 return createFromStream(is, false, preferAnimation, this);
400             }
401         }
402     }
403 
404     /**
405      * Takes ownership of the AssetInputStream.
406      *
407      * @hide
408      */
409     public static class AssetInputStreamSource extends Source {
AssetInputStreamSource(@onNull AssetInputStream ais, @NonNull Resources res, @NonNull TypedValue value)410         public AssetInputStreamSource(@NonNull AssetInputStream ais,
411                 @NonNull Resources res, @NonNull TypedValue value) {
412             mAssetInputStream = ais;
413             mResources = res;
414 
415             if (value.density == TypedValue.DENSITY_DEFAULT) {
416                 mDensity = DisplayMetrics.DENSITY_DEFAULT;
417             } else if (value.density != TypedValue.DENSITY_NONE) {
418                 mDensity = value.density;
419             } else {
420                 mDensity = Bitmap.DENSITY_NONE;
421             }
422         }
423 
424         private AssetInputStream mAssetInputStream;
425         private final Resources  mResources;
426         private final int        mDensity;
427 
428         @Override
getResources()429         public Resources getResources() { return mResources; }
430 
431         @Override
getDensity()432         public int getDensity() {
433             return mDensity;
434         }
435 
436         @Override
createImageDecoder(boolean preferAnimation)437         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
438             synchronized (this) {
439                 if (mAssetInputStream == null) {
440                     throw new IOException("Cannot reuse AssetInputStreamSource");
441                 }
442                 AssetInputStream ais = mAssetInputStream;
443                 mAssetInputStream = null;
444                 return createFromAsset(ais, preferAnimation, this);
445             }
446         }
447     }
448 
449     private static class ResourceSource extends Source {
ResourceSource(@onNull Resources res, int resId)450         ResourceSource(@NonNull Resources res, int resId) {
451             mResources = res;
452             mResId = resId;
453             mResDensity = Bitmap.DENSITY_NONE;
454         }
455 
456         final Resources mResources;
457         final int       mResId;
458         int             mResDensity;
459         private Object  mLock = new Object();
460 
461         @Override
getResources()462         public Resources getResources() { return mResources; }
463 
464         @Override
getDensity()465         public int getDensity() {
466             synchronized (mLock) {
467                 return mResDensity;
468             }
469         }
470 
471         @Override
createImageDecoder(boolean preferAnimation)472         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
473             TypedValue value = new TypedValue();
474             // This is just used in order to access the underlying Asset and
475             // keep it alive.
476             InputStream is = mResources.openRawResource(mResId, value);
477 
478             synchronized (mLock) {
479                 if (value.density == TypedValue.DENSITY_DEFAULT) {
480                     mResDensity = DisplayMetrics.DENSITY_DEFAULT;
481                 } else if (value.density != TypedValue.DENSITY_NONE) {
482                     mResDensity = value.density;
483                 }
484             }
485 
486             return createFromAsset((AssetInputStream) is, preferAnimation, this);
487         }
488     }
489 
490     /**
491      *  ImageDecoder will own the AssetInputStream.
492      */
createFromAsset(AssetInputStream ais, boolean preferAnimation, Source source)493     private static ImageDecoder createFromAsset(AssetInputStream ais,
494             boolean preferAnimation, Source source) throws IOException {
495         ImageDecoder decoder = null;
496         try {
497             long asset = ais.getNativeAsset();
498             decoder = nCreate(asset, preferAnimation, source);
499         } finally {
500             if (decoder == null) {
501                 IoUtils.closeQuietly(ais);
502             } else {
503                 decoder.mInputStream = ais;
504                 decoder.mOwnsInputStream = true;
505             }
506         }
507         return decoder;
508     }
509 
510     private static class AssetSource extends Source {
AssetSource(@onNull AssetManager assets, @NonNull String fileName)511         AssetSource(@NonNull AssetManager assets, @NonNull String fileName) {
512             mAssets = assets;
513             mFileName = fileName;
514         }
515 
516         private final AssetManager mAssets;
517         private final String mFileName;
518 
519         @Override
createImageDecoder(boolean preferAnimation)520         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
521             InputStream is = mAssets.open(mFileName);
522             return createFromAsset((AssetInputStream) is, preferAnimation, this);
523         }
524     }
525 
526     private static class FileSource extends Source {
FileSource(@onNull File file)527         FileSource(@NonNull File file) {
528             mFile = file;
529         }
530 
531         private final File mFile;
532 
533         @Override
createImageDecoder(boolean preferAnimation)534         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
535             return createFromFile(mFile, preferAnimation, this);
536         }
537     }
538 
539     private static class CallableSource extends Source {
CallableSource(@onNull Callable<AssetFileDescriptor> callable)540         CallableSource(@NonNull Callable<AssetFileDescriptor> callable) {
541             mCallable = callable;
542         }
543 
544         private final Callable<AssetFileDescriptor> mCallable;
545 
546         @Override
createImageDecoder(boolean preferAnimation)547         public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
548             AssetFileDescriptor assetFd = null;
549             try {
550                 assetFd = mCallable.call();
551             } catch (Exception e) {
552                 if (e instanceof IOException) {
553                     throw (IOException) e;
554                 } else {
555                     throw new IOException(e);
556                 }
557             }
558             return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
559         }
560     }
561 
562     /**
563      *  Information about an encoded image.
564      */
565     public static class ImageInfo {
566         private final Size mSize;
567         private ImageDecoder mDecoder;
568 
ImageInfo(@onNull ImageDecoder decoder)569         private ImageInfo(@NonNull ImageDecoder decoder) {
570             mSize = new Size(decoder.mWidth, decoder.mHeight);
571             mDecoder = decoder;
572         }
573 
574         /**
575          * Size of the image, without scaling or cropping.
576          */
577         @NonNull
getSize()578         public Size getSize() {
579             return mSize;
580         }
581 
582         /**
583          * The mimeType of the image.
584          */
585         @NonNull
getMimeType()586         public String getMimeType() {
587             return mDecoder.getMimeType();
588         }
589 
590         /**
591          * Whether the image is animated.
592          *
593          * <p>If {@code true}, {@link #decodeDrawable decodeDrawable} will
594          * return an {@link AnimatedImageDrawable}.</p>
595          */
isAnimated()596         public boolean isAnimated() {
597             return mDecoder.mAnimated;
598         }
599 
600         /**
601          * If known, the color space the decoded bitmap will have. Note that the
602          * output color space is not guaranteed to be the color space the bitmap
603          * is encoded with. If not known (when the config is
604          * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error,
605          * it is set to null.
606          */
607         @Nullable
getColorSpace()608         public ColorSpace getColorSpace() {
609             return mDecoder.getColorSpace();
610         }
611     };
612 
613     /** @removed
614      * @deprecated Subsumed by {@link #DecodeException}.
615      */
616     @Deprecated
617     public static class IncompleteException extends IOException {};
618 
619     /**
620      *  Interface for changing the default settings of a decode.
621      *
622      *  <p>Supply an instance to
623      *  {@link #decodeDrawable(Source, OnHeaderDecodedListener) decodeDrawable}
624      *  or {@link #decodeBitmap(Source, OnHeaderDecodedListener) decodeBitmap},
625      *  which will call {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}
626      *  (in the same thread) once the size is known. The implementation of
627      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded} can then
628      *  change the decode settings as desired.
629      */
630     public static interface OnHeaderDecodedListener {
631         /**
632          *  Called by {@link ImageDecoder} when the header has been decoded and
633          *  the image size is known.
634          *
635          *  @param decoder the object performing the decode, for changing
636          *      its default settings.
637          *  @param info information about the encoded image.
638          *  @param source object that created {@code decoder}.
639          */
onHeaderDecoded(@onNull ImageDecoder decoder, @NonNull ImageInfo info, @NonNull Source source)640         public void onHeaderDecoded(@NonNull ImageDecoder decoder,
641                 @NonNull ImageInfo info, @NonNull Source source);
642 
643     };
644 
645     /** @removed
646      * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
647      */
648     @Deprecated
649     public static final int ERROR_SOURCE_EXCEPTION  = 1;
650 
651     /** @removed
652      * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
653      */
654     @Deprecated
655     public static final int ERROR_SOURCE_INCOMPLETE = 2;
656 
657     /** @removed
658      * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
659      */
660     @Deprecated
661     public static final int ERROR_SOURCE_ERROR      = 3;
662 
663     /**
664      *  Information about an interrupted decode.
665      */
666     public static final class DecodeException extends IOException {
667         /**
668          *  An Exception was thrown reading the {@link Source}.
669          */
670         public static final int SOURCE_EXCEPTION  = 1;
671 
672         /**
673          *  The encoded data was incomplete.
674          */
675         public static final int SOURCE_INCOMPLETE = 2;
676 
677         /**
678          *  The encoded data contained an error.
679          */
680         public static final int SOURCE_MALFORMED_DATA      = 3;
681 
682         /** @hide **/
683         @Retention(SOURCE)
684         @IntDef(value = { SOURCE_EXCEPTION, SOURCE_INCOMPLETE, SOURCE_MALFORMED_DATA },
685                 prefix = {"SOURCE_"})
686         public @interface Error {};
687 
688         @Error final int mError;
689         @NonNull final Source mSource;
690 
DecodeException(@rror int error, @Nullable Throwable cause, @NonNull Source source)691         DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
692             super(errorMessage(error, cause), cause);
693             mError = error;
694             mSource = source;
695         }
696 
697         /**
698          * Private method called by JNI.
699          */
700         @SuppressWarnings("unused")
DecodeException(@rror int error, @Nullable String msg, @Nullable Throwable cause, @NonNull Source source)701         DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
702                 @NonNull Source source) {
703             super(msg + errorMessage(error, cause), cause);
704             mError = error;
705             mSource = source;
706         }
707 
708         /**
709          *  Retrieve the reason that decoding was interrupted.
710          *
711          *  <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
712          *  {@link java.lang.Throwable} can be retrieved with
713          *  {@link java.lang.Throwable#getCause}.</p>
714          */
715         @Error
getError()716         public int getError() {
717             return mError;
718         }
719 
720         /**
721          *  Retrieve the {@link Source Source} that was interrupted.
722          *
723          *  <p>This can be used for equality checking to find the Source which
724          *  failed to completely decode.</p>
725          */
726         @NonNull
getSource()727         public Source getSource() {
728             return mSource;
729         }
730 
errorMessage(@rror int error, @Nullable Throwable cause)731         private static String errorMessage(@Error int error, @Nullable Throwable cause) {
732             switch (error) {
733                 case SOURCE_EXCEPTION:
734                     return "Exception in input: " + cause;
735                 case SOURCE_INCOMPLETE:
736                     return "Input was incomplete.";
737                 case SOURCE_MALFORMED_DATA:
738                     return "Input contained an error.";
739                 default:
740                     return "";
741             }
742         }
743     }
744 
745     /**
746      *  Interface for inspecting a {@link DecodeException DecodeException}
747      *  and potentially preventing it from being thrown.
748      *
749      *  <p>If an instance is passed to
750      *  {@link #setOnPartialImageListener setOnPartialImageListener}, a
751      *  {@link DecodeException DecodeException} that would otherwise have been
752      *  thrown can be inspected inside
753      *  {@link OnPartialImageListener#onPartialImage onPartialImage}.
754      *  If {@link OnPartialImageListener#onPartialImage onPartialImage} returns
755      *  {@code true}, a partial image will be created.
756      */
757     public static interface OnPartialImageListener {
758         /**
759          *  Called by {@link ImageDecoder} when there is only a partial image to
760          *  display.
761          *
762          *  <p>If decoding is interrupted after having decoded a partial image,
763          *  this method will be called. The implementation can inspect the
764          *  {@link DecodeException DecodeException} and optionally finish the
765          *  rest of the decode creation process to create a partial {@link Drawable}
766          *  or {@link Bitmap}.
767          *
768          *  @param exception exception containing information about the
769          *      decode interruption.
770          *  @return {@code true} to create and return a {@link Drawable} or
771          *      {@link Bitmap} with partial data. {@code false} (which is the
772          *      default) to abort the decode and throw {@code e}. Any undecoded
773          *      lines in the image will be blank.
774          */
onPartialImage(@onNull DecodeException exception)775         boolean onPartialImage(@NonNull DecodeException exception);
776     };
777 
778     // Fields
779     private long          mNativePtr;
780     private final int     mWidth;
781     private final int     mHeight;
782     private final boolean mAnimated;
783     private final boolean mIsNinePatch;
784 
785     private int        mDesiredWidth;
786     private int        mDesiredHeight;
787     private int        mAllocator = ALLOCATOR_DEFAULT;
788     private boolean    mUnpremultipliedRequired = false;
789     private boolean    mMutable = false;
790     private boolean    mConserveMemory = false;
791     private boolean    mDecodeAsAlphaMask = false;
792     private ColorSpace mDesiredColorSpace = null;
793     private Rect       mCropRect;
794     private Rect       mOutPaddingRect;
795     private Source     mSource;
796 
797     private PostProcessor          mPostProcessor;
798     private OnPartialImageListener mOnPartialImageListener;
799 
800     // Objects for interacting with the input.
801     private InputStream         mInputStream;
802     private boolean             mOwnsInputStream;
803     private byte[]              mTempStorage;
804     private AssetFileDescriptor mAssetFd;
805     private final AtomicBoolean mClosed = new AtomicBoolean();
806     private final CloseGuard    mCloseGuard = CloseGuard.get();
807 
808     /**
809      * Private constructor called by JNI. {@link #close} must be
810      * called after decoding to delete native resources.
811      */
812     @SuppressWarnings("unused")
ImageDecoder(long nativePtr, int width, int height, boolean animated, boolean isNinePatch)813     private ImageDecoder(long nativePtr, int width, int height,
814             boolean animated, boolean isNinePatch) {
815         mNativePtr = nativePtr;
816         mWidth = width;
817         mHeight = height;
818         mDesiredWidth = width;
819         mDesiredHeight = height;
820         mAnimated = animated;
821         mIsNinePatch = isNinePatch;
822         mCloseGuard.open("close");
823     }
824 
825     @Override
finalize()826     protected void finalize() throws Throwable {
827         try {
828             if (mCloseGuard != null) {
829                 mCloseGuard.warnIfOpen();
830             }
831 
832             // Avoid closing these in finalizer.
833             mInputStream = null;
834             mAssetFd = null;
835 
836             close();
837         } finally {
838             super.finalize();
839         }
840     }
841 
842     /**
843      * Return if the given MIME type is a supported file format that can be
844      * decoded by this class. This can be useful to determine if a file can be
845      * decoded directly, or if it needs to be converted into a more general
846      * format using an API like {@link ContentResolver#openTypedAssetFile}.
847      */
isMimeTypeSupported(@onNull String mimeType)848     public static boolean isMimeTypeSupported(@NonNull String mimeType) {
849         Objects.requireNonNull(mimeType);
850         switch (mimeType.toLowerCase(Locale.US)) {
851             case "image/png":
852             case "image/jpeg":
853             case "image/webp":
854             case "image/gif":
855             case "image/heif":
856             case "image/heic":
857             case "image/bmp":
858             case "image/x-ico":
859             case "image/vnd.wap.wbmp":
860             case "image/x-sony-arw":
861             case "image/x-canon-cr2":
862             case "image/x-adobe-dng":
863             case "image/x-nikon-nef":
864             case "image/x-nikon-nrw":
865             case "image/x-olympus-orf":
866             case "image/x-fuji-raf":
867             case "image/x-panasonic-rw2":
868             case "image/x-pentax-pef":
869             case "image/x-samsung-srw":
870                 return true;
871             default:
872                 return false;
873         }
874     }
875 
876     /**
877      * Create a new {@link Source Source} from a resource.
878      *
879      * @param res the {@link Resources} object containing the image data.
880      * @param resId resource ID of the image data.
881      * @return a new Source object, which can be passed to
882      *      {@link #decodeDrawable decodeDrawable} or
883      *      {@link #decodeBitmap decodeBitmap}.
884      */
885     @AnyThread
886     @NonNull
createSource(@onNull Resources res, int resId)887     public static Source createSource(@NonNull Resources res, int resId)
888     {
889         return new ResourceSource(res, resId);
890     }
891 
892     /**
893      * Create a new {@link Source Source} from a {@link android.net.Uri}.
894      *
895      * <h5>Accepts the following URI schemes:</h5>
896      * <ul>
897      * <li>content ({@link ContentResolver#SCHEME_CONTENT})</li>
898      * <li>android.resource ({@link ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
899      * <li>file ({@link ContentResolver#SCHEME_FILE})</li>
900      * </ul>
901      *
902      * @param cr to retrieve from.
903      * @param uri of the image file.
904      * @return a new Source object, which can be passed to
905      *      {@link #decodeDrawable decodeDrawable} or
906      *      {@link #decodeBitmap decodeBitmap}.
907      */
908     @AnyThread
909     @NonNull
createSource(@onNull ContentResolver cr, @NonNull Uri uri)910     public static Source createSource(@NonNull ContentResolver cr,
911             @NonNull Uri uri) {
912         return new ContentResolverSource(cr, uri, null);
913     }
914 
915     /**
916      * Provide Resources for density scaling.
917      *
918      * @hide
919      */
920     @AnyThread
921     @NonNull
createSource(@onNull ContentResolver cr, @NonNull Uri uri, @Nullable Resources res)922     public static Source createSource(@NonNull ContentResolver cr,
923             @NonNull Uri uri, @Nullable Resources res) {
924         return new ContentResolverSource(cr, uri, res);
925     }
926 
927     /**
928      * Create a new {@link Source Source} from a file in the "assets" directory.
929      */
930     @AnyThread
931     @NonNull
createSource(@onNull AssetManager assets, @NonNull String fileName)932     public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) {
933         return new AssetSource(assets, fileName);
934     }
935 
936     /**
937      * Create a new {@link Source Source} from a byte array.
938      *
939      * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
940      * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
941      * will continue reading from {@code data}, so its contents must not
942      * be modified, even after the {@code AnimatedImageDrawable} is returned.
943      * {@code data}'s contents should never be modified during decode.</p>
944      *
945      * @param data byte array of compressed image data.
946      * @param offset offset into data for where the decoder should begin
947      *      parsing.
948      * @param length number of bytes, beginning at offset, to parse.
949      * @return a new Source object, which can be passed to
950      *      {@link #decodeDrawable decodeDrawable} or
951      *      {@link #decodeBitmap decodeBitmap}.
952      * @throws NullPointerException if data is null.
953      * @throws ArrayIndexOutOfBoundsException if offset and length are
954      *      not within data.
955      */
956     @AnyThread
957     @NonNull
createSource(@onNull byte[] data, int offset, int length)958     public static Source createSource(@NonNull byte[] data, int offset,
959             int length) throws ArrayIndexOutOfBoundsException {
960         if (data == null) {
961             throw new NullPointerException("null byte[] in createSource!");
962         }
963         if (offset < 0 || length < 0 || offset >= data.length ||
964                 offset + length > data.length) {
965             throw new ArrayIndexOutOfBoundsException(
966                     "invalid offset/length!");
967         }
968         return new ByteArraySource(data, offset, length);
969     }
970 
971     /**
972      * Create a new {@link Source Source} from a byte array.
973      *
974      * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
975      * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
976      * will continue reading from {@code data}, so its contents must not
977      * be modified, even after the {@code AnimatedImageDrawable} is returned.
978      * {@code data}'s contents should never be modified during decode.</p>
979      *
980      * @param data byte array of compressed image data.
981      * @return a new Source object, which can be passed to
982      *      {@link #decodeDrawable decodeDrawable} or
983      *      {@link #decodeBitmap decodeBitmap}.
984      * @throws NullPointerException if data is null.
985      */
986     @AnyThread
987     @NonNull
createSource(@onNull byte[] data)988     public static Source createSource(@NonNull byte[] data) {
989         return createSource(data, 0, data.length);
990     }
991 
992     /**
993      * Create a new {@link Source Source} from a {@link java.nio.ByteBuffer}.
994      *
995      * <p>Decoding will start from {@link java.nio.ByteBuffer#position() buffer.position()}.
996      * The position of {@code buffer} will not be affected.</p>
997      *
998      * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
999      * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
1000      * will continue reading from the {@code buffer}, so its contents must not
1001      * be modified, even after the {@code AnimatedImageDrawable} is returned.
1002      * {@code buffer}'s contents should never be modified during decode.</p>
1003      *
1004      * @return a new Source object, which can be passed to
1005      *      {@link #decodeDrawable decodeDrawable} or
1006      *      {@link #decodeBitmap decodeBitmap}.
1007      */
1008     @AnyThread
1009     @NonNull
createSource(@onNull ByteBuffer buffer)1010     public static Source createSource(@NonNull ByteBuffer buffer) {
1011         return new ByteBufferSource(buffer);
1012     }
1013 
1014     /**
1015      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
1016      *
1017      * <p>Unlike other Sources, this one cannot be reused.</p>
1018      *
1019      * @hide
1020      */
1021     @AnyThread
1022     @NonNull
createSource(Resources res, InputStream is)1023     public static Source createSource(Resources res, InputStream is) {
1024         return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
1025     }
1026 
1027     /**
1028      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
1029      *
1030      * <p>Unlike other Sources, this one cannot be reused.</p>
1031      *
1032      * @hide
1033      */
1034     @AnyThread
1035     @TestApi
1036     @NonNull
createSource(Resources res, InputStream is, int density)1037     public static Source createSource(Resources res, InputStream is, int density) {
1038         return new InputStreamSource(res, is, density);
1039     }
1040 
1041     /**
1042      * Create a new {@link Source Source} from a {@link java.io.File}.
1043      * <p>
1044      * This method should only be used for files that you have direct access to;
1045      * if you'd like to work with files hosted outside your app, use an API like
1046      * {@link #createSource(Callable)} or
1047      * {@link #createSource(ContentResolver, Uri)}.
1048      * @return a new Source object, which can be passed to
1049      *      {@link #decodeDrawable decodeDrawable} or
1050      *      {@link #decodeBitmap decodeBitmap}.
1051      */
1052     @AnyThread
1053     @NonNull
createSource(@onNull File file)1054     public static Source createSource(@NonNull File file) {
1055         return new FileSource(file);
1056     }
1057 
1058     /**
1059      * Create a new {@link Source Source} from a {@link Callable} that returns a
1060      * new {@link AssetFileDescriptor} for each request. This provides control
1061      * over how the {@link AssetFileDescriptor} is created, such as passing
1062      * options into {@link ContentResolver#openTypedAssetFileDescriptor}, or
1063      * enabling use of a {@link android.os.CancellationSignal}.
1064      * <p>
1065      * It's important for the given {@link Callable} to return a new, unique
1066      * {@link AssetFileDescriptor} for each invocation, to support reuse of the
1067      * returned {@link Source Source}.
1068      *
1069      * @return a new Source object, which can be passed to
1070      *         {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap
1071      *         decodeBitmap}.
1072      */
1073     @AnyThread
1074     @NonNull
createSource(@onNull Callable<AssetFileDescriptor> callable)1075     public static Source createSource(@NonNull Callable<AssetFileDescriptor> callable) {
1076         return new CallableSource(callable);
1077     }
1078 
1079     /**
1080      *  Return the width and height of a given sample size.
1081      *
1082      *  <p>This takes an input that functions like
1083      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
1084      *  height that can be achieved by sampling the encoded image. Other widths
1085      *  and heights may be supported, but will require an additional (internal)
1086      *  scaling step. Such internal scaling is *not* supported with
1087      *  {@link #setUnpremultipliedRequired} set to {@code true}.</p>
1088      *
1089      *  @param sampleSize Sampling rate of the encoded image.
1090      *  @return {@link android.util.Size} of the width and height after
1091      *      sampling.
1092      */
1093     @NonNull
getSampledSize(int sampleSize)1094     private Size getSampledSize(int sampleSize) {
1095         if (sampleSize <= 0) {
1096             throw new IllegalArgumentException("sampleSize must be positive! "
1097                     + "provided " + sampleSize);
1098         }
1099         if (mNativePtr == 0) {
1100             throw new IllegalStateException("ImageDecoder is closed!");
1101         }
1102 
1103         return nGetSampledSize(mNativePtr, sampleSize);
1104     }
1105 
1106     // Modifiers
1107     /** @removed
1108      * @deprecated Renamed to {@link #setTargetSize}.
1109      */
1110     @Deprecated
setResize(int width, int height)1111     public ImageDecoder setResize(int width, int height) {
1112         this.setTargetSize(width, height);
1113         return this;
1114     }
1115 
1116     /**
1117      *  Specify the size of the output {@link Drawable} or {@link Bitmap}.
1118      *
1119      *  <p>By default, the output size will match the size of the encoded
1120      *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1121      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1122      *
1123      *  <p>This will sample or scale the output to an arbitrary size that may
1124      *  be smaller or larger than the encoded size.</p>
1125      *
1126      *  <p>Only the last call to this or {@link #setTargetSampleSize} is
1127      *  respected.</p>
1128      *
1129      *  <p>Like all setters on ImageDecoder, this must be called inside
1130      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1131      *
1132      *  @param width width in pixels of the output, must be greater than 0
1133      *  @param height height in pixels of the output, must be greater than 0
1134      */
setTargetSize(@x @ntRangefrom = 1) int width, @Px @IntRange(from = 1) int height)1135     public void setTargetSize(@Px @IntRange(from = 1) int width,
1136                               @Px @IntRange(from = 1) int height) {
1137         if (width <= 0 || height <= 0) {
1138             throw new IllegalArgumentException("Dimensions must be positive! "
1139                     + "provided (" + width + ", " + height + ")");
1140         }
1141 
1142         mDesiredWidth = width;
1143         mDesiredHeight = height;
1144     }
1145 
1146     /** @removed
1147      * @deprecated Renamed to {@link #setTargetSampleSize}.
1148      */
1149     @Deprecated
setResize(int sampleSize)1150     public ImageDecoder setResize(int sampleSize) {
1151         this.setTargetSampleSize(sampleSize);
1152         return this;
1153     }
1154 
getTargetDimension(int original, int sampleSize, int computed)1155     private int getTargetDimension(int original, int sampleSize, int computed) {
1156         // Sampling will never result in a smaller size than 1.
1157         if (sampleSize >= original) {
1158             return 1;
1159         }
1160 
1161         // Use integer divide to find the desired size. If that is what
1162         // getSampledSize computed, that is the size to use.
1163         int target = original / sampleSize;
1164         if (computed == target) {
1165             return computed;
1166         }
1167 
1168         // If sampleSize does not divide evenly into original, the decoder
1169         // may round in either direction. It just needs to get a result that
1170         // is close.
1171         int reverse = computed * sampleSize;
1172         if (Math.abs(reverse - original) < sampleSize) {
1173             // This is the size that can be decoded most efficiently.
1174             return computed;
1175         }
1176 
1177         // The decoder could not get close (e.g. it is a DNG image).
1178         return target;
1179     }
1180 
1181     /**
1182      *  Set the target size with a sampleSize.
1183      *
1184      *  <p>By default, the output size will match the size of the encoded
1185      *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1186      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1187      *
1188      *  <p>Requests the decoder to subsample the original image, returning a
1189      *  smaller image to save memory. The {@code sampleSize} is the number of pixels
1190      *  in either dimension that correspond to a single pixel in the output.
1191      *  For example, {@code sampleSize == 4} returns an image that is 1/4 the
1192      *  width/height of the original, and 1/16 the number of pixels.</p>
1193      *
1194      *  <p>Must be greater than or equal to 1.</p>
1195      *
1196      *  <p>This has the same effect as calling {@link #setTargetSize} with
1197      *  dimensions based on the {@code sampleSize}. Unlike dividing the original
1198      *  width and height by the {@code sampleSize} manually, calling this method
1199      *  allows {@code ImageDecoder} to round in the direction that it can do most
1200      *  efficiently.</p>
1201      *
1202      *  <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
1203      *
1204      *  <p>Like all setters on ImageDecoder, this must be called inside
1205      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1206      *
1207      *  @param sampleSize sampling rate of the encoded image.
1208      */
setTargetSampleSize(@ntRangefrom = 1) int sampleSize)1209     public void setTargetSampleSize(@IntRange(from = 1) int sampleSize) {
1210         Size size = this.getSampledSize(sampleSize);
1211         int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
1212         int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
1213         this.setTargetSize(targetWidth, targetHeight);
1214     }
1215 
requestedResize()1216     private boolean requestedResize() {
1217         return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
1218     }
1219 
1220     // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
1221     /**
1222      *  Use the default allocation for the pixel memory.
1223      *
1224      *  Will typically result in a {@link Bitmap.Config#HARDWARE}
1225      *  allocation, but may be software for small images. In addition, this will
1226      *  switch to software when HARDWARE is incompatible, e.g.
1227      *  {@link #setMutableRequired setMutableRequired(true)} or
1228      *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)}.
1229      */
1230     public static final int ALLOCATOR_DEFAULT = 0;
1231 
1232     /**
1233      *  Use a software allocation for the pixel memory.
1234      *
1235      *  <p>Useful for drawing to a software {@link Canvas} or for
1236      *  accessing the pixels on the final output.
1237      */
1238     public static final int ALLOCATOR_SOFTWARE = 1;
1239 
1240     /**
1241      *  Use shared memory for the pixel memory.
1242      *
1243      *  <p>Useful for sharing across processes.
1244      */
1245     public static final int ALLOCATOR_SHARED_MEMORY = 2;
1246 
1247     /**
1248      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
1249      *
1250      *  <p>When this is combined with incompatible options, like
1251      *  {@link #setMutableRequired setMutableRequired(true)} or
1252      *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)},
1253      *  {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}
1254      *  will throw an {@link java.lang.IllegalStateException}.
1255      */
1256     public static final int ALLOCATOR_HARDWARE = 3;
1257 
1258     /** @hide **/
1259     @Retention(SOURCE)
1260     @IntDef(value = { ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE,
1261               ALLOCATOR_SHARED_MEMORY, ALLOCATOR_HARDWARE },
1262               prefix = {"ALLOCATOR_"})
1263     public @interface Allocator {};
1264 
1265     /**
1266      *  Choose the backing for the pixel memory.
1267      *
1268      *  <p>This is ignored for animated drawables.</p>
1269      *
1270      *  <p>Like all setters on ImageDecoder, this must be called inside
1271      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1272      *
1273      *  @param allocator Type of allocator to use.
1274      */
setAllocator(@llocator int allocator)1275     public void setAllocator(@Allocator int allocator) {
1276         if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
1277             throw new IllegalArgumentException("invalid allocator " + allocator);
1278         }
1279         mAllocator = allocator;
1280     }
1281 
1282     /**
1283      *  Return the allocator for the pixel memory.
1284      */
1285     @Allocator
getAllocator()1286     public int getAllocator() {
1287         return mAllocator;
1288     }
1289 
1290     /**
1291      *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
1292      *
1293      *  <p>By default, ImageDecoder will create a {@link Bitmap} with
1294      *  premultiplied pixels, which is required for drawing with the
1295      *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
1296      *  this method with a value of {@code true} will result in
1297      *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
1298      *  pixels. See {@link Bitmap#isPremultiplied Bitmap.isPremultiplied()}.
1299      *  This is incompatible with {@link #decodeDrawable decodeDrawable};
1300      *  attempting to decode an unpremultiplied {@link Drawable} will throw an
1301      *  {@link java.lang.IllegalStateException}. </p>
1302      *
1303      *  <p>Like all setters on ImageDecoder, this must be called inside
1304      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1305      */
setUnpremultipliedRequired(boolean unpremultipliedRequired)1306     public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
1307         mUnpremultipliedRequired = unpremultipliedRequired;
1308     }
1309 
1310     /** @removed
1311      * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
1312      */
1313     @Deprecated
setRequireUnpremultiplied(boolean unpremultipliedRequired)1314     public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
1315         this.setUnpremultipliedRequired(unpremultipliedRequired);
1316         return this;
1317     }
1318 
1319     /**
1320      *  Return whether the {@link Bitmap} will have unpremultiplied pixels.
1321      */
isUnpremultipliedRequired()1322     public boolean isUnpremultipliedRequired() {
1323         return mUnpremultipliedRequired;
1324     }
1325 
1326     /** @removed
1327      * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
1328      */
1329     @Deprecated
getRequireUnpremultiplied()1330     public boolean getRequireUnpremultiplied() {
1331         return this.isUnpremultipliedRequired();
1332     }
1333 
1334     /**
1335      *  Modify the image after decoding and scaling.
1336      *
1337      *  <p>This allows adding effects prior to returning a {@link Drawable} or
1338      *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
1339      *  this is the only way to process the image after decoding.</p>
1340      *
1341      *  <p>If combined with {@link #setTargetSize} and/or {@link #setCrop},
1342      *  {@link PostProcessor#onPostProcess} occurs last.</p>
1343      *
1344      *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
1345      *
1346      *  <p>For an animated image, the drawing commands drawn on the
1347      *  {@link Canvas} will be recorded immediately and then applied to each
1348      *  frame.</p>
1349      *
1350      *  <p>Like all setters on ImageDecoder, this must be called inside
1351      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1352      *
1353      */
setPostProcessor(@ullable PostProcessor postProcessor)1354     public void setPostProcessor(@Nullable PostProcessor postProcessor) {
1355         mPostProcessor = postProcessor;
1356     }
1357 
1358     /**
1359      *  Return the {@link PostProcessor} currently set.
1360      */
1361     @Nullable
getPostProcessor()1362     public PostProcessor getPostProcessor() {
1363         return mPostProcessor;
1364     }
1365 
1366     /**
1367      *  Set (replace) the {@link OnPartialImageListener} on this object.
1368      *
1369      *  <p>Will be called if there is an error in the input. Without one, an
1370      *  error will result in an {@code Exception} being thrown.</p>
1371      *
1372      *  <p>Like all setters on ImageDecoder, this must be called inside
1373      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1374      *
1375      */
setOnPartialImageListener(@ullable OnPartialImageListener listener)1376     public void setOnPartialImageListener(@Nullable OnPartialImageListener listener) {
1377         mOnPartialImageListener = listener;
1378     }
1379 
1380     /**
1381      *  Return the {@link OnPartialImageListener OnPartialImageListener} currently set.
1382      */
1383     @Nullable
getOnPartialImageListener()1384     public OnPartialImageListener getOnPartialImageListener() {
1385         return mOnPartialImageListener;
1386     }
1387 
1388     /**
1389      *  Crop the output to {@code subset} of the (possibly) scaled image.
1390      *
1391      *  <p>{@code subset} must be contained within the size set by
1392      *  {@link #setTargetSize} or the bounds of the image if setTargetSize was
1393      *  not called. Otherwise an {@link IllegalStateException} will be thrown by
1394      *  {@link #decodeDrawable decodeDrawable}/{@link #decodeBitmap decodeBitmap}.</p>
1395      *
1396      *  <p>NOT intended as a replacement for
1397      *  {@link BitmapRegionDecoder#decodeRegion BitmapRegionDecoder.decodeRegion()}.
1398      *  This supports all formats, but merely crops the output.</p>
1399      *
1400      *  <p>Like all setters on ImageDecoder, this must be called inside
1401      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1402      *
1403      */
setCrop(@ullable Rect subset)1404     public void setCrop(@Nullable Rect subset) {
1405         mCropRect = subset;
1406     }
1407 
1408     /**
1409      *  Return the cropping rectangle, if set.
1410      */
1411     @Nullable
getCrop()1412     public Rect getCrop() {
1413         return mCropRect;
1414     }
1415 
1416     /**
1417      *  Set a Rect for retrieving nine patch padding.
1418      *
1419      *  If the image is a nine patch, this Rect will be set to the padding
1420      *  rectangle during decode. Otherwise it will not be modified.
1421      *
1422      *  <p>Like all setters on ImageDecoder, this must be called inside
1423      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1424      *
1425      *  @hide
1426      *  Must be public for access from android.graphics.drawable,
1427      *  but must not be called from outside the UI module.
1428      */
setOutPaddingRect(@onNull Rect outPadding)1429     public void setOutPaddingRect(@NonNull Rect outPadding) {
1430         mOutPaddingRect = outPadding;
1431     }
1432 
1433     /**
1434      *  Specify whether the {@link Bitmap} should be mutable.
1435      *
1436      *  <p>By default, a {@link Bitmap} created by {@link #decodeBitmap decodeBitmap}
1437      *  will be immutable i.e. {@link Bitmap#isMutable() Bitmap.isMutable()} returns
1438      *  {@code false}. This can be changed with {@code setMutableRequired(true)}.
1439      *
1440      *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
1441      *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
1442      *  Attempting to combine them will throw an
1443      *  {@link java.lang.IllegalStateException}.</p>
1444      *
1445      *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable decodeDrawable},
1446      *  which would require retrieving the Bitmap from the returned Drawable in
1447      *  order to modify. Attempting to decode a mutable {@link Drawable} will
1448      *  throw an {@link java.lang.IllegalStateException}.</p>
1449      *
1450      *  <p>Like all setters on ImageDecoder, this must be called inside
1451      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1452      */
setMutableRequired(boolean mutable)1453     public void setMutableRequired(boolean mutable) {
1454         mMutable = mutable;
1455     }
1456 
1457     /** @removed
1458      * @deprecated Renamed to {@link #setMutableRequired}.
1459      */
1460     @Deprecated
setMutable(boolean mutable)1461     public ImageDecoder setMutable(boolean mutable) {
1462         this.setMutableRequired(mutable);
1463         return this;
1464     }
1465 
1466     /**
1467      *  Return whether the decoded {@link Bitmap} will be mutable.
1468      */
isMutableRequired()1469     public boolean isMutableRequired() {
1470         return mMutable;
1471     }
1472 
1473     /** @removed
1474      * @deprecated Renamed to {@link #isMutableRequired}.
1475      */
1476     @Deprecated
getMutable()1477     public boolean getMutable() {
1478         return this.isMutableRequired();
1479     }
1480 
1481     /**
1482      * Save memory if possible by using a denser {@link Bitmap.Config} at the
1483      * cost of some image quality.
1484      *
1485      * <p>For example an opaque 8-bit image may be compressed into an
1486      * {@link Bitmap.Config#RGB_565} configuration, sacrificing image
1487      * quality to save memory.
1488      */
1489     public static final int MEMORY_POLICY_LOW_RAM = 0;
1490 
1491     /**
1492      * Use the most natural {@link Bitmap.Config} for the internal {@link Bitmap}.
1493      *
1494      * <p>This is the recommended default for most applications and usages. This
1495      * will use the closest {@link Bitmap.Config} for the encoded source. If the
1496      * encoded source does not exactly match any {@link Bitmap.Config}, the next
1497      * highest quality {@link Bitmap.Config} will be used avoiding any loss in
1498      * image quality.
1499      */
1500     public static final int MEMORY_POLICY_DEFAULT  = 1;
1501 
1502     /** @hide **/
1503     @Retention(SOURCE)
1504     @IntDef(value = { MEMORY_POLICY_DEFAULT, MEMORY_POLICY_LOW_RAM },
1505               prefix = {"MEMORY_POLICY_"})
1506     public @interface MemoryPolicy {};
1507 
1508     /**
1509      *  Specify the memory policy for the decoded {@link Bitmap}.
1510      *
1511      *  <p>Like all setters on ImageDecoder, this must be called inside
1512      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1513      */
setMemorySizePolicy(@emoryPolicy int policy)1514     public void setMemorySizePolicy(@MemoryPolicy int policy) {
1515         mConserveMemory = (policy == MEMORY_POLICY_LOW_RAM);
1516     }
1517 
1518     /**
1519      *  Retrieve the memory policy for the decoded {@link Bitmap}.
1520      */
1521     @MemoryPolicy
getMemorySizePolicy()1522     public int getMemorySizePolicy() {
1523         return mConserveMemory ? MEMORY_POLICY_LOW_RAM : MEMORY_POLICY_DEFAULT;
1524     }
1525 
1526     /** @removed
1527      * @deprecated Replaced by {@link #setMemorySizePolicy}.
1528      */
1529     @Deprecated
setConserveMemory(boolean conserveMemory)1530     public void setConserveMemory(boolean conserveMemory) {
1531         mConserveMemory = conserveMemory;
1532     }
1533 
1534     /** @removed
1535      * @deprecated Replaced by {@link #getMemorySizePolicy}.
1536      */
1537     @Deprecated
getConserveMemory()1538     public boolean getConserveMemory() {
1539         return mConserveMemory;
1540     }
1541 
1542     /**
1543      *  Specify whether to potentially treat the output as an alpha mask.
1544      *
1545      *  <p>If this is set to {@code true} and the image is encoded in a format
1546      *  with only one channel, treat that channel as alpha. Otherwise this call has
1547      *  no effect.</p>
1548      *
1549      *  <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
1550      *  combine them will result in {@link #decodeDrawable decodeDrawable}/
1551      *  {@link #decodeBitmap decodeBitmap} throwing an
1552      *  {@link java.lang.IllegalStateException}.</p>
1553      *
1554      *  <p>Like all setters on ImageDecoder, this must be called inside
1555      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1556      */
setDecodeAsAlphaMaskEnabled(boolean enabled)1557     public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
1558         mDecodeAsAlphaMask = enabled;
1559     }
1560 
1561     /** @removed
1562      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1563      */
1564     @Deprecated
setDecodeAsAlphaMask(boolean enabled)1565     public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
1566         this.setDecodeAsAlphaMaskEnabled(enabled);
1567         return this;
1568     }
1569 
1570     /** @removed
1571      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1572      */
1573     @Deprecated
setAsAlphaMask(boolean asAlphaMask)1574     public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
1575         this.setDecodeAsAlphaMask(asAlphaMask);
1576         return this;
1577     }
1578 
1579     /**
1580      *  Return whether to treat single channel input as alpha.
1581      *
1582      *  <p>This returns whether {@link #setDecodeAsAlphaMaskEnabled} was set to
1583      *  {@code true}. It may still return {@code true} even if the image has
1584      *  more than one channel and therefore will not be treated as an alpha
1585      *  mask.</p>
1586      */
isDecodeAsAlphaMaskEnabled()1587     public boolean isDecodeAsAlphaMaskEnabled() {
1588         return mDecodeAsAlphaMask;
1589     }
1590 
1591     /** @removed
1592      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1593      */
1594     @Deprecated
getDecodeAsAlphaMask()1595     public boolean getDecodeAsAlphaMask() {
1596         return mDecodeAsAlphaMask;
1597     }
1598 
1599     /** @removed
1600      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1601      */
1602     @Deprecated
getAsAlphaMask()1603     public boolean getAsAlphaMask() {
1604         return this.getDecodeAsAlphaMask();
1605     }
1606 
1607     /**
1608      * Specify the desired {@link ColorSpace} for the output.
1609      *
1610      * <p>If non-null, the decoder will try to decode into {@code colorSpace}.
1611      * If it is null, which is the default, or the request cannot be met, the
1612      * decoder will pick either the color space embedded in the image or the
1613      * {@link ColorSpace} best suited for the requested image configuration
1614      * (for instance {@link ColorSpace.Named#SRGB sRGB} for the
1615      * {@link Bitmap.Config#ARGB_8888} configuration and
1616      * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
1617      * {@link Bitmap.Config#RGBA_F16}).</p>
1618      *
1619      * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
1620      * currently supported. An <code>IllegalArgumentException</code> will
1621      * be thrown by {@link #decodeDrawable decodeDrawable}/
1622      * {@link #decodeBitmap decodeBitmap} when setting a non-RGB color space
1623      * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
1624      *
1625      * <p class="note">The specified color space's transfer function must be
1626      * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
1627      * <code>IllegalArgumentException</code> will be thrown by the decode methods
1628      * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
1629      * specified color space returns null.</p>
1630      *
1631      * <p>Like all setters on ImageDecoder, this must be called inside
1632      * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1633      */
setTargetColorSpace(ColorSpace colorSpace)1634     public void setTargetColorSpace(ColorSpace colorSpace) {
1635         mDesiredColorSpace = colorSpace;
1636     }
1637 
1638     /**
1639      * Closes this resource, relinquishing any underlying resources. This method
1640      * is invoked automatically on objects managed by the try-with-resources
1641      * statement.
1642      *
1643      * <p>This is an implementation detail of {@link ImageDecoder}, and should
1644      * never be called manually.</p>
1645      */
1646     @Override
close()1647     public void close() {
1648         mCloseGuard.close();
1649         if (!mClosed.compareAndSet(false, true)) {
1650             return;
1651         }
1652         nClose(mNativePtr);
1653         mNativePtr = 0;
1654 
1655         if (mOwnsInputStream) {
1656             IoUtils.closeQuietly(mInputStream);
1657         }
1658         IoUtils.closeQuietly(mAssetFd);
1659 
1660         mInputStream = null;
1661         mAssetFd = null;
1662         mTempStorage = null;
1663     }
1664 
checkState(boolean animated)1665     private void checkState(boolean animated) {
1666         if (mNativePtr == 0) {
1667             throw new IllegalStateException("Cannot use closed ImageDecoder!");
1668         }
1669 
1670         checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
1671 
1672         // animated ignores the allocator, so no need to check for incompatible
1673         // fields.
1674         if (!animated && mAllocator == ALLOCATOR_HARDWARE) {
1675             if (mMutable) {
1676                 throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
1677             }
1678             if (mDecodeAsAlphaMask) {
1679                 throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
1680             }
1681         }
1682 
1683         if (mPostProcessor != null && mUnpremultipliedRequired) {
1684             throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
1685         }
1686     }
1687 
checkSubset(int width, int height, Rect r)1688     private static void checkSubset(int width, int height, Rect r) {
1689         if (r == null) {
1690             return;
1691         }
1692         if (r.width() <= 0 || r.height() <= 0) {
1693             throw new IllegalStateException("Subset " + r + " is empty/unsorted");
1694         }
1695         if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
1696             throw new IllegalStateException("Subset " + r + " not contained by "
1697                     + "scaled image bounds: (" + width + " x " + height + ")");
1698         }
1699     }
1700 
checkForExtended()1701     private boolean checkForExtended() {
1702         if (mDesiredColorSpace == null) {
1703             return false;
1704         }
1705         return mDesiredColorSpace == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
1706                 || mDesiredColorSpace == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
1707     }
1708 
getColorSpacePtr()1709     private long getColorSpacePtr() {
1710         if (mDesiredColorSpace == null) {
1711             return 0;
1712         }
1713         return mDesiredColorSpace.getNativeInstance();
1714     }
1715 
1716     @WorkerThread
1717     @NonNull
decodeBitmapInternal()1718     private Bitmap decodeBitmapInternal() throws IOException {
1719         checkState(false);
1720         return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
1721                 mDesiredWidth, mDesiredHeight, mCropRect,
1722                 mMutable, mAllocator, mUnpremultipliedRequired,
1723                 mConserveMemory, mDecodeAsAlphaMask, getColorSpacePtr(),
1724                 checkForExtended());
1725     }
1726 
callHeaderDecoded(@ullable OnHeaderDecodedListener listener, @NonNull Source src)1727     private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
1728             @NonNull Source src) {
1729         if (listener != null) {
1730             ImageInfo info = new ImageInfo(this);
1731             try {
1732                 listener.onHeaderDecoded(this, info, src);
1733             } finally {
1734                 info.mDecoder = null;
1735             }
1736         }
1737     }
1738 
1739     /**
1740      *  Create a {@link Drawable} from a {@code Source}.
1741      *
1742      *  @param src representing the encoded image.
1743      *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
1744      *      default settings on the {@code ImageDecoder}. This will be called on
1745      *      the same thread as {@code decodeDrawable} before that method returns.
1746      *      This is required in order to change any of the default settings.
1747      *  @return Drawable for displaying the image.
1748      *  @throws IOException if {@code src} is not found, is an unsupported
1749      *      format, or cannot be decoded for any reason.
1750      */
1751     @WorkerThread
1752     @NonNull
decodeDrawable(@onNull Source src, @NonNull OnHeaderDecodedListener listener)1753     public static Drawable decodeDrawable(@NonNull Source src,
1754             @NonNull OnHeaderDecodedListener listener) throws IOException {
1755         if (listener == null) {
1756             throw new IllegalArgumentException("listener cannot be null! "
1757                     + "Use decodeDrawable(Source) to not have a listener");
1758         }
1759         return decodeDrawableImpl(src, listener);
1760     }
1761 
1762     @WorkerThread
1763     @NonNull
decodeDrawableImpl(@onNull Source src, @Nullable OnHeaderDecodedListener listener)1764     private static Drawable decodeDrawableImpl(@NonNull Source src,
1765             @Nullable OnHeaderDecodedListener listener) throws IOException {
1766         try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
1767             decoder.mSource = src;
1768             decoder.callHeaderDecoded(listener, src);
1769 
1770             if (decoder.mUnpremultipliedRequired) {
1771                 // Though this could be supported (ignored) for opaque images,
1772                 // it seems better to always report this error.
1773                 throw new IllegalStateException("Cannot decode a Drawable " +
1774                                                 "with unpremultiplied pixels!");
1775             }
1776 
1777             if (decoder.mMutable) {
1778                 throw new IllegalStateException("Cannot decode a mutable " +
1779                                                 "Drawable!");
1780             }
1781 
1782             // this call potentially manipulates the decoder so it must be performed prior to
1783             // decoding the bitmap and after decode set the density on the resulting bitmap
1784             final int srcDensity = decoder.computeDensity(src);
1785             if (decoder.mAnimated) {
1786                 // AnimatedImageDrawable calls postProcessAndRelease only if
1787                 // mPostProcessor exists.
1788                 ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
1789                         null : decoder;
1790                 decoder.checkState(true);
1791                 Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
1792                         postProcessPtr, decoder.mDesiredWidth,
1793                         decoder.mDesiredHeight, decoder.getColorSpacePtr(),
1794                         decoder.checkForExtended(), srcDensity,
1795                         src.computeDstDensity(), decoder.mCropRect,
1796                         decoder.mInputStream, decoder.mAssetFd);
1797                 // d has taken ownership of these objects.
1798                 decoder.mInputStream = null;
1799                 decoder.mAssetFd = null;
1800                 return d;
1801             }
1802 
1803             Bitmap bm = decoder.decodeBitmapInternal();
1804             bm.setDensity(srcDensity);
1805 
1806             Resources res = src.getResources();
1807             byte[] np = bm.getNinePatchChunk();
1808             if (np != null && NinePatch.isNinePatchChunk(np)) {
1809                 Rect opticalInsets = new Rect();
1810                 bm.getOpticalInsets(opticalInsets);
1811                 Rect padding = decoder.mOutPaddingRect;
1812                 if (padding == null) {
1813                     padding = new Rect();
1814                 }
1815                 nGetPadding(decoder.mNativePtr, padding);
1816                 return new NinePatchDrawable(res, bm, np, padding,
1817                         opticalInsets, null);
1818             }
1819 
1820             return new BitmapDrawable(res, bm);
1821         }
1822     }
1823 
1824     /**
1825      *  Create a {@link Drawable} from a {@code Source}.
1826      *
1827      *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
1828      *  the default settings will be used. In order to change any settings, call
1829      *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} instead.</p>
1830      *
1831      *  @param src representing the encoded image.
1832      *  @return Drawable for displaying the image.
1833      *  @throws IOException if {@code src} is not found, is an unsupported
1834      *      format, or cannot be decoded for any reason.
1835      */
1836     @WorkerThread
1837     @NonNull
decodeDrawable(@onNull Source src)1838     public static Drawable decodeDrawable(@NonNull Source src)
1839             throws IOException {
1840         return decodeDrawableImpl(src, null);
1841     }
1842 
1843     /**
1844      *  Create a {@link Bitmap} from a {@code Source}.
1845      *
1846      *  @param src representing the encoded image.
1847      *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
1848      *      default settings on the {@code ImageDecoder}. This will be called on
1849      *      the same thread as {@code decodeBitmap} before that method returns.
1850      *      This is required in order to change any of the default settings.
1851      *  @return Bitmap containing the image.
1852      *  @throws IOException if {@code src} is not found, is an unsupported
1853      *      format, or cannot be decoded for any reason.
1854      */
1855     @WorkerThread
1856     @NonNull
decodeBitmap(@onNull Source src, @NonNull OnHeaderDecodedListener listener)1857     public static Bitmap decodeBitmap(@NonNull Source src,
1858             @NonNull OnHeaderDecodedListener listener) throws IOException {
1859         if (listener == null) {
1860             throw new IllegalArgumentException("listener cannot be null! "
1861                     + "Use decodeBitmap(Source) to not have a listener");
1862         }
1863         return decodeBitmapImpl(src, listener);
1864     }
1865 
1866     @WorkerThread
1867     @NonNull
decodeBitmapImpl(@onNull Source src, @Nullable OnHeaderDecodedListener listener)1868     private static Bitmap decodeBitmapImpl(@NonNull Source src,
1869             @Nullable OnHeaderDecodedListener listener) throws IOException {
1870         try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
1871             decoder.mSource = src;
1872             decoder.callHeaderDecoded(listener, src);
1873 
1874             // this call potentially manipulates the decoder so it must be performed prior to
1875             // decoding the bitmap
1876             final int srcDensity = decoder.computeDensity(src);
1877             Bitmap bm = decoder.decodeBitmapInternal();
1878             bm.setDensity(srcDensity);
1879 
1880             Rect padding = decoder.mOutPaddingRect;
1881             if (padding != null) {
1882                 byte[] np = bm.getNinePatchChunk();
1883                 if (np != null && NinePatch.isNinePatchChunk(np)) {
1884                     nGetPadding(decoder.mNativePtr, padding);
1885                 }
1886             }
1887 
1888             return bm;
1889         }
1890     }
1891 
1892     // This method may modify the decoder so it must be called prior to performing the decode
computeDensity(@onNull Source src)1893     private int computeDensity(@NonNull Source src) {
1894         // if the caller changed the size then we treat the density as unknown
1895         if (this.requestedResize()) {
1896             return Bitmap.DENSITY_NONE;
1897         }
1898 
1899         final int srcDensity = src.getDensity();
1900         if (srcDensity == Bitmap.DENSITY_NONE) {
1901             return srcDensity;
1902         }
1903 
1904         // Scaling up nine-patch divs is imprecise and is better handled
1905         // at draw time. An app won't be relying on the internal Bitmap's
1906         // size, so it is safe to let NinePatchDrawable handle scaling.
1907         // mPostProcessor disables nine-patching, so behave normally if
1908         // it is present.
1909         if (mIsNinePatch && mPostProcessor == null) {
1910             return srcDensity;
1911         }
1912 
1913         // Special stuff for compatibility mode: if the target density is not
1914         // the same as the display density, but the resource -is- the same as
1915         // the display density, then don't scale it down to the target density.
1916         // This allows us to load the system's density-correct resources into
1917         // an application in compatibility mode, without scaling those down
1918         // to the compatibility density only to have them scaled back up when
1919         // drawn to the screen.
1920         Resources res = src.getResources();
1921         if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
1922             return srcDensity;
1923         }
1924 
1925         final int dstDensity = src.computeDstDensity();
1926         if (srcDensity == dstDensity) {
1927             return srcDensity;
1928         }
1929 
1930         // For P and above, only resize if it would be a downscale. Scale up prior
1931         // to P in case the app relies on the Bitmap's size without considering density.
1932         if (srcDensity < dstDensity
1933                 && Compatibility.getTargetSdkVersion() >= Build.VERSION_CODES.P) {
1934             return srcDensity;
1935         }
1936 
1937         float scale = (float) dstDensity / srcDensity;
1938         int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
1939         int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
1940         this.setTargetSize(scaledWidth, scaledHeight);
1941         return dstDensity;
1942     }
1943 
1944     @NonNull
getMimeType()1945     private String getMimeType() {
1946         return nGetMimeType(mNativePtr);
1947     }
1948 
1949     @Nullable
getColorSpace()1950     private ColorSpace getColorSpace() {
1951         return nGetColorSpace(mNativePtr);
1952     }
1953 
1954     /**
1955      *  Create a {@link Bitmap} from a {@code Source}.
1956      *
1957      *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
1958      *  the default settings will be used. In order to change any settings, call
1959      *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)} instead.</p>
1960      *
1961      *  @param src representing the encoded image.
1962      *  @return Bitmap containing the image.
1963      *  @throws IOException if {@code src} is not found, is an unsupported
1964      *      format, or cannot be decoded for any reason.
1965      */
1966     @WorkerThread
1967     @NonNull
decodeBitmap(@onNull Source src)1968     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
1969         return decodeBitmapImpl(src, null);
1970     }
1971 
1972     /**
1973      * Private method called by JNI.
1974      */
1975     @SuppressWarnings("unused")
postProcessAndRelease(@onNull Canvas canvas)1976     private int postProcessAndRelease(@NonNull Canvas canvas) {
1977         try {
1978             return mPostProcessor.onPostProcess(canvas);
1979         } finally {
1980             canvas.release();
1981         }
1982     }
1983 
1984     /**
1985      * Private method called by JNI.
1986      */
1987     @SuppressWarnings("unused")
onPartialImage(@ecodeException.Error int error, @Nullable Throwable cause)1988     private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause)
1989             throws DecodeException {
1990         DecodeException exception = new DecodeException(error, cause, mSource);
1991         if (mOnPartialImageListener == null
1992                 || !mOnPartialImageListener.onPartialImage(exception)) {
1993             throw exception;
1994         }
1995     }
1996 
nCreate(long asset, boolean preferAnimation, Source src)1997     private static native ImageDecoder nCreate(long asset,
1998             boolean preferAnimation, Source src) throws IOException;
nCreate(ByteBuffer buffer, int position, int limit, boolean preferAnimation, Source src)1999     private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,
2000             boolean preferAnimation, Source src) throws IOException;
nCreate(byte[] data, int offset, int length, boolean preferAnimation, Source src)2001     private static native ImageDecoder nCreate(byte[] data, int offset, int length,
2002             boolean preferAnimation, Source src) throws IOException;
nCreate(InputStream is, byte[] storage, boolean preferAnimation, Source src)2003     private static native ImageDecoder nCreate(InputStream is, byte[] storage,
2004             boolean preferAnimation, Source src) throws IOException;
2005     // The fd must be seekable.
nCreate(FileDescriptor fd, long length, boolean preferAnimation, Source src)2006     private static native ImageDecoder nCreate(FileDescriptor fd, long length,
2007             boolean preferAnimation, Source src) throws IOException;
2008     @NonNull
nDecodeBitmap(long nativePtr, @NonNull ImageDecoder decoder, boolean doPostProcess, int width, int height, @Nullable Rect cropRect, boolean mutable, int allocator, boolean unpremulRequired, boolean conserveMemory, boolean decodeAsAlphaMask, long desiredColorSpace, boolean extended)2009     private static native Bitmap nDecodeBitmap(long nativePtr,
2010             @NonNull ImageDecoder decoder,
2011             boolean doPostProcess,
2012             int width, int height,
2013             @Nullable Rect cropRect, boolean mutable,
2014             int allocator, boolean unpremulRequired,
2015             boolean conserveMemory, boolean decodeAsAlphaMask,
2016             long desiredColorSpace, boolean extended)
2017         throws IOException;
nGetSampledSize(long nativePtr, int sampleSize)2018     private static native Size nGetSampledSize(long nativePtr,
2019                                                int sampleSize);
nGetPadding(long nativePtr, @NonNull Rect outRect)2020     private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
nClose(long nativePtr)2021     private static native void nClose(long nativePtr);
nGetMimeType(long nativePtr)2022     private static native String nGetMimeType(long nativePtr);
nGetColorSpace(long nativePtr)2023     private static native ColorSpace nGetColorSpace(long nativePtr);
2024 }
2025