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