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.compat.annotation.UnsupportedAppUsage;
33 import android.content.ContentResolver;
34 import android.content.res.AssetFileDescriptor;
35 import android.content.res.AssetManager;
36 import android.content.res.AssetManager.AssetInputStream;
37 import android.content.res.Resources;
38 import android.graphics.drawable.AnimatedImageDrawable;
39 import android.graphics.drawable.BitmapDrawable;
40 import android.graphics.drawable.Drawable;
41 import android.graphics.drawable.NinePatchDrawable;
42 import android.net.Uri;
43 import android.os.Build;
44 import android.system.ErrnoException;
45 import android.system.Os;
46 import android.util.DisplayMetrics;
47 import android.util.Size;
48 import android.util.TypedValue;
49 
50 import dalvik.system.CloseGuard;
51 
52 import libcore.io.IoUtils;
53 
54 import java.io.File;
55 import java.io.FileDescriptor;
56 import java.io.FileInputStream;
57 import java.io.FileNotFoundException;
58 import java.io.IOException;
59 import java.io.InputStream;
60 import java.lang.annotation.Retention;
61 import java.nio.ByteBuffer;
62 import java.util.Locale;
63 import java.util.Objects;
64 import java.util.concurrent.Callable;
65 import java.util.concurrent.atomic.AtomicBoolean;
66 
67 /**
68  *  <p>A class for converting encoded images (like {@code PNG}, {@code JPEG},
69  *  {@code WEBP}, {@code GIF}, or {@code HEIF}) into {@link Drawable} or
70  *  {@link Bitmap} objects.
71  *
72  *  <p>To use it, first create a {@link Source Source} using one of the
73  *  {@code createSource} overloads. For example, to decode from a {@link File}, call
74  *  {@link #createSource(File)} and pass the result to {@link #decodeDrawable(Source)}
75  *  or {@link #decodeBitmap(Source)}:
76  *
77  *  <pre class="prettyprint">
78  *  File file = new File(...);
79  *  ImageDecoder.Source source = ImageDecoder.createSource(file);
80  *  Drawable drawable = ImageDecoder.decodeDrawable(source);
81  *  </pre>
82  *
83  *  <p>To change the default settings, pass the {@link Source Source} and an
84  *  {@link OnHeaderDecodedListener OnHeaderDecodedListener} to
85  *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} or
86  *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)}. For example, to
87  *  create a sampled image with half the width and height of the original image,
88  *  call {@link #setTargetSampleSize setTargetSampleSize(2)} inside
89  *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}:
90  *
91  *  <pre class="prettyprint">
92  *  OnHeaderDecodedListener listener = new OnHeaderDecodedListener() {
93  *      public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) {
94  *          decoder.setTargetSampleSize(2);
95  *      }
96  *  };
97  *  Drawable drawable = ImageDecoder.decodeDrawable(source, listener);
98  *  </pre>
99  *
100  *  <p>The {@link ImageInfo ImageInfo} contains information about the encoded image, like
101  *  its width and height, and the {@link Source Source} can be used to match to a particular
102  *  {@link Source Source} if a single {@link OnHeaderDecodedListener OnHeaderDecodedListener}
103  *  is used with multiple {@link Source Source} objects.
104  *
105  *  <p>The {@link OnHeaderDecodedListener OnHeaderDecodedListener} can also be implemented
106  *  as a lambda:
107  *
108  *  <pre class="prettyprint">
109  *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
110  *      decoder.setTargetSampleSize(2);
111  *  });
112  *  </pre>
113  *
114  *  <p>If the encoded image is an animated {@code GIF} or {@code WEBP},
115  *  {@link #decodeDrawable decodeDrawable} will return an {@link AnimatedImageDrawable}. To
116  *  start its animation, call {@link AnimatedImageDrawable#start AnimatedImageDrawable.start()}:
117  *
118  *  <pre class="prettyprint">
119  *  Drawable drawable = ImageDecoder.decodeDrawable(source);
120  *  if (drawable instanceof AnimatedImageDrawable) {
121  *      ((AnimatedImageDrawable) drawable).start();
122  *  }
123  *  </pre>
124  *
125  *  <p>By default, a {@link Bitmap} created by {@link ImageDecoder} (including
126  *  one that is inside a {@link Drawable}) will be immutable (i.e.
127  *  {@link Bitmap#isMutable Bitmap.isMutable()} returns {@code false}), and it
128  *  will typically have {@code Config} {@link Bitmap.Config#HARDWARE}. Although
129  *  these properties can be changed with {@link #setMutableRequired setMutableRequired(true)}
130  *  (which is only compatible with {@link #decodeBitmap(Source)} and
131  *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)}) and {@link #setAllocator},
132  *  it is also possible to apply custom effects regardless of the mutability of
133  *  the final returned object by passing a {@link PostProcessor} to
134  *  {@link #setPostProcessor setPostProcessor}. A {@link PostProcessor} can also be a lambda:
135  *
136  *  <pre class="prettyprint">
137  *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
138  *      decoder.setPostProcessor((canvas) -&gt; {
139  *              // This will create rounded corners.
140  *              Path path = new Path();
141  *              path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
142  *              int width = canvas.getWidth();
143  *              int height = canvas.getHeight();
144  *              path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
145  *              Paint paint = new Paint();
146  *              paint.setAntiAlias(true);
147  *              paint.setColor(Color.TRANSPARENT);
148  *              paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
149  *              canvas.drawPath(path, paint);
150  *              return PixelFormat.TRANSLUCENT;
151  *      });
152  *  });
153  *  </pre>
154  *
155  *  <p>If the encoded image is incomplete or contains an error, or if an
156  *  {@link Exception} occurs during decoding, a {@link DecodeException DecodeException}
157  *  will be thrown. In some cases, the {@link ImageDecoder} may have decoded part of
158  *  the image. In order to display the partial image, an
159  *  {@link OnPartialImageListener OnPartialImageListener} must be passed to
160  *  {@link #setOnPartialImageListener setOnPartialImageListener}. For example:
161  *
162  *  <pre class="prettyprint">
163  *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
164  *      decoder.setOnPartialImageListener((DecodeException e) -&gt; {
165  *              // Returning true indicates to create a Drawable or Bitmap even
166  *              // if the whole image could not be decoded. Any remaining lines
167  *              // will be blank.
168  *              return true;
169  *      });
170  *  });
171  *  </pre>
172  */
173 public final class ImageDecoder implements AutoCloseable {
174     /** @hide **/
175     public static int sApiLevel;
176 
177     /**
178      *  Source of encoded image data.
179      *
180      *  <p>References the data that will be used to decode a {@link Drawable}
181      *  or {@link Bitmap} in {@link #decodeDrawable decodeDrawable} or
182      *  {@link #decodeBitmap decodeBitmap}. Constructing a {@code Source} (with
183      *  one of the overloads of {@code createSource}) can be done on any thread
184      *  because the construction simply captures values. The real work is done
185      *  in {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}.
186      *
187      *  <p>A {@code Source} object can be reused to create multiple versions of the
188      *  same image. For example, to decode a full size image and its thumbnail,
189      *  the same {@code Source} can be used once with no
190      *  {@link OnHeaderDecodedListener OnHeaderDecodedListener} and once with an
191      *  implementation of {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}
192      *  that calls {@link #setTargetSize} with smaller dimensions. One {@code Source}
193      *  can even be used simultaneously in multiple threads.</p>
194      */
195     public static abstract class Source {
Source()196         private Source() {}
197 
198         /* @hide */
199         @Nullable
getResources()200         Resources getResources() { return null; }
201 
202         /* @hide */
getDensity()203         int getDensity() { return Bitmap.DENSITY_NONE; }
204 
205         /* @hide */
computeDstDensity()206         final int computeDstDensity() {
207             Resources res = getResources();
208             if (res == null) {
209                 return Bitmap.getDefaultDensity();
210             }
211 
212             return res.getDisplayMetrics().densityDpi;
213         }
214 
215         /* @hide */
216         @NonNull
createImageDecoder()217         abstract ImageDecoder createImageDecoder() throws IOException;
218     };
219 
220     private static class ByteArraySource extends Source {
ByteArraySource(@onNull byte[] data, int offset, int length)221         ByteArraySource(@NonNull byte[] data, int offset, int length) {
222             mData = data;
223             mOffset = offset;
224             mLength = length;
225         };
226         private final byte[] mData;
227         private final int    mOffset;
228         private final int    mLength;
229 
230         @Override
createImageDecoder()231         public ImageDecoder createImageDecoder() throws IOException {
232             return nCreate(mData, mOffset, mLength, this);
233         }
234     }
235 
236     private static class ByteBufferSource extends Source {
ByteBufferSource(@onNull ByteBuffer buffer)237         ByteBufferSource(@NonNull ByteBuffer buffer) {
238             mBuffer = buffer;
239         }
240         private final ByteBuffer mBuffer;
241 
242         @Override
createImageDecoder()243         public ImageDecoder createImageDecoder() throws IOException {
244             if (!mBuffer.isDirect() && mBuffer.hasArray()) {
245                 int offset = mBuffer.arrayOffset() + mBuffer.position();
246                 int length = mBuffer.limit() - mBuffer.position();
247                 return nCreate(mBuffer.array(), offset, length, this);
248             }
249             ByteBuffer buffer = mBuffer.slice();
250             return nCreate(buffer, buffer.position(), buffer.limit(), this);
251         }
252     }
253 
254     private static class ContentResolverSource extends Source {
ContentResolverSource(@onNull ContentResolver resolver, @NonNull Uri uri, @Nullable Resources res)255         ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri,
256                 @Nullable Resources res) {
257             mResolver = resolver;
258             mUri = uri;
259             mResources = res;
260         }
261 
262         private final ContentResolver mResolver;
263         private final Uri mUri;
264         private final Resources mResources;
265 
266         @Nullable
getResources()267         Resources getResources() { return mResources; }
268 
269         @Override
createImageDecoder()270         public ImageDecoder createImageDecoder() throws IOException {
271             AssetFileDescriptor assetFd = null;
272             try {
273                 if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
274                     assetFd = mResolver.openTypedAssetFileDescriptor(mUri,
275                             "image/*", null);
276                 } else {
277                     assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
278                 }
279             } catch (FileNotFoundException e) {
280                 // Some images cannot be opened as AssetFileDescriptors (e.g.
281                 // bmp, ico). Open them as InputStreams.
282                 InputStream is = mResolver.openInputStream(mUri);
283                 if (is == null) {
284                     throw new FileNotFoundException(mUri.toString());
285                 }
286 
287                 return createFromStream(is, true, this);
288             }
289             return createFromAssetFileDescriptor(assetFd, this);
290         }
291     }
292 
293     @NonNull
createFromFile(@onNull File file, @NonNull Source source)294     private static ImageDecoder createFromFile(@NonNull File file,
295             @NonNull Source source) throws IOException {
296         FileInputStream stream = new FileInputStream(file);
297         FileDescriptor fd = stream.getFD();
298         try {
299             Os.lseek(fd, 0, SEEK_CUR);
300         } catch (ErrnoException e) {
301             return createFromStream(stream, true, source);
302         }
303 
304         ImageDecoder decoder = null;
305         try {
306             decoder = nCreate(fd, source);
307         } finally {
308             if (decoder == null) {
309                 IoUtils.closeQuietly(stream);
310             } else {
311                 decoder.mInputStream = stream;
312                 decoder.mOwnsInputStream = true;
313             }
314         }
315         return decoder;
316     }
317 
318     @NonNull
createFromStream(@onNull InputStream is, boolean closeInputStream, Source source)319     private static ImageDecoder createFromStream(@NonNull InputStream is,
320             boolean closeInputStream, Source source) throws IOException {
321         // Arbitrary size matches BitmapFactory.
322         byte[] storage = new byte[16 * 1024];
323         ImageDecoder decoder = null;
324         try {
325             decoder = nCreate(is, storage, source);
326         } finally {
327             if (decoder == null) {
328                 if (closeInputStream) {
329                     IoUtils.closeQuietly(is);
330                 }
331             } else {
332                 decoder.mInputStream = is;
333                 decoder.mOwnsInputStream = closeInputStream;
334                 decoder.mTempStorage = storage;
335             }
336         }
337 
338         return decoder;
339     }
340 
341     @NonNull
createFromAssetFileDescriptor(@onNull AssetFileDescriptor assetFd, Source source)342     private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
343             Source source) throws IOException {
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, source);
352             } catch (ErrnoException e) {
353                 decoder = createFromStream(new FileInputStream(fd), true, 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()391         public ImageDecoder createImageDecoder() 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, 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()437         public ImageDecoder createImageDecoder() 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, 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()472         public ImageDecoder createImageDecoder() 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, this);
487         }
488     }
489 
490     /**
491      *  ImageDecoder will own the AssetInputStream.
492      */
createFromAsset(AssetInputStream ais, Source source)493     private static ImageDecoder createFromAsset(AssetInputStream ais,
494             Source source) throws IOException {
495         ImageDecoder decoder = null;
496         try {
497             long asset = ais.getNativeAsset();
498             decoder = nCreate(asset, 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()520         public ImageDecoder createImageDecoder() throws IOException {
521             InputStream is = mAssets.open(mFileName);
522             return createFromAsset((AssetInputStream) is, 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()534         public ImageDecoder createImageDecoder() throws IOException {
535             return createFromFile(mFile, 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()547         public ImageDecoder createImageDecoder() 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, 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      * @param data byte array of compressed image data.
940      * @param offset offset into data for where the decoder should begin
941      *      parsing.
942      * @param length number of bytes, beginning at offset, to parse.
943      * @return a new Source object, which can be passed to
944      *      {@link #decodeDrawable decodeDrawable} or
945      *      {@link #decodeBitmap decodeBitmap}.
946      * @throws NullPointerException if data is null.
947      * @throws ArrayIndexOutOfBoundsException if offset and length are
948      *      not within data.
949      * @hide
950      */
951     @AnyThread
952     @NonNull
createSource(@onNull byte[] data, int offset, int length)953     public static Source createSource(@NonNull byte[] data, int offset,
954             int length) throws ArrayIndexOutOfBoundsException {
955         if (data == null) {
956             throw new NullPointerException("null byte[] in createSource!");
957         }
958         if (offset < 0 || length < 0 || offset >= data.length ||
959                 offset + length > data.length) {
960             throw new ArrayIndexOutOfBoundsException(
961                     "invalid offset/length!");
962         }
963         return new ByteArraySource(data, offset, length);
964     }
965 
966     /**
967      * See {@link #createSource(byte[], int, int).
968      * @hide
969      */
970     @AnyThread
971     @NonNull
createSource(@onNull byte[] data)972     public static Source createSource(@NonNull byte[] data) {
973         return createSource(data, 0, data.length);
974     }
975 
976     /**
977      * Create a new {@link Source Source} from a {@link java.nio.ByteBuffer}.
978      *
979      * <p>Decoding will start from {@link java.nio.ByteBuffer#position() buffer.position()}.
980      * The position of {@code buffer} will not be affected.</p>
981      *
982      * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
983      * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
984      * will continue reading from the {@code buffer}, so its contents must not
985      * be modified, even after the {@code AnimatedImageDrawable} is returned.
986      * {@code buffer}'s contents should never be modified during decode.</p>
987      *
988      * @return a new Source object, which can be passed to
989      *      {@link #decodeDrawable decodeDrawable} or
990      *      {@link #decodeBitmap decodeBitmap}.
991      */
992     @AnyThread
993     @NonNull
createSource(@onNull ByteBuffer buffer)994     public static Source createSource(@NonNull ByteBuffer buffer) {
995         return new ByteBufferSource(buffer);
996     }
997 
998     /**
999      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
1000      *
1001      * <p>Unlike other Sources, this one cannot be reused.</p>
1002      *
1003      * @hide
1004      */
1005     @AnyThread
1006     @NonNull
createSource(Resources res, InputStream is)1007     public static Source createSource(Resources res, InputStream is) {
1008         return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
1009     }
1010 
1011     /**
1012      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
1013      *
1014      * <p>Unlike other Sources, this one cannot be reused.</p>
1015      *
1016      * @hide
1017      */
1018     @AnyThread
1019     @TestApi
1020     @NonNull
createSource(Resources res, InputStream is, int density)1021     public static Source createSource(Resources res, InputStream is, int density) {
1022         return new InputStreamSource(res, is, density);
1023     }
1024 
1025     /**
1026      * Create a new {@link Source Source} from a {@link java.io.File}.
1027      *
1028      * @return a new Source object, which can be passed to
1029      *      {@link #decodeDrawable decodeDrawable} or
1030      *      {@link #decodeBitmap decodeBitmap}.
1031      */
1032     @AnyThread
1033     @NonNull
createSource(@onNull File file)1034     public static Source createSource(@NonNull File file) {
1035         return new FileSource(file);
1036     }
1037 
1038     /**
1039      * Create a new {@link Source Source} from a {@link Callable} that returns a
1040      * new {@link AssetFileDescriptor} for each request. This provides control
1041      * over how the {@link AssetFileDescriptor} is created, such as passing
1042      * options into {@link ContentResolver#openTypedAssetFileDescriptor}, or
1043      * enabling use of a {@link android.os.CancellationSignal}.
1044      * <p>
1045      * It's important for the given {@link Callable} to return a new, unique
1046      * {@link AssetFileDescriptor} for each invocation, to support reuse of the
1047      * returned {@link Source Source}.
1048      *
1049      * @return a new Source object, which can be passed to
1050      *         {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap
1051      *         decodeBitmap}.
1052      */
1053     @AnyThread
1054     @NonNull
createSource(@onNull Callable<AssetFileDescriptor> callable)1055     public static Source createSource(@NonNull Callable<AssetFileDescriptor> callable) {
1056         return new CallableSource(callable);
1057     }
1058 
1059     /**
1060      *  Return the width and height of a given sample size.
1061      *
1062      *  <p>This takes an input that functions like
1063      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
1064      *  height that can be achieved by sampling the encoded image. Other widths
1065      *  and heights may be supported, but will require an additional (internal)
1066      *  scaling step. Such internal scaling is *not* supported with
1067      *  {@link #setUnpremultipliedRequired} set to {@code true}.</p>
1068      *
1069      *  @param sampleSize Sampling rate of the encoded image.
1070      *  @return {@link android.util.Size} of the width and height after
1071      *      sampling.
1072      *
1073      *  @hide
1074      */
1075     @NonNull
getSampledSize(int sampleSize)1076     public Size getSampledSize(int sampleSize) {
1077         if (sampleSize <= 0) {
1078             throw new IllegalArgumentException("sampleSize must be positive! "
1079                     + "provided " + sampleSize);
1080         }
1081         if (mNativePtr == 0) {
1082             throw new IllegalStateException("ImageDecoder is closed!");
1083         }
1084 
1085         return nGetSampledSize(mNativePtr, sampleSize);
1086     }
1087 
1088     // Modifiers
1089     /** @removed
1090      * @deprecated Renamed to {@link #setTargetSize}.
1091      */
1092     @Deprecated
setResize(int width, int height)1093     public ImageDecoder setResize(int width, int height) {
1094         this.setTargetSize(width, height);
1095         return this;
1096     }
1097 
1098     /**
1099      *  Specify the size of the output {@link Drawable} or {@link Bitmap}.
1100      *
1101      *  <p>By default, the output size will match the size of the encoded
1102      *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1103      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1104      *
1105      *  <p>This will sample or scale the output to an arbitrary size that may
1106      *  be smaller or larger than the encoded size.</p>
1107      *
1108      *  <p>Only the last call to this or {@link #setTargetSampleSize} is
1109      *  respected.</p>
1110      *
1111      *  <p>Like all setters on ImageDecoder, this must be called inside
1112      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1113      *
1114      *  @param width width in pixels of the output, must be greater than 0
1115      *  @param height height in pixels of the output, must be greater than 0
1116      */
setTargetSize(@x @ntRangefrom = 1) int width, @Px @IntRange(from = 1) int height)1117     public void setTargetSize(@Px @IntRange(from = 1) int width,
1118                               @Px @IntRange(from = 1) int height) {
1119         if (width <= 0 || height <= 0) {
1120             throw new IllegalArgumentException("Dimensions must be positive! "
1121                     + "provided (" + width + ", " + height + ")");
1122         }
1123 
1124         mDesiredWidth = width;
1125         mDesiredHeight = height;
1126     }
1127 
1128     /** @removed
1129      * @deprecated Renamed to {@link #setTargetSampleSize}.
1130      */
1131     @Deprecated
setResize(int sampleSize)1132     public ImageDecoder setResize(int sampleSize) {
1133         this.setTargetSampleSize(sampleSize);
1134         return this;
1135     }
1136 
getTargetDimension(int original, int sampleSize, int computed)1137     private int getTargetDimension(int original, int sampleSize, int computed) {
1138         // Sampling will never result in a smaller size than 1.
1139         if (sampleSize >= original) {
1140             return 1;
1141         }
1142 
1143         // Use integer divide to find the desired size. If that is what
1144         // getSampledSize computed, that is the size to use.
1145         int target = original / sampleSize;
1146         if (computed == target) {
1147             return computed;
1148         }
1149 
1150         // If sampleSize does not divide evenly into original, the decoder
1151         // may round in either direction. It just needs to get a result that
1152         // is close.
1153         int reverse = computed * sampleSize;
1154         if (Math.abs(reverse - original) < sampleSize) {
1155             // This is the size that can be decoded most efficiently.
1156             return computed;
1157         }
1158 
1159         // The decoder could not get close (e.g. it is a DNG image).
1160         return target;
1161     }
1162 
1163     /**
1164      *  Set the target size with a sampleSize.
1165      *
1166      *  <p>By default, the output size will match the size of the encoded
1167      *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1168      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1169      *
1170      *  <p>Requests the decoder to subsample the original image, returning a
1171      *  smaller image to save memory. The {@code sampleSize} is the number of pixels
1172      *  in either dimension that correspond to a single pixel in the output.
1173      *  For example, {@code sampleSize == 4} returns an image that is 1/4 the
1174      *  width/height of the original, and 1/16 the number of pixels.</p>
1175      *
1176      *  <p>Must be greater than or equal to 1.</p>
1177      *
1178      *  <p>This has the same effect as calling {@link #setTargetSize} with
1179      *  dimensions based on the {@code sampleSize}. Unlike dividing the original
1180      *  width and height by the {@code sampleSize} manually, calling this method
1181      *  allows {@code ImageDecoder} to round in the direction that it can do most
1182      *  efficiently.</p>
1183      *
1184      *  <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
1185      *
1186      *  <p>Like all setters on ImageDecoder, this must be called inside
1187      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1188      *
1189      *  @param sampleSize sampling rate of the encoded image.
1190      */
setTargetSampleSize(@ntRangefrom = 1) int sampleSize)1191     public void setTargetSampleSize(@IntRange(from = 1) int sampleSize) {
1192         Size size = this.getSampledSize(sampleSize);
1193         int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
1194         int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
1195         this.setTargetSize(targetWidth, targetHeight);
1196     }
1197 
requestedResize()1198     private boolean requestedResize() {
1199         return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
1200     }
1201 
1202     // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
1203     /**
1204      *  Use the default allocation for the pixel memory.
1205      *
1206      *  Will typically result in a {@link Bitmap.Config#HARDWARE}
1207      *  allocation, but may be software for small images. In addition, this will
1208      *  switch to software when HARDWARE is incompatible, e.g.
1209      *  {@link #setMutableRequired setMutableRequired(true)} or
1210      *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)}.
1211      */
1212     public static final int ALLOCATOR_DEFAULT = 0;
1213 
1214     /**
1215      *  Use a software allocation for the pixel memory.
1216      *
1217      *  <p>Useful for drawing to a software {@link Canvas} or for
1218      *  accessing the pixels on the final output.
1219      */
1220     public static final int ALLOCATOR_SOFTWARE = 1;
1221 
1222     /**
1223      *  Use shared memory for the pixel memory.
1224      *
1225      *  <p>Useful for sharing across processes.
1226      */
1227     public static final int ALLOCATOR_SHARED_MEMORY = 2;
1228 
1229     /**
1230      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
1231      *
1232      *  <p>When this is combined with incompatible options, like
1233      *  {@link #setMutableRequired setMutableRequired(true)} or
1234      *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)},
1235      *  {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}
1236      *  will throw an {@link java.lang.IllegalStateException}.
1237      */
1238     public static final int ALLOCATOR_HARDWARE = 3;
1239 
1240     /** @hide **/
1241     @Retention(SOURCE)
1242     @IntDef(value = { ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE,
1243               ALLOCATOR_SHARED_MEMORY, ALLOCATOR_HARDWARE },
1244               prefix = {"ALLOCATOR_"})
1245     public @interface Allocator {};
1246 
1247     /**
1248      *  Choose the backing for the pixel memory.
1249      *
1250      *  <p>This is ignored for animated drawables.</p>
1251      *
1252      *  <p>Like all setters on ImageDecoder, this must be called inside
1253      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1254      *
1255      *  @param allocator Type of allocator to use.
1256      */
setAllocator(@llocator int allocator)1257     public void setAllocator(@Allocator int allocator) {
1258         if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
1259             throw new IllegalArgumentException("invalid allocator " + allocator);
1260         }
1261         mAllocator = allocator;
1262     }
1263 
1264     /**
1265      *  Return the allocator for the pixel memory.
1266      */
1267     @Allocator
getAllocator()1268     public int getAllocator() {
1269         return mAllocator;
1270     }
1271 
1272     /**
1273      *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
1274      *
1275      *  <p>By default, ImageDecoder will create a {@link Bitmap} with
1276      *  premultiplied pixels, which is required for drawing with the
1277      *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
1278      *  this method with a value of {@code true} will result in
1279      *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
1280      *  pixels. See {@link Bitmap#isPremultiplied Bitmap.isPremultiplied()}.
1281      *  This is incompatible with {@link #decodeDrawable decodeDrawable};
1282      *  attempting to decode an unpremultiplied {@link Drawable} will throw an
1283      *  {@link java.lang.IllegalStateException}. </p>
1284      *
1285      *  <p>Like all setters on ImageDecoder, this must be called inside
1286      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1287      */
setUnpremultipliedRequired(boolean unpremultipliedRequired)1288     public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
1289         mUnpremultipliedRequired = unpremultipliedRequired;
1290     }
1291 
1292     /** @removed
1293      * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
1294      */
1295     @Deprecated
setRequireUnpremultiplied(boolean unpremultipliedRequired)1296     public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
1297         this.setUnpremultipliedRequired(unpremultipliedRequired);
1298         return this;
1299     }
1300 
1301     /**
1302      *  Return whether the {@link Bitmap} will have unpremultiplied pixels.
1303      */
isUnpremultipliedRequired()1304     public boolean isUnpremultipliedRequired() {
1305         return mUnpremultipliedRequired;
1306     }
1307 
1308     /** @removed
1309      * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
1310      */
1311     @Deprecated
getRequireUnpremultiplied()1312     public boolean getRequireUnpremultiplied() {
1313         return this.isUnpremultipliedRequired();
1314     }
1315 
1316     /**
1317      *  Modify the image after decoding and scaling.
1318      *
1319      *  <p>This allows adding effects prior to returning a {@link Drawable} or
1320      *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
1321      *  this is the only way to process the image after decoding.</p>
1322      *
1323      *  <p>If combined with {@link #setTargetSize} and/or {@link #setCrop},
1324      *  {@link PostProcessor#onPostProcess} occurs last.</p>
1325      *
1326      *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
1327      *
1328      *  <p>For an animated image, the drawing commands drawn on the
1329      *  {@link Canvas} will be recorded immediately and then applied to each
1330      *  frame.</p>
1331      *
1332      *  <p>Like all setters on ImageDecoder, this must be called inside
1333      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1334      *
1335      */
setPostProcessor(@ullable PostProcessor postProcessor)1336     public void setPostProcessor(@Nullable PostProcessor postProcessor) {
1337         mPostProcessor = postProcessor;
1338     }
1339 
1340     /**
1341      *  Return the {@link PostProcessor} currently set.
1342      */
1343     @Nullable
getPostProcessor()1344     public PostProcessor getPostProcessor() {
1345         return mPostProcessor;
1346     }
1347 
1348     /**
1349      *  Set (replace) the {@link OnPartialImageListener} on this object.
1350      *
1351      *  <p>Will be called if there is an error in the input. Without one, an
1352      *  error will result in an {@code Exception} being thrown.</p>
1353      *
1354      *  <p>Like all setters on ImageDecoder, this must be called inside
1355      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1356      *
1357      */
setOnPartialImageListener(@ullable OnPartialImageListener listener)1358     public void setOnPartialImageListener(@Nullable OnPartialImageListener listener) {
1359         mOnPartialImageListener = listener;
1360     }
1361 
1362     /**
1363      *  Return the {@link OnPartialImageListener OnPartialImageListener} currently set.
1364      */
1365     @Nullable
getOnPartialImageListener()1366     public OnPartialImageListener getOnPartialImageListener() {
1367         return mOnPartialImageListener;
1368     }
1369 
1370     /**
1371      *  Crop the output to {@code subset} of the (possibly) scaled image.
1372      *
1373      *  <p>{@code subset} must be contained within the size set by
1374      *  {@link #setTargetSize} or the bounds of the image if setTargetSize was
1375      *  not called. Otherwise an {@link IllegalStateException} will be thrown by
1376      *  {@link #decodeDrawable decodeDrawable}/{@link #decodeBitmap decodeBitmap}.</p>
1377      *
1378      *  <p>NOT intended as a replacement for
1379      *  {@link BitmapRegionDecoder#decodeRegion BitmapRegionDecoder.decodeRegion()}.
1380      *  This supports all formats, but merely crops the output.</p>
1381      *
1382      *  <p>Like all setters on ImageDecoder, this must be called inside
1383      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1384      *
1385      */
setCrop(@ullable Rect subset)1386     public void setCrop(@Nullable Rect subset) {
1387         mCropRect = subset;
1388     }
1389 
1390     /**
1391      *  Return the cropping rectangle, if set.
1392      */
1393     @Nullable
getCrop()1394     public Rect getCrop() {
1395         return mCropRect;
1396     }
1397 
1398     /**
1399      *  Set a Rect for retrieving nine patch padding.
1400      *
1401      *  If the image is a nine patch, this Rect will be set to the padding
1402      *  rectangle during decode. Otherwise it will not be modified.
1403      *
1404      *  <p>Like all setters on ImageDecoder, this must be called inside
1405      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1406      *
1407      *  @hide
1408      */
setOutPaddingRect(@onNull Rect outPadding)1409     public void setOutPaddingRect(@NonNull Rect outPadding) {
1410         mOutPaddingRect = outPadding;
1411     }
1412 
1413     /**
1414      *  Specify whether the {@link Bitmap} should be mutable.
1415      *
1416      *  <p>By default, a {@link Bitmap} created by {@link #decodeBitmap decodeBitmap}
1417      *  will be immutable i.e. {@link Bitmap#isMutable() Bitmap.isMutable()} returns
1418      *  {@code false}. This can be changed with {@code setMutableRequired(true)}.
1419      *
1420      *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
1421      *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
1422      *  Attempting to combine them will throw an
1423      *  {@link java.lang.IllegalStateException}.</p>
1424      *
1425      *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable decodeDrawable},
1426      *  which would require retrieving the Bitmap from the returned Drawable in
1427      *  order to modify. Attempting to decode a mutable {@link Drawable} will
1428      *  throw an {@link java.lang.IllegalStateException}.</p>
1429      *
1430      *  <p>Like all setters on ImageDecoder, this must be called inside
1431      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1432      */
setMutableRequired(boolean mutable)1433     public void setMutableRequired(boolean mutable) {
1434         mMutable = mutable;
1435     }
1436 
1437     /** @removed
1438      * @deprecated Renamed to {@link #setMutableRequired}.
1439      */
1440     @Deprecated
setMutable(boolean mutable)1441     public ImageDecoder setMutable(boolean mutable) {
1442         this.setMutableRequired(mutable);
1443         return this;
1444     }
1445 
1446     /**
1447      *  Return whether the decoded {@link Bitmap} will be mutable.
1448      */
isMutableRequired()1449     public boolean isMutableRequired() {
1450         return mMutable;
1451     }
1452 
1453     /** @removed
1454      * @deprecated Renamed to {@link #isMutableRequired}.
1455      */
1456     @Deprecated
getMutable()1457     public boolean getMutable() {
1458         return this.isMutableRequired();
1459     }
1460 
1461     /**
1462      * Save memory if possible by using a denser {@link Bitmap.Config} at the
1463      * cost of some image quality.
1464      *
1465      * <p>For example an opaque 8-bit image may be compressed into an
1466      * {@link Bitmap.Config#RGB_565} configuration, sacrificing image
1467      * quality to save memory.
1468      */
1469     public static final int MEMORY_POLICY_LOW_RAM = 0;
1470 
1471     /**
1472      * Use the most natural {@link Bitmap.Config} for the internal {@link Bitmap}.
1473      *
1474      * <p>This is the recommended default for most applications and usages. This
1475      * will use the closest {@link Bitmap.Config} for the encoded source. If the
1476      * encoded source does not exactly match any {@link Bitmap.Config}, the next
1477      * highest quality {@link Bitmap.Config} will be used avoiding any loss in
1478      * image quality.
1479      */
1480     public static final int MEMORY_POLICY_DEFAULT  = 1;
1481 
1482     /** @hide **/
1483     @Retention(SOURCE)
1484     @IntDef(value = { MEMORY_POLICY_DEFAULT, MEMORY_POLICY_LOW_RAM },
1485               prefix = {"MEMORY_POLICY_"})
1486     public @interface MemoryPolicy {};
1487 
1488     /**
1489      *  Specify the memory policy for the decoded {@link Bitmap}.
1490      *
1491      *  <p>Like all setters on ImageDecoder, this must be called inside
1492      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1493      */
setMemorySizePolicy(@emoryPolicy int policy)1494     public void setMemorySizePolicy(@MemoryPolicy int policy) {
1495         mConserveMemory = (policy == MEMORY_POLICY_LOW_RAM);
1496     }
1497 
1498     /**
1499      *  Retrieve the memory policy for the decoded {@link Bitmap}.
1500      */
1501     @MemoryPolicy
getMemorySizePolicy()1502     public int getMemorySizePolicy() {
1503         return mConserveMemory ? MEMORY_POLICY_LOW_RAM : MEMORY_POLICY_DEFAULT;
1504     }
1505 
1506     /** @removed
1507      * @deprecated Replaced by {@link #setMemorySizePolicy}.
1508      */
1509     @Deprecated
setConserveMemory(boolean conserveMemory)1510     public void setConserveMemory(boolean conserveMemory) {
1511         mConserveMemory = conserveMemory;
1512     }
1513 
1514     /** @removed
1515      * @deprecated Replaced by {@link #getMemorySizePolicy}.
1516      */
1517     @Deprecated
getConserveMemory()1518     public boolean getConserveMemory() {
1519         return mConserveMemory;
1520     }
1521 
1522     /**
1523      *  Specify whether to potentially treat the output as an alpha mask.
1524      *
1525      *  <p>If this is set to {@code true} and the image is encoded in a format
1526      *  with only one channel, treat that channel as alpha. Otherwise this call has
1527      *  no effect.</p>
1528      *
1529      *  <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
1530      *  combine them will result in {@link #decodeDrawable decodeDrawable}/
1531      *  {@link #decodeBitmap decodeBitmap} throwing an
1532      *  {@link java.lang.IllegalStateException}.</p>
1533      *
1534      *  <p>Like all setters on ImageDecoder, this must be called inside
1535      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1536      */
setDecodeAsAlphaMaskEnabled(boolean enabled)1537     public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
1538         mDecodeAsAlphaMask = enabled;
1539     }
1540 
1541     /** @removed
1542      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1543      */
1544     @Deprecated
setDecodeAsAlphaMask(boolean enabled)1545     public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
1546         this.setDecodeAsAlphaMaskEnabled(enabled);
1547         return this;
1548     }
1549 
1550     /** @removed
1551      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1552      */
1553     @Deprecated
setAsAlphaMask(boolean asAlphaMask)1554     public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
1555         this.setDecodeAsAlphaMask(asAlphaMask);
1556         return this;
1557     }
1558 
1559     /**
1560      *  Return whether to treat single channel input as alpha.
1561      *
1562      *  <p>This returns whether {@link #setDecodeAsAlphaMaskEnabled} was set to
1563      *  {@code true}. It may still return {@code true} even if the image has
1564      *  more than one channel and therefore will not be treated as an alpha
1565      *  mask.</p>
1566      */
isDecodeAsAlphaMaskEnabled()1567     public boolean isDecodeAsAlphaMaskEnabled() {
1568         return mDecodeAsAlphaMask;
1569     }
1570 
1571     /** @removed
1572      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1573      */
1574     @Deprecated
getDecodeAsAlphaMask()1575     public boolean getDecodeAsAlphaMask() {
1576         return mDecodeAsAlphaMask;
1577     }
1578 
1579     /** @removed
1580      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1581      */
1582     @Deprecated
getAsAlphaMask()1583     public boolean getAsAlphaMask() {
1584         return this.getDecodeAsAlphaMask();
1585     }
1586 
1587     /**
1588      * Specify the desired {@link ColorSpace} for the output.
1589      *
1590      * <p>If non-null, the decoder will try to decode into {@code colorSpace}.
1591      * If it is null, which is the default, or the request cannot be met, the
1592      * decoder will pick either the color space embedded in the image or the
1593      * {@link ColorSpace} best suited for the requested image configuration
1594      * (for instance {@link ColorSpace.Named#SRGB sRGB} for the
1595      * {@link Bitmap.Config#ARGB_8888} configuration and
1596      * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
1597      * {@link Bitmap.Config#RGBA_F16}).</p>
1598      *
1599      * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
1600      * currently supported. An <code>IllegalArgumentException</code> will
1601      * be thrown by {@link #decodeDrawable decodeDrawable}/
1602      * {@link #decodeBitmap decodeBitmap} when setting a non-RGB color space
1603      * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
1604      *
1605      * <p class="note">The specified color space's transfer function must be
1606      * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
1607      * <code>IllegalArgumentException</code> will be thrown by the decode methods
1608      * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
1609      * specified color space returns null.</p>
1610      *
1611      * <p>Like all setters on ImageDecoder, this must be called inside
1612      * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1613      */
setTargetColorSpace(ColorSpace colorSpace)1614     public void setTargetColorSpace(ColorSpace colorSpace) {
1615         mDesiredColorSpace = colorSpace;
1616     }
1617 
1618     /**
1619      * Closes this resource, relinquishing any underlying resources. This method
1620      * is invoked automatically on objects managed by the try-with-resources
1621      * statement.
1622      *
1623      * <p>This is an implementation detail of {@link ImageDecoder}, and should
1624      * never be called manually.</p>
1625      */
1626     @Override
close()1627     public void close() {
1628         mCloseGuard.close();
1629         if (!mClosed.compareAndSet(false, true)) {
1630             return;
1631         }
1632         nClose(mNativePtr);
1633         mNativePtr = 0;
1634 
1635         if (mOwnsInputStream) {
1636             IoUtils.closeQuietly(mInputStream);
1637         }
1638         IoUtils.closeQuietly(mAssetFd);
1639 
1640         mInputStream = null;
1641         mAssetFd = null;
1642         mTempStorage = null;
1643     }
1644 
checkState(boolean animated)1645     private void checkState(boolean animated) {
1646         if (mNativePtr == 0) {
1647             throw new IllegalStateException("Cannot use closed ImageDecoder!");
1648         }
1649 
1650         checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
1651 
1652         // animated ignores the allocator, so no need to check for incompatible
1653         // fields.
1654         if (!animated && mAllocator == ALLOCATOR_HARDWARE) {
1655             if (mMutable) {
1656                 throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
1657             }
1658             if (mDecodeAsAlphaMask) {
1659                 throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
1660             }
1661         }
1662 
1663         if (mPostProcessor != null && mUnpremultipliedRequired) {
1664             throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
1665         }
1666     }
1667 
checkSubset(int width, int height, Rect r)1668     private static void checkSubset(int width, int height, Rect r) {
1669         if (r == null) {
1670             return;
1671         }
1672         if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
1673             throw new IllegalStateException("Subset " + r + " not contained by "
1674                     + "scaled image bounds: (" + width + " x " + height + ")");
1675         }
1676     }
1677 
checkForExtended()1678     private boolean checkForExtended() {
1679         if (mDesiredColorSpace == null) {
1680             return false;
1681         }
1682         return mDesiredColorSpace == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
1683                 || mDesiredColorSpace == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
1684     }
1685 
getColorSpacePtr()1686     private long getColorSpacePtr() {
1687         if (mDesiredColorSpace == null) {
1688             return 0;
1689         }
1690         return mDesiredColorSpace.getNativeInstance();
1691     }
1692 
1693     @WorkerThread
1694     @NonNull
decodeBitmapInternal()1695     private Bitmap decodeBitmapInternal() throws IOException {
1696         checkState(false);
1697         return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
1698                 mDesiredWidth, mDesiredHeight, mCropRect,
1699                 mMutable, mAllocator, mUnpremultipliedRequired,
1700                 mConserveMemory, mDecodeAsAlphaMask, getColorSpacePtr(),
1701                 checkForExtended());
1702     }
1703 
callHeaderDecoded(@ullable OnHeaderDecodedListener listener, @NonNull Source src)1704     private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
1705             @NonNull Source src) {
1706         if (listener != null) {
1707             ImageInfo info = new ImageInfo(this);
1708             try {
1709                 listener.onHeaderDecoded(this, info, src);
1710             } finally {
1711                 info.mDecoder = null;
1712             }
1713         }
1714     }
1715 
1716     /**
1717      *  Create a {@link Drawable} from a {@code Source}.
1718      *
1719      *  @param src representing the encoded image.
1720      *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
1721      *      default settings on the {@code ImageDecoder}. This will be called on
1722      *      the same thread as {@code decodeDrawable} before that method returns.
1723      *      This is required in order to change any of the default settings.
1724      *  @return Drawable for displaying the image.
1725      *  @throws IOException if {@code src} is not found, is an unsupported
1726      *      format, or cannot be decoded for any reason.
1727      */
1728     @WorkerThread
1729     @NonNull
decodeDrawable(@onNull Source src, @NonNull OnHeaderDecodedListener listener)1730     public static Drawable decodeDrawable(@NonNull Source src,
1731             @NonNull OnHeaderDecodedListener listener) throws IOException {
1732         if (listener == null) {
1733             throw new IllegalArgumentException("listener cannot be null! "
1734                     + "Use decodeDrawable(Source) to not have a listener");
1735         }
1736         return decodeDrawableImpl(src, listener);
1737     }
1738 
1739     @WorkerThread
1740     @NonNull
decodeDrawableImpl(@onNull Source src, @Nullable OnHeaderDecodedListener listener)1741     private static Drawable decodeDrawableImpl(@NonNull Source src,
1742             @Nullable OnHeaderDecodedListener listener) throws IOException {
1743         try (ImageDecoder decoder = src.createImageDecoder()) {
1744             decoder.mSource = src;
1745             decoder.callHeaderDecoded(listener, src);
1746 
1747             if (decoder.mUnpremultipliedRequired) {
1748                 // Though this could be supported (ignored) for opaque images,
1749                 // it seems better to always report this error.
1750                 throw new IllegalStateException("Cannot decode a Drawable " +
1751                                                 "with unpremultiplied pixels!");
1752             }
1753 
1754             if (decoder.mMutable) {
1755                 throw new IllegalStateException("Cannot decode a mutable " +
1756                                                 "Drawable!");
1757             }
1758 
1759             // this call potentially manipulates the decoder so it must be performed prior to
1760             // decoding the bitmap and after decode set the density on the resulting bitmap
1761             final int srcDensity = decoder.computeDensity(src);
1762             if (decoder.mAnimated) {
1763                 // AnimatedImageDrawable calls postProcessAndRelease only if
1764                 // mPostProcessor exists.
1765                 ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
1766                         null : decoder;
1767                 decoder.checkState(true);
1768                 Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
1769                         postProcessPtr, decoder.mDesiredWidth,
1770                         decoder.mDesiredHeight, decoder.getColorSpacePtr(),
1771                         decoder.checkForExtended(), srcDensity,
1772                         src.computeDstDensity(), decoder.mCropRect,
1773                         decoder.mInputStream, decoder.mAssetFd);
1774                 // d has taken ownership of these objects.
1775                 decoder.mInputStream = null;
1776                 decoder.mAssetFd = null;
1777                 return d;
1778             }
1779 
1780             Bitmap bm = decoder.decodeBitmapInternal();
1781             bm.setDensity(srcDensity);
1782 
1783             Resources res = src.getResources();
1784             byte[] np = bm.getNinePatchChunk();
1785             if (np != null && NinePatch.isNinePatchChunk(np)) {
1786                 Rect opticalInsets = new Rect();
1787                 bm.getOpticalInsets(opticalInsets);
1788                 Rect padding = decoder.mOutPaddingRect;
1789                 if (padding == null) {
1790                     padding = new Rect();
1791                 }
1792                 nGetPadding(decoder.mNativePtr, padding);
1793                 return new NinePatchDrawable(res, bm, np, padding,
1794                         opticalInsets, null);
1795             }
1796 
1797             return new BitmapDrawable(res, bm);
1798         }
1799     }
1800 
1801     /**
1802      *  Create a {@link Drawable} from a {@code Source}.
1803      *
1804      *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
1805      *  the default settings will be used. In order to change any settings, call
1806      *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} instead.</p>
1807      *
1808      *  @param src representing the encoded image.
1809      *  @return Drawable for displaying the image.
1810      *  @throws IOException if {@code src} is not found, is an unsupported
1811      *      format, or cannot be decoded for any reason.
1812      */
1813     @WorkerThread
1814     @NonNull
decodeDrawable(@onNull Source src)1815     public static Drawable decodeDrawable(@NonNull Source src)
1816             throws IOException {
1817         return decodeDrawableImpl(src, null);
1818     }
1819 
1820     /**
1821      *  Create a {@link Bitmap} from a {@code Source}.
1822      *
1823      *  @param src representing the encoded image.
1824      *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
1825      *      default settings on the {@code ImageDecoder}. This will be called on
1826      *      the same thread as {@code decodeBitmap} before that method returns.
1827      *      This is required in order to change any of the default settings.
1828      *  @return Bitmap containing the image.
1829      *  @throws IOException if {@code src} is not found, is an unsupported
1830      *      format, or cannot be decoded for any reason.
1831      */
1832     @WorkerThread
1833     @NonNull
decodeBitmap(@onNull Source src, @NonNull OnHeaderDecodedListener listener)1834     public static Bitmap decodeBitmap(@NonNull Source src,
1835             @NonNull OnHeaderDecodedListener listener) throws IOException {
1836         if (listener == null) {
1837             throw new IllegalArgumentException("listener cannot be null! "
1838                     + "Use decodeBitmap(Source) to not have a listener");
1839         }
1840         return decodeBitmapImpl(src, listener);
1841     }
1842 
1843     @WorkerThread
1844     @NonNull
decodeBitmapImpl(@onNull Source src, @Nullable OnHeaderDecodedListener listener)1845     private static Bitmap decodeBitmapImpl(@NonNull Source src,
1846             @Nullable OnHeaderDecodedListener listener) throws IOException {
1847         try (ImageDecoder decoder = src.createImageDecoder()) {
1848             decoder.mSource = src;
1849             decoder.callHeaderDecoded(listener, src);
1850 
1851             // this call potentially manipulates the decoder so it must be performed prior to
1852             // decoding the bitmap
1853             final int srcDensity = decoder.computeDensity(src);
1854             Bitmap bm = decoder.decodeBitmapInternal();
1855             bm.setDensity(srcDensity);
1856 
1857             Rect padding = decoder.mOutPaddingRect;
1858             if (padding != null) {
1859                 byte[] np = bm.getNinePatchChunk();
1860                 if (np != null && NinePatch.isNinePatchChunk(np)) {
1861                     nGetPadding(decoder.mNativePtr, padding);
1862                 }
1863             }
1864 
1865             return bm;
1866         }
1867     }
1868 
1869     // This method may modify the decoder so it must be called prior to performing the decode
computeDensity(@onNull Source src)1870     private int computeDensity(@NonNull Source src) {
1871         // if the caller changed the size then we treat the density as unknown
1872         if (this.requestedResize()) {
1873             return Bitmap.DENSITY_NONE;
1874         }
1875 
1876         final int srcDensity = src.getDensity();
1877         if (srcDensity == Bitmap.DENSITY_NONE) {
1878             return srcDensity;
1879         }
1880 
1881         // Scaling up nine-patch divs is imprecise and is better handled
1882         // at draw time. An app won't be relying on the internal Bitmap's
1883         // size, so it is safe to let NinePatchDrawable handle scaling.
1884         // mPostProcessor disables nine-patching, so behave normally if
1885         // it is present.
1886         if (mIsNinePatch && mPostProcessor == null) {
1887             return srcDensity;
1888         }
1889 
1890         // Special stuff for compatibility mode: if the target density is not
1891         // the same as the display density, but the resource -is- the same as
1892         // the display density, then don't scale it down to the target density.
1893         // This allows us to load the system's density-correct resources into
1894         // an application in compatibility mode, without scaling those down
1895         // to the compatibility density only to have them scaled back up when
1896         // drawn to the screen.
1897         Resources res = src.getResources();
1898         if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
1899             return srcDensity;
1900         }
1901 
1902         final int dstDensity = src.computeDstDensity();
1903         if (srcDensity == dstDensity) {
1904             return srcDensity;
1905         }
1906 
1907         // For P and above, only resize if it would be a downscale. Scale up prior
1908         // to P in case the app relies on the Bitmap's size without considering density.
1909         if (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P) {
1910             return srcDensity;
1911         }
1912 
1913         float scale = (float) dstDensity / srcDensity;
1914         int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
1915         int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
1916         this.setTargetSize(scaledWidth, scaledHeight);
1917         return dstDensity;
1918     }
1919 
1920     @NonNull
getMimeType()1921     private String getMimeType() {
1922         return nGetMimeType(mNativePtr);
1923     }
1924 
1925     @Nullable
getColorSpace()1926     private ColorSpace getColorSpace() {
1927         return nGetColorSpace(mNativePtr);
1928     }
1929 
1930     /**
1931      *  Create a {@link Bitmap} from a {@code Source}.
1932      *
1933      *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
1934      *  the default settings will be used. In order to change any settings, call
1935      *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)} instead.</p>
1936      *
1937      *  @param src representing the encoded image.
1938      *  @return Bitmap containing the image.
1939      *  @throws IOException if {@code src} is not found, is an unsupported
1940      *      format, or cannot be decoded for any reason.
1941      */
1942     @WorkerThread
1943     @NonNull
decodeBitmap(@onNull Source src)1944     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
1945         return decodeBitmapImpl(src, null);
1946     }
1947 
1948     /**
1949      * Private method called by JNI.
1950      */
1951     @SuppressWarnings("unused")
1952     @UnsupportedAppUsage
postProcessAndRelease(@onNull Canvas canvas)1953     private int postProcessAndRelease(@NonNull Canvas canvas) {
1954         try {
1955             return mPostProcessor.onPostProcess(canvas);
1956         } finally {
1957             canvas.release();
1958         }
1959     }
1960 
1961     /**
1962      * Private method called by JNI.
1963      */
1964     @SuppressWarnings("unused")
onPartialImage(@ecodeException.Error int error, @Nullable Throwable cause)1965     private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause)
1966             throws DecodeException {
1967         DecodeException exception = new DecodeException(error, cause, mSource);
1968         if (mOnPartialImageListener == null
1969                 || !mOnPartialImageListener.onPartialImage(exception)) {
1970             throw exception;
1971         }
1972     }
1973 
nCreate(long asset, Source src)1974     private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
nCreate(ByteBuffer buffer, int position, int limit, Source src)1975     private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
1976                                                int limit, Source src) throws IOException;
nCreate(byte[] data, int offset, int length, Source src)1977     private static native ImageDecoder nCreate(byte[] data, int offset, int length,
1978                                                Source src) throws IOException;
nCreate(InputStream is, byte[] storage, Source src)1979     private static native ImageDecoder nCreate(InputStream is, byte[] storage,
1980                                                Source src) throws IOException;
1981     // The fd must be seekable.
nCreate(FileDescriptor fd, Source src)1982     private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
1983     @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)1984     private static native Bitmap nDecodeBitmap(long nativePtr,
1985             @NonNull ImageDecoder decoder,
1986             boolean doPostProcess,
1987             int width, int height,
1988             @Nullable Rect cropRect, boolean mutable,
1989             int allocator, boolean unpremulRequired,
1990             boolean conserveMemory, boolean decodeAsAlphaMask,
1991             long desiredColorSpace, boolean extended)
1992         throws IOException;
nGetSampledSize(long nativePtr, int sampleSize)1993     private static native Size nGetSampledSize(long nativePtr,
1994                                                int sampleSize);
nGetPadding(long nativePtr, @NonNull Rect outRect)1995     private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
nClose(long nativePtr)1996     private static native void nClose(long nativePtr);
nGetMimeType(long nativePtr)1997     private static native String nGetMimeType(long nativePtr);
nGetColorSpace(long nativePtr)1998     private static native ColorSpace nGetColorSpace(long nativePtr);
1999 }
2000