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     /** @removed
685      * @deprecated Subsumed by {@link #DecodeException}.
686      */
687     @Deprecated
688     public static class IncompleteException extends IOException {};
689 
690     /**
691      *  Interface for changing the default settings of a decode.
692      *
693      *  <p>Supply an instance to
694      *  {@link #decodeDrawable(Source, OnHeaderDecodedListener) decodeDrawable}
695      *  or {@link #decodeBitmap(Source, OnHeaderDecodedListener) decodeBitmap},
696      *  which will call {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}
697      *  (in the same thread) once the size is known. The implementation of
698      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded} can then
699      *  change the decode settings as desired.
700      */
701     public static interface OnHeaderDecodedListener {
702         /**
703          *  Called by {@link ImageDecoder} when the header has been decoded and
704          *  the image size is known.
705          *
706          *  @param decoder the object performing the decode, for changing
707          *      its default settings.
708          *  @param info information about the encoded image.
709          *  @param source object that created {@code decoder}.
710          */
onHeaderDecoded(@onNull ImageDecoder decoder, @NonNull ImageInfo info, @NonNull Source source)711         public void onHeaderDecoded(@NonNull ImageDecoder decoder,
712                 @NonNull ImageInfo info, @NonNull Source source);
713 
714     };
715 
716     /** @removed
717      * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
718      */
719     @Deprecated
720     public static final int ERROR_SOURCE_EXCEPTION  = 1;
721 
722     /** @removed
723      * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
724      */
725     @Deprecated
726     public static final int ERROR_SOURCE_INCOMPLETE = 2;
727 
728     /** @removed
729      * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
730      */
731     @Deprecated
732     public static final int ERROR_SOURCE_ERROR      = 3;
733 
734     /**
735      *  Information about an interrupted decode.
736      */
737     public static final class DecodeException extends IOException {
738         /**
739          *  An Exception was thrown reading the {@link Source}.
740          */
741         public static final int SOURCE_EXCEPTION  = 1;
742 
743         /**
744          *  The encoded data was incomplete.
745          */
746         public static final int SOURCE_INCOMPLETE = 2;
747 
748         /**
749          *  The encoded data contained an error.
750          */
751         public static final int SOURCE_MALFORMED_DATA      = 3;
752 
753         /** @hide **/
754         @Retention(SOURCE)
755         @IntDef(value = { SOURCE_EXCEPTION, SOURCE_INCOMPLETE, SOURCE_MALFORMED_DATA },
756                 prefix = {"SOURCE_"})
757         public @interface Error {};
758 
759         @Error final int mError;
760         @NonNull final Source mSource;
761 
DecodeException(@rror int error, @Nullable Throwable cause, @NonNull Source source)762         DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
763             super(errorMessage(error, cause), cause);
764             mError = error;
765             mSource = source;
766         }
767 
768         /**
769          * Private method called by JNI.
770          */
771         @SuppressWarnings("unused")
DecodeException(@rror int error, @Nullable String msg, @Nullable Throwable cause, @NonNull Source source)772         DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
773                 @NonNull Source source) {
774             super(msg + errorMessage(error, cause), cause);
775             mError = error;
776             mSource = source;
777         }
778 
779         /**
780          *  Retrieve the reason that decoding was interrupted.
781          *
782          *  <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
783          *  {@link java.lang.Throwable} can be retrieved with
784          *  {@link java.lang.Throwable#getCause}.</p>
785          */
786         @Error
getError()787         public int getError() {
788             return mError;
789         }
790 
791         /**
792          *  Retrieve the {@link Source Source} that was interrupted.
793          *
794          *  <p>This can be used for equality checking to find the Source which
795          *  failed to completely decode.</p>
796          */
797         @NonNull
getSource()798         public Source getSource() {
799             return mSource;
800         }
801 
errorMessage(@rror int error, @Nullable Throwable cause)802         private static String errorMessage(@Error int error, @Nullable Throwable cause) {
803             switch (error) {
804                 case SOURCE_EXCEPTION:
805                     return "Exception in input: " + cause;
806                 case SOURCE_INCOMPLETE:
807                     return "Input was incomplete.";
808                 case SOURCE_MALFORMED_DATA:
809                     return "Input contained an error.";
810                 default:
811                     return "";
812             }
813         }
814     }
815 
816     /**
817      *  Interface for inspecting a {@link DecodeException DecodeException}
818      *  and potentially preventing it from being thrown.
819      *
820      *  <p>If an instance is passed to
821      *  {@link #setOnPartialImageListener setOnPartialImageListener}, a
822      *  {@link DecodeException DecodeException} that would otherwise have been
823      *  thrown can be inspected inside
824      *  {@link OnPartialImageListener#onPartialImage onPartialImage}.
825      *  If {@link OnPartialImageListener#onPartialImage onPartialImage} returns
826      *  {@code true}, a partial image will be created.
827      */
828     public static interface OnPartialImageListener {
829         /**
830          *  Called by {@link ImageDecoder} when there is only a partial image to
831          *  display.
832          *
833          *  <p>If decoding is interrupted after having decoded a partial image,
834          *  this method will be called. The implementation can inspect the
835          *  {@link DecodeException DecodeException} and optionally finish the
836          *  rest of the decode creation process to create a partial {@link Drawable}
837          *  or {@link Bitmap}.
838          *
839          *  @param exception exception containing information about the
840          *      decode interruption.
841          *  @return {@code true} to create and return a {@link Drawable} or
842          *      {@link Bitmap} with partial data. {@code false} (which is the
843          *      default) to abort the decode and throw {@code e}. Any undecoded
844          *      lines in the image will be blank.
845          */
onPartialImage(@onNull DecodeException exception)846         boolean onPartialImage(@NonNull DecodeException exception);
847     };
848 
849     // Fields
850     private long          mNativePtr;
851     private final int     mWidth;
852     private final int     mHeight;
853     private final boolean mAnimated;
854     private final boolean mIsNinePatch;
855 
856     private int        mDesiredWidth;
857     private int        mDesiredHeight;
858     private int        mAllocator = ALLOCATOR_DEFAULT;
859     private boolean    mUnpremultipliedRequired = false;
860     private boolean    mMutable = false;
861     private boolean    mConserveMemory = false;
862     private boolean    mDecodeAsAlphaMask = false;
863     private ColorSpace mDesiredColorSpace = null;
864     private Rect       mCropRect;
865     private Rect       mOutPaddingRect;
866     private Source     mSource;
867 
868     private PostProcessor          mPostProcessor;
869     private OnPartialImageListener mOnPartialImageListener;
870 
871     // Objects for interacting with the input.
872     private InputStream         mInputStream;
873     private boolean             mOwnsInputStream;
874     private byte[]              mTempStorage;
875     private AssetFileDescriptor mAssetFd;
876     private final AtomicBoolean mClosed = new AtomicBoolean();
877     private final CloseGuard    mCloseGuard = CloseGuard.get();
878 
879     /**
880      * Private constructor called by JNI. {@link #close} must be
881      * called after decoding to delete native resources.
882      */
883     @SuppressWarnings("unused")
ImageDecoder(long nativePtr, int width, int height, boolean animated, boolean isNinePatch)884     private ImageDecoder(long nativePtr, int width, int height,
885             boolean animated, boolean isNinePatch) {
886         mNativePtr = nativePtr;
887         mWidth = width;
888         mHeight = height;
889         mDesiredWidth = width;
890         mDesiredHeight = height;
891         mAnimated = animated;
892         mIsNinePatch = isNinePatch;
893         mCloseGuard.open("close");
894     }
895 
896     @Override
finalize()897     protected void finalize() throws Throwable {
898         try {
899             if (mCloseGuard != null) {
900                 mCloseGuard.warnIfOpen();
901             }
902 
903             // Avoid closing these in finalizer.
904             mInputStream = null;
905             mAssetFd = null;
906 
907             close();
908         } finally {
909             super.finalize();
910         }
911     }
912 
913     /**
914      * Return if the given MIME type is a supported file format that can be
915      * decoded by this class. This can be useful to determine if a file can be
916      * decoded directly, or if it needs to be converted into a more general
917      * format using an API like {@link ContentResolver#openTypedAssetFile}.
918      */
isMimeTypeSupported(@onNull String mimeType)919     public static boolean isMimeTypeSupported(@NonNull String mimeType) {
920         Objects.requireNonNull(mimeType);
921         switch (mimeType.toLowerCase(Locale.US)) {
922             case "image/png":
923             case "image/jpeg":
924             case "image/webp":
925             case "image/gif":
926             case "image/bmp":
927             case "image/x-ico":
928             case "image/vnd.wap.wbmp":
929             case "image/x-sony-arw":
930             case "image/x-canon-cr2":
931             case "image/x-adobe-dng":
932             case "image/x-nikon-nef":
933             case "image/x-nikon-nrw":
934             case "image/x-olympus-orf":
935             case "image/x-fuji-raf":
936             case "image/x-panasonic-rw2":
937             case "image/x-pentax-pef":
938             case "image/x-samsung-srw":
939                 return true;
940             case "image/heif":
941             case "image/heic":
942                 return isHevcDecoderSupported();
943             case "image/avif":
944                 return isP010SupportedForAV1();
945             default:
946                 return false;
947         }
948     }
949 
950     /**
951      * Create a new {@link Source Source} from a resource.
952      *
953      * @param res the {@link Resources} object containing the image data.
954      * @param resId resource ID of the image data.
955      * @return a new Source object, which can be passed to
956      *      {@link #decodeDrawable decodeDrawable} or
957      *      {@link #decodeBitmap decodeBitmap}.
958      */
959     @AnyThread
960     @NonNull
createSource(@onNull Resources res, int resId)961     public static Source createSource(@NonNull Resources res, int resId)
962     {
963         return new ResourceSource(res, resId);
964     }
965 
966     /**
967      * Create a new {@link Source Source} from a {@link android.net.Uri}.
968      *
969      * <h5>Accepts the following URI schemes:</h5>
970      * <ul>
971      * <li>content ({@link ContentResolver#SCHEME_CONTENT})</li>
972      * <li>android.resource ({@link ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
973      * <li>file ({@link ContentResolver#SCHEME_FILE})</li>
974      * </ul>
975      *
976      * @param cr to retrieve from.
977      * @param uri of the image file.
978      * @return a new Source object, which can be passed to
979      *      {@link #decodeDrawable decodeDrawable} or
980      *      {@link #decodeBitmap decodeBitmap}.
981      */
982     @AnyThread
983     @NonNull
createSource(@onNull ContentResolver cr, @NonNull Uri uri)984     public static Source createSource(@NonNull ContentResolver cr,
985             @NonNull Uri uri) {
986         return new ContentResolverSource(cr, uri, null);
987     }
988 
989     /**
990      * Provide Resources for density scaling.
991      *
992      * @hide
993      */
994     @AnyThread
995     @NonNull
createSource(@onNull ContentResolver cr, @NonNull Uri uri, @Nullable Resources res)996     public static Source createSource(@NonNull ContentResolver cr,
997             @NonNull Uri uri, @Nullable Resources res) {
998         return new ContentResolverSource(cr, uri, res);
999     }
1000 
1001     /**
1002      * Create a new {@link Source Source} from a file in the "assets" directory.
1003      */
1004     @AnyThread
1005     @NonNull
createSource(@onNull AssetManager assets, @NonNull String fileName)1006     public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) {
1007         return new AssetSource(assets, fileName);
1008     }
1009 
1010     /**
1011      * Create a new {@link Source Source} from a byte array.
1012      *
1013      * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
1014      * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
1015      * will continue reading from {@code data}, so its contents must not
1016      * be modified, even after the {@code AnimatedImageDrawable} is returned.
1017      * {@code data}'s contents should never be modified during decode.</p>
1018      *
1019      * @param data byte array of compressed image data.
1020      * @param offset offset into data for where the decoder should begin
1021      *      parsing.
1022      * @param length number of bytes, beginning at offset, to parse.
1023      * @return a new Source object, which can be passed to
1024      *      {@link #decodeDrawable decodeDrawable} or
1025      *      {@link #decodeBitmap decodeBitmap}.
1026      * @throws NullPointerException if data is null.
1027      * @throws ArrayIndexOutOfBoundsException if offset and length are
1028      *      not within data.
1029      */
1030     @AnyThread
1031     @NonNull
createSource(@onNull byte[] data, int offset, int length)1032     public static Source createSource(@NonNull byte[] data, int offset,
1033             int length) throws ArrayIndexOutOfBoundsException {
1034         if (data == null) {
1035             throw new NullPointerException("null byte[] in createSource!");
1036         }
1037         if (offset < 0 || length < 0 || offset >= data.length ||
1038                 offset + length > data.length) {
1039             throw new ArrayIndexOutOfBoundsException(
1040                     "invalid offset/length!");
1041         }
1042         return new ByteArraySource(data, offset, length);
1043     }
1044 
1045     /**
1046      * Create a new {@link Source Source} from a byte array.
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 {@code data}, so its contents must not
1051      * be modified, even after the {@code AnimatedImageDrawable} is returned.
1052      * {@code data}'s contents should never be modified during decode.</p>
1053      *
1054      * @param data byte array of compressed image data.
1055      * @return a new Source object, which can be passed to
1056      *      {@link #decodeDrawable decodeDrawable} or
1057      *      {@link #decodeBitmap decodeBitmap}.
1058      * @throws NullPointerException if data is null.
1059      */
1060     @AnyThread
1061     @NonNull
createSource(@onNull byte[] data)1062     public static Source createSource(@NonNull byte[] data) {
1063         return createSource(data, 0, data.length);
1064     }
1065 
1066     /**
1067      * Create a new {@link Source Source} from a {@link java.nio.ByteBuffer}.
1068      *
1069      * <p>Decoding will start from {@link java.nio.ByteBuffer#position() buffer.position()}.
1070      * The position of {@code buffer} will not be affected.</p>
1071      *
1072      * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
1073      * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
1074      * will continue reading from the {@code buffer}, so its contents must not
1075      * be modified, even after the {@code AnimatedImageDrawable} is returned.
1076      * {@code buffer}'s contents should never be modified during decode.</p>
1077      *
1078      * @return a new Source object, which can be passed to
1079      *      {@link #decodeDrawable decodeDrawable} or
1080      *      {@link #decodeBitmap decodeBitmap}.
1081      */
1082     @AnyThread
1083     @NonNull
createSource(@onNull ByteBuffer buffer)1084     public static Source createSource(@NonNull ByteBuffer buffer) {
1085         return new ByteBufferSource(buffer);
1086     }
1087 
1088     /**
1089      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
1090      *
1091      * <p>Unlike other Sources, this one cannot be reused.</p>
1092      *
1093      * @hide
1094      */
1095     @AnyThread
1096     @NonNull
createSource(Resources res, @NonNull InputStream is)1097     public static Source createSource(Resources res, @NonNull InputStream is) {
1098         return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
1099     }
1100 
1101     /**
1102      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
1103      *
1104      * <p>Unlike other Sources, this one cannot be reused.</p>
1105      *
1106      * @hide
1107      */
1108     @AnyThread
1109     @TestApi
1110     @NonNull
createSource(Resources res, @NonNull InputStream is, int density)1111     public static Source createSource(Resources res, @NonNull InputStream is, int density) {
1112         return new InputStreamSource(res, is, density);
1113     }
1114 
1115     /**
1116      * Create a new {@link Source Source} from a {@link java.io.File}.
1117      * <p>
1118      * This method should only be used for files that you have direct access to;
1119      * if you'd like to work with files hosted outside your app, use an API like
1120      * {@link #createSource(Callable)} or
1121      * {@link #createSource(ContentResolver, Uri)}.
1122      * @return a new Source object, which can be passed to
1123      *      {@link #decodeDrawable decodeDrawable} or
1124      *      {@link #decodeBitmap decodeBitmap}.
1125      */
1126     @AnyThread
1127     @NonNull
createSource(@onNull File file)1128     public static Source createSource(@NonNull File file) {
1129         return new FileSource(file);
1130     }
1131 
1132     /**
1133      * Create a new {@link Source Source} from a {@link Callable} that returns a
1134      * new {@link AssetFileDescriptor} for each request. This provides control
1135      * over how the {@link AssetFileDescriptor} is created, such as passing
1136      * options into {@link ContentResolver#openTypedAssetFileDescriptor}, or
1137      * enabling use of a {@link android.os.CancellationSignal}.
1138      * <p>
1139      * It's important for the given {@link Callable} to return a new, unique
1140      * {@link AssetFileDescriptor} for each invocation, to support reuse of the
1141      * returned {@link Source Source}.
1142      *
1143      * @return a new Source object, which can be passed to
1144      *         {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap
1145      *         decodeBitmap}.
1146      */
1147     @AnyThread
1148     @NonNull
createSource(@onNull Callable<AssetFileDescriptor> callable)1149     public static Source createSource(@NonNull Callable<AssetFileDescriptor> callable) {
1150         return new CallableSource(callable);
1151     }
1152 
1153     /**
1154      *  Return the width and height of a given sample size.
1155      *
1156      *  <p>This takes an input that functions like
1157      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
1158      *  height that can be achieved by sampling the encoded image. Other widths
1159      *  and heights may be supported, but will require an additional (internal)
1160      *  scaling step. Such internal scaling is *not* supported with
1161      *  {@link #setUnpremultipliedRequired} set to {@code true}.</p>
1162      *
1163      *  @param sampleSize Sampling rate of the encoded image.
1164      *  @return {@link android.util.Size} of the width and height after
1165      *      sampling.
1166      */
1167     @NonNull
getSampledSize(int sampleSize)1168     private Size getSampledSize(int sampleSize) {
1169         if (sampleSize <= 0) {
1170             throw new IllegalArgumentException("sampleSize must be positive! "
1171                     + "provided " + sampleSize);
1172         }
1173         if (mNativePtr == 0) {
1174             throw new IllegalStateException("ImageDecoder is closed!");
1175         }
1176 
1177         return nGetSampledSize(mNativePtr, sampleSize);
1178     }
1179 
1180     // Modifiers
1181     /** @removed
1182      * @deprecated Renamed to {@link #setTargetSize}.
1183      */
1184     @Deprecated
setResize(int width, int height)1185     public ImageDecoder setResize(int width, int height) {
1186         this.setTargetSize(width, height);
1187         return this;
1188     }
1189 
1190     /**
1191      *  Specify the size of the output {@link Drawable} or {@link Bitmap}.
1192      *
1193      *  <p>By default, the output size will match the size of the encoded
1194      *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1195      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1196      *
1197      *  <p>This will sample or scale the output to an arbitrary size that may
1198      *  be smaller or larger than the encoded size.</p>
1199      *
1200      *  <p>Only the last call to this or {@link #setTargetSampleSize} is
1201      *  respected.</p>
1202      *
1203      *  <p>Like all setters on ImageDecoder, this must be called inside
1204      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1205      *
1206      *  @param width width in pixels of the output, must be greater than 0
1207      *  @param height height in pixels of the output, must be greater than 0
1208      */
setTargetSize(@x @ntRangefrom = 1) int width, @Px @IntRange(from = 1) int height)1209     public void setTargetSize(@Px @IntRange(from = 1) int width,
1210                               @Px @IntRange(from = 1) int height) {
1211         if (width <= 0 || height <= 0) {
1212             throw new IllegalArgumentException("Dimensions must be positive! "
1213                     + "provided (" + width + ", " + height + ")");
1214         }
1215 
1216         mDesiredWidth = width;
1217         mDesiredHeight = height;
1218     }
1219 
1220     /** @removed
1221      * @deprecated Renamed to {@link #setTargetSampleSize}.
1222      */
1223     @Deprecated
setResize(int sampleSize)1224     public ImageDecoder setResize(int sampleSize) {
1225         this.setTargetSampleSize(sampleSize);
1226         return this;
1227     }
1228 
getTargetDimension(int original, int sampleSize, int computed)1229     private int getTargetDimension(int original, int sampleSize, int computed) {
1230         // Sampling will never result in a smaller size than 1.
1231         if (sampleSize >= original) {
1232             return 1;
1233         }
1234 
1235         // Use integer divide to find the desired size. If that is what
1236         // getSampledSize computed, that is the size to use.
1237         int target = original / sampleSize;
1238         if (computed == target) {
1239             return computed;
1240         }
1241 
1242         // If sampleSize does not divide evenly into original, the decoder
1243         // may round in either direction. It just needs to get a result that
1244         // is close.
1245         int reverse = computed * sampleSize;
1246         if (Math.abs(reverse - original) < sampleSize) {
1247             // This is the size that can be decoded most efficiently.
1248             return computed;
1249         }
1250 
1251         // The decoder could not get close (e.g. it is a DNG image).
1252         return target;
1253     }
1254 
1255     /**
1256      *  Set the target size with a sampleSize.
1257      *
1258      *  <p>By default, the output size will match the size of the encoded
1259      *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1260      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1261      *
1262      *  <p>Requests the decoder to subsample the original image, returning a
1263      *  smaller image to save memory. The {@code sampleSize} is the number of pixels
1264      *  in either dimension that correspond to a single pixel in the output.
1265      *  For example, {@code sampleSize == 4} returns an image that is 1/4 the
1266      *  width/height of the original, and 1/16 the number of pixels.</p>
1267      *
1268      *  <p>Must be greater than or equal to 1.</p>
1269      *
1270      *  <p>This has the same effect as calling {@link #setTargetSize} with
1271      *  dimensions based on the {@code sampleSize}. Unlike dividing the original
1272      *  width and height by the {@code sampleSize} manually, calling this method
1273      *  allows {@code ImageDecoder} to round in the direction that it can do most
1274      *  efficiently.</p>
1275      *
1276      *  <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
1277      *
1278      *  <p>Like all setters on ImageDecoder, this must be called inside
1279      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1280      *
1281      *  @param sampleSize sampling rate of the encoded image.
1282      */
setTargetSampleSize(@ntRangefrom = 1) int sampleSize)1283     public void setTargetSampleSize(@IntRange(from = 1) int sampleSize) {
1284         Size size = this.getSampledSize(sampleSize);
1285         int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
1286         int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
1287         this.setTargetSize(targetWidth, targetHeight);
1288     }
1289 
requestedResize()1290     private boolean requestedResize() {
1291         return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
1292     }
1293 
1294     // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
1295     /**
1296      *  Use the default allocation for the pixel memory.
1297      *
1298      *  Will typically result in a {@link Bitmap.Config#HARDWARE}
1299      *  allocation, but may be software for small images. In addition, this will
1300      *  switch to software when HARDWARE is incompatible, e.g.
1301      *  {@link #setMutableRequired setMutableRequired(true)} or
1302      *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)}.
1303      */
1304     public static final int ALLOCATOR_DEFAULT = 0;
1305 
1306     /**
1307      *  Use a software allocation for the pixel memory.
1308      *
1309      *  <p>Useful for drawing to a software {@link Canvas} or for
1310      *  accessing the pixels on the final output.
1311      */
1312     public static final int ALLOCATOR_SOFTWARE = 1;
1313 
1314     /**
1315      *  Use shared memory for the pixel memory.
1316      *
1317      *  <p>Useful for sharing across processes.
1318      */
1319     public static final int ALLOCATOR_SHARED_MEMORY = 2;
1320 
1321     /**
1322      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
1323      *
1324      *  <p>When this is combined with incompatible options, like
1325      *  {@link #setMutableRequired setMutableRequired(true)} or
1326      *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)},
1327      *  {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}
1328      *  will throw an {@link java.lang.IllegalStateException}.
1329      */
1330     public static final int ALLOCATOR_HARDWARE = 3;
1331 
1332     /** @hide **/
1333     @Retention(SOURCE)
1334     @IntDef(value = { ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE,
1335               ALLOCATOR_SHARED_MEMORY, ALLOCATOR_HARDWARE },
1336               prefix = {"ALLOCATOR_"})
1337     public @interface Allocator {};
1338 
1339     /**
1340      *  Choose the backing for the pixel memory.
1341      *
1342      *  <p>This is ignored for animated drawables.</p>
1343      *
1344      *  <p>Like all setters on ImageDecoder, this must be called inside
1345      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1346      *
1347      *  @param allocator Type of allocator to use.
1348      */
setAllocator(@llocator int allocator)1349     public void setAllocator(@Allocator int allocator) {
1350         if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
1351             throw new IllegalArgumentException("invalid allocator " + allocator);
1352         }
1353         mAllocator = allocator;
1354     }
1355 
1356     /**
1357      *  Return the allocator for the pixel memory.
1358      */
1359     @Allocator
getAllocator()1360     public int getAllocator() {
1361         return mAllocator;
1362     }
1363 
1364     /**
1365      *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
1366      *
1367      *  <p>By default, ImageDecoder will create a {@link Bitmap} with
1368      *  premultiplied pixels, which is required for drawing with the
1369      *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
1370      *  this method with a value of {@code true} will result in
1371      *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
1372      *  pixels. See {@link Bitmap#isPremultiplied Bitmap.isPremultiplied()}.
1373      *  This is incompatible with {@link #decodeDrawable decodeDrawable};
1374      *  attempting to decode an unpremultiplied {@link Drawable} will throw an
1375      *  {@link java.lang.IllegalStateException}. </p>
1376      *
1377      *  <p>Like all setters on ImageDecoder, this must be called inside
1378      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1379      */
setUnpremultipliedRequired(boolean unpremultipliedRequired)1380     public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
1381         mUnpremultipliedRequired = unpremultipliedRequired;
1382     }
1383 
1384     /** @removed
1385      * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
1386      */
1387     @Deprecated
setRequireUnpremultiplied(boolean unpremultipliedRequired)1388     public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
1389         this.setUnpremultipliedRequired(unpremultipliedRequired);
1390         return this;
1391     }
1392 
1393     /**
1394      *  Return whether the {@link Bitmap} will have unpremultiplied pixels.
1395      */
isUnpremultipliedRequired()1396     public boolean isUnpremultipliedRequired() {
1397         return mUnpremultipliedRequired;
1398     }
1399 
1400     /** @removed
1401      * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
1402      */
1403     @Deprecated
getRequireUnpremultiplied()1404     public boolean getRequireUnpremultiplied() {
1405         return this.isUnpremultipliedRequired();
1406     }
1407 
1408     /**
1409      *  Modify the image after decoding and scaling.
1410      *
1411      *  <p>This allows adding effects prior to returning a {@link Drawable} or
1412      *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
1413      *  this is the only way to process the image after decoding.</p>
1414      *
1415      *  <p>If combined with {@link #setTargetSize} and/or {@link #setCrop},
1416      *  {@link PostProcessor#onPostProcess} occurs last.</p>
1417      *
1418      *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
1419      *
1420      *  <p>For an animated image, the drawing commands drawn on the
1421      *  {@link Canvas} will be recorded immediately and then applied to each
1422      *  frame.</p>
1423      *
1424      *  <p>Like all setters on ImageDecoder, this must be called inside
1425      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1426      *
1427      */
setPostProcessor(@ullable PostProcessor postProcessor)1428     public void setPostProcessor(@Nullable PostProcessor postProcessor) {
1429         mPostProcessor = postProcessor;
1430     }
1431 
1432     /**
1433      *  Return the {@link PostProcessor} currently set.
1434      */
1435     @Nullable
getPostProcessor()1436     public PostProcessor getPostProcessor() {
1437         return mPostProcessor;
1438     }
1439 
1440     /**
1441      *  Set (replace) the {@link OnPartialImageListener} on this object.
1442      *
1443      *  <p>Will be called if there is an error in the input. Without one, an
1444      *  error will result in an {@code Exception} being thrown.</p>
1445      *
1446      *  <p>Like all setters on ImageDecoder, this must be called inside
1447      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1448      *
1449      */
setOnPartialImageListener(@ullable OnPartialImageListener listener)1450     public void setOnPartialImageListener(@Nullable OnPartialImageListener listener) {
1451         mOnPartialImageListener = listener;
1452     }
1453 
1454     /**
1455      *  Return the {@link OnPartialImageListener OnPartialImageListener} currently set.
1456      */
1457     @Nullable
getOnPartialImageListener()1458     public OnPartialImageListener getOnPartialImageListener() {
1459         return mOnPartialImageListener;
1460     }
1461 
1462     /**
1463      *  Crop the output to {@code subset} of the (possibly) scaled image.
1464      *
1465      *  <p>{@code subset} must be contained within the size set by
1466      *  {@link #setTargetSize} or the bounds of the image if setTargetSize was
1467      *  not called. Otherwise an {@link IllegalStateException} will be thrown by
1468      *  {@link #decodeDrawable decodeDrawable}/{@link #decodeBitmap decodeBitmap}.</p>
1469      *
1470      *  <p>NOT intended as a replacement for
1471      *  {@link BitmapRegionDecoder#decodeRegion BitmapRegionDecoder.decodeRegion()}.
1472      *  This supports all formats, but merely crops the output.</p>
1473      *
1474      *  <p>Like all setters on ImageDecoder, this must be called inside
1475      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1476      *
1477      */
setCrop(@ullable Rect subset)1478     public void setCrop(@Nullable Rect subset) {
1479         mCropRect = subset;
1480     }
1481 
1482     /**
1483      *  Return the cropping rectangle, if set.
1484      */
1485     @Nullable
getCrop()1486     public Rect getCrop() {
1487         return mCropRect;
1488     }
1489 
1490     /**
1491      *  Set a Rect for retrieving nine patch padding.
1492      *
1493      *  If the image is a nine patch, this Rect will be set to the padding
1494      *  rectangle during decode. Otherwise it will not be modified.
1495      *
1496      *  <p>Like all setters on ImageDecoder, this must be called inside
1497      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1498      *
1499      *  @hide
1500      *  Must be public for access from android.graphics.drawable,
1501      *  but must not be called from outside the UI module.
1502      */
setOutPaddingRect(@onNull Rect outPadding)1503     public void setOutPaddingRect(@NonNull Rect outPadding) {
1504         mOutPaddingRect = outPadding;
1505     }
1506 
1507     /**
1508      *  Specify whether the {@link Bitmap} should be mutable.
1509      *
1510      *  <p>By default, a {@link Bitmap} created by {@link #decodeBitmap decodeBitmap}
1511      *  will be immutable i.e. {@link Bitmap#isMutable() Bitmap.isMutable()} returns
1512      *  {@code false}. This can be changed with {@code setMutableRequired(true)}.
1513      *
1514      *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
1515      *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
1516      *  Attempting to combine them will throw an
1517      *  {@link java.lang.IllegalStateException}.</p>
1518      *
1519      *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable decodeDrawable},
1520      *  which would require retrieving the Bitmap from the returned Drawable in
1521      *  order to modify. Attempting to decode a mutable {@link Drawable} will
1522      *  throw an {@link java.lang.IllegalStateException}.</p>
1523      *
1524      *  <p>Like all setters on ImageDecoder, this must be called inside
1525      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1526      */
setMutableRequired(boolean mutable)1527     public void setMutableRequired(boolean mutable) {
1528         mMutable = mutable;
1529     }
1530 
1531     /** @removed
1532      * @deprecated Renamed to {@link #setMutableRequired}.
1533      */
1534     @Deprecated
setMutable(boolean mutable)1535     public ImageDecoder setMutable(boolean mutable) {
1536         this.setMutableRequired(mutable);
1537         return this;
1538     }
1539 
1540     /**
1541      *  Return whether the decoded {@link Bitmap} will be mutable.
1542      */
isMutableRequired()1543     public boolean isMutableRequired() {
1544         return mMutable;
1545     }
1546 
1547     /** @removed
1548      * @deprecated Renamed to {@link #isMutableRequired}.
1549      */
1550     @Deprecated
getMutable()1551     public boolean getMutable() {
1552         return this.isMutableRequired();
1553     }
1554 
1555     /**
1556      * Save memory if possible by using a denser {@link Bitmap.Config} at the
1557      * cost of some image quality.
1558      *
1559      * <p>For example an opaque 8-bit image may be compressed into an
1560      * {@link Bitmap.Config#RGB_565} configuration, sacrificing image
1561      * quality to save memory.
1562      */
1563     public static final int MEMORY_POLICY_LOW_RAM = 0;
1564 
1565     /**
1566      * Use the most natural {@link Bitmap.Config} for the internal {@link Bitmap}.
1567      *
1568      * <p>This is the recommended default for most applications and usages. This
1569      * will use the closest {@link Bitmap.Config} for the encoded source. If the
1570      * encoded source does not exactly match any {@link Bitmap.Config}, the next
1571      * highest quality {@link Bitmap.Config} will be used avoiding any loss in
1572      * image quality.
1573      */
1574     public static final int MEMORY_POLICY_DEFAULT  = 1;
1575 
1576     /** @hide **/
1577     @Retention(SOURCE)
1578     @IntDef(value = { MEMORY_POLICY_DEFAULT, MEMORY_POLICY_LOW_RAM },
1579               prefix = {"MEMORY_POLICY_"})
1580     public @interface MemoryPolicy {};
1581 
1582     /**
1583      *  Specify the memory policy for the decoded {@link Bitmap}.
1584      *
1585      *  <p>Like all setters on ImageDecoder, this must be called inside
1586      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1587      */
setMemorySizePolicy(@emoryPolicy int policy)1588     public void setMemorySizePolicy(@MemoryPolicy int policy) {
1589         mConserveMemory = (policy == MEMORY_POLICY_LOW_RAM);
1590     }
1591 
1592     /**
1593      *  Retrieve the memory policy for the decoded {@link Bitmap}.
1594      */
1595     @MemoryPolicy
getMemorySizePolicy()1596     public int getMemorySizePolicy() {
1597         return mConserveMemory ? MEMORY_POLICY_LOW_RAM : MEMORY_POLICY_DEFAULT;
1598     }
1599 
1600     /** @removed
1601      * @deprecated Replaced by {@link #setMemorySizePolicy}.
1602      */
1603     @Deprecated
setConserveMemory(boolean conserveMemory)1604     public void setConserveMemory(boolean conserveMemory) {
1605         mConserveMemory = conserveMemory;
1606     }
1607 
1608     /** @removed
1609      * @deprecated Replaced by {@link #getMemorySizePolicy}.
1610      */
1611     @Deprecated
getConserveMemory()1612     public boolean getConserveMemory() {
1613         return mConserveMemory;
1614     }
1615 
1616     /**
1617      *  Specify whether to potentially treat the output as an alpha mask.
1618      *
1619      *  <p>If this is set to {@code true} and the image is encoded in a format
1620      *  with only one channel, treat that channel as alpha. Otherwise this call has
1621      *  no effect.</p>
1622      *
1623      *  <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
1624      *  combine them will result in {@link #decodeDrawable decodeDrawable}/
1625      *  {@link #decodeBitmap decodeBitmap} throwing an
1626      *  {@link java.lang.IllegalStateException}.</p>
1627      *
1628      *  <p>Like all setters on ImageDecoder, this must be called inside
1629      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1630      */
setDecodeAsAlphaMaskEnabled(boolean enabled)1631     public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
1632         mDecodeAsAlphaMask = enabled;
1633     }
1634 
1635     /** @removed
1636      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1637      */
1638     @Deprecated
setDecodeAsAlphaMask(boolean enabled)1639     public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
1640         this.setDecodeAsAlphaMaskEnabled(enabled);
1641         return this;
1642     }
1643 
1644     /** @removed
1645      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1646      */
1647     @Deprecated
setAsAlphaMask(boolean asAlphaMask)1648     public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
1649         this.setDecodeAsAlphaMask(asAlphaMask);
1650         return this;
1651     }
1652 
1653     /**
1654      *  Return whether to treat single channel input as alpha.
1655      *
1656      *  <p>This returns whether {@link #setDecodeAsAlphaMaskEnabled} was set to
1657      *  {@code true}. It may still return {@code true} even if the image has
1658      *  more than one channel and therefore will not be treated as an alpha
1659      *  mask.</p>
1660      */
isDecodeAsAlphaMaskEnabled()1661     public boolean isDecodeAsAlphaMaskEnabled() {
1662         return mDecodeAsAlphaMask;
1663     }
1664 
1665     /** @removed
1666      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1667      */
1668     @Deprecated
getDecodeAsAlphaMask()1669     public boolean getDecodeAsAlphaMask() {
1670         return mDecodeAsAlphaMask;
1671     }
1672 
1673     /** @removed
1674      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1675      */
1676     @Deprecated
getAsAlphaMask()1677     public boolean getAsAlphaMask() {
1678         return this.getDecodeAsAlphaMask();
1679     }
1680 
1681     /**
1682      * Specify the desired {@link ColorSpace} for the output.
1683      *
1684      * <p>If non-null, the decoder will try to decode into {@code colorSpace}.
1685      * If it is null, which is the default, or the request cannot be met, the
1686      * decoder will pick either the color space embedded in the image or the
1687      * {@link ColorSpace} best suited for the requested image configuration
1688      * (for instance {@link ColorSpace.Named#SRGB sRGB} for the
1689      * {@link Bitmap.Config#ARGB_8888} configuration and
1690      * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
1691      * {@link Bitmap.Config#RGBA_F16}).</p>
1692      *
1693      * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
1694      * currently supported. An <code>IllegalArgumentException</code> will
1695      * be thrown by {@link #decodeDrawable decodeDrawable}/
1696      * {@link #decodeBitmap decodeBitmap} when setting a non-RGB color space
1697      * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
1698      *
1699      * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
1700      * the specified color space's transfer function must be
1701      * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
1702      * <code>IllegalArgumentException</code> will be thrown by the decode methods
1703      * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
1704      * specified color space returns null.
1705      * Starting from {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
1706      * the color spaces with non ICC parametric curve transfer function are allowed.
1707      * E.g., {@link ColorSpace.Named#BT2020_HLG BT2020_HLG}.
1708      * </p>
1709      *
1710      * <p>Like all setters on ImageDecoder, this must be called inside
1711      * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1712      */
setTargetColorSpace(ColorSpace colorSpace)1713     public void setTargetColorSpace(ColorSpace colorSpace) {
1714         mDesiredColorSpace = colorSpace;
1715     }
1716 
1717     /**
1718      * Closes this resource, relinquishing any underlying resources. This method
1719      * is invoked automatically on objects managed by the try-with-resources
1720      * statement.
1721      *
1722      * <p>This is an implementation detail of {@link ImageDecoder}, and should
1723      * never be called manually.</p>
1724      */
1725     @Override
close()1726     public void close() {
1727         mCloseGuard.close();
1728         if (!mClosed.compareAndSet(false, true)) {
1729             return;
1730         }
1731         nClose(mNativePtr);
1732         mNativePtr = 0;
1733 
1734         if (mOwnsInputStream) {
1735             IoUtils.closeQuietly(mInputStream);
1736         }
1737         IoUtils.closeQuietly(mAssetFd);
1738 
1739         mInputStream = null;
1740         mAssetFd = null;
1741         mTempStorage = null;
1742     }
1743 
checkState(boolean animated)1744     private void checkState(boolean animated) {
1745         if (mNativePtr == 0) {
1746             throw new IllegalStateException("Cannot use closed ImageDecoder!");
1747         }
1748 
1749         checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
1750 
1751         // animated ignores the allocator, so no need to check for incompatible
1752         // fields.
1753         if (!animated && mAllocator == ALLOCATOR_HARDWARE) {
1754             if (mMutable) {
1755                 throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
1756             }
1757             if (mDecodeAsAlphaMask) {
1758                 throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
1759             }
1760         }
1761 
1762         if (mPostProcessor != null && mUnpremultipliedRequired) {
1763             throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
1764         }
1765     }
1766 
checkSubset(int width, int height, Rect r)1767     private static void checkSubset(int width, int height, Rect r) {
1768         if (r == null) {
1769             return;
1770         }
1771         if (r.width() <= 0 || r.height() <= 0) {
1772             throw new IllegalStateException("Subset " + r + " is empty/unsorted");
1773         }
1774         if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
1775             throw new IllegalStateException("Subset " + r + " not contained by "
1776                     + "scaled image bounds: (" + width + " x " + height + ")");
1777         }
1778     }
1779 
checkForExtended()1780     private boolean checkForExtended() {
1781         if (mDesiredColorSpace == null) {
1782             return false;
1783         }
1784         return mDesiredColorSpace == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
1785                 || mDesiredColorSpace == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
1786     }
1787 
getColorSpacePtr()1788     private long getColorSpacePtr() {
1789         if (mDesiredColorSpace == null) {
1790             return 0;
1791         }
1792         return mDesiredColorSpace.getNativeInstance();
1793     }
1794 
1795     @WorkerThread
1796     @NonNull
decodeBitmapInternal()1797     private Bitmap decodeBitmapInternal() throws IOException {
1798         checkState(false);
1799         return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
1800                 mDesiredWidth, mDesiredHeight, mCropRect,
1801                 mMutable, mAllocator, mUnpremultipliedRequired,
1802                 mConserveMemory, mDecodeAsAlphaMask, getColorSpacePtr(),
1803                 checkForExtended());
1804     }
1805 
callHeaderDecoded(@ullable OnHeaderDecodedListener listener, @NonNull Source src)1806     private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
1807             @NonNull Source src) {
1808         if (listener != null) {
1809             ImageInfo info =
1810                     new ImageInfo(
1811                             new Size(mWidth, mHeight), mAnimated, getMimeType(), getColorSpace());
1812             listener.onHeaderDecoded(this, info, src);
1813         }
1814     }
1815 
1816     /**
1817      * Return {@link ImageInfo} from a {@code Source}.
1818      *
1819      * <p>Returns the same {@link ImageInfo} object that a usual decoding process would return as
1820      * part of {@link OnHeaderDecodedListener}.
1821      *
1822      * @param src representing the encoded image.
1823      * @return ImageInfo describing the image.
1824      * @throws IOException if {@code src} is not found, is an unsupported format, or cannot be
1825      *     decoded for any reason.
1826      * @hide
1827      */
1828     @WorkerThread
1829     @NonNull
decodeHeader(@onNull Source src)1830     public static ImageInfo decodeHeader(@NonNull Source src) throws IOException {
1831         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeHeader");
1832         try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
1833             // We don't want to leak decoder so resolve all properties immediately.
1834             return new ImageInfo(
1835                     new Size(decoder.mWidth, decoder.mHeight),
1836                     decoder.mAnimated,
1837                     decoder.getMimeType(),
1838                     decoder.getColorSpace());
1839         } finally {
1840             // Close the ImageDecoder#decodeHeader trace.
1841             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
1842         }
1843     }
1844 
1845     /**
1846      *  Create a {@link Drawable} from a {@code Source}.
1847      *
1848      *  @param src representing the encoded image.
1849      *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
1850      *      default settings on the {@code ImageDecoder}. This will be called on
1851      *      the same thread as {@code decodeDrawable} before that method returns.
1852      *      This is required in order to change any of the default settings.
1853      *  @return Drawable for displaying the image.
1854      *  @throws IOException if {@code src} is not found, is an unsupported
1855      *      format, or cannot be decoded for any reason.
1856      */
1857     @WorkerThread
1858     @NonNull
decodeDrawable(@onNull Source src, @NonNull OnHeaderDecodedListener listener)1859     public static Drawable decodeDrawable(@NonNull Source src,
1860             @NonNull OnHeaderDecodedListener listener) throws IOException {
1861         if (listener == null) {
1862             throw new IllegalArgumentException("listener cannot be null! "
1863                     + "Use decodeDrawable(Source) to not have a listener");
1864         }
1865         return decodeDrawableImpl(src, listener);
1866     }
1867 
1868     @WorkerThread
1869     @NonNull
decodeDrawableImpl(@onNull Source src, @Nullable OnHeaderDecodedListener listener)1870     private static Drawable decodeDrawableImpl(@NonNull Source src,
1871             @Nullable OnHeaderDecodedListener listener) throws IOException {
1872         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeDrawable");
1873         try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
1874             decoder.mSource = src;
1875             decoder.callHeaderDecoded(listener, src);
1876 
1877             try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
1878                 if (decoder.mUnpremultipliedRequired) {
1879                     // Though this could be supported (ignored) for opaque images,
1880                     // it seems better to always report this error.
1881                     throw new IllegalStateException(
1882                             "Cannot decode a Drawable with unpremultiplied pixels!");
1883                 }
1884 
1885                 if (decoder.mMutable) {
1886                     throw new IllegalStateException("Cannot decode a mutable Drawable!");
1887                 }
1888 
1889                 // this call potentially manipulates the decoder so it must be performed prior to
1890                 // decoding the bitmap and after decode set the density on the resulting bitmap
1891                 final int srcDensity = decoder.computeDensity(src);
1892                 if (decoder.mAnimated) {
1893                     // AnimatedImageDrawable calls postProcessAndRelease only if
1894                     // mPostProcessor exists.
1895                     ImageDecoder postProcessPtr = decoder.mPostProcessor == null ? null : decoder;
1896                     decoder.checkState(true);
1897                     Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
1898                             postProcessPtr, decoder.mDesiredWidth,
1899                             decoder.mDesiredHeight, decoder.getColorSpacePtr(),
1900                             decoder.checkForExtended(), srcDensity,
1901                             src.computeDstDensity(), decoder.mCropRect,
1902                             decoder.mInputStream, decoder.mAssetFd);
1903                     // d has taken ownership of these objects.
1904                     decoder.mInputStream = null;
1905                     decoder.mAssetFd = null;
1906                     return d;
1907                 }
1908 
1909                 Bitmap bm = decoder.decodeBitmapInternal();
1910                 bm.setDensity(srcDensity);
1911 
1912                 Resources res = src.getResources();
1913                 byte[] np = bm.getNinePatchChunk();
1914                 if (np != null && NinePatch.isNinePatchChunk(np)) {
1915                     Rect opticalInsets = new Rect();
1916                     bm.getOpticalInsets(opticalInsets);
1917                     Rect padding = decoder.mOutPaddingRect;
1918                     if (padding == null) {
1919                         padding = new Rect();
1920                     }
1921                     nGetPadding(decoder.mNativePtr, padding);
1922                     return new NinePatchDrawable(res, bm, np, padding,
1923                             opticalInsets, null);
1924                 }
1925 
1926                 return new BitmapDrawable(res, bm);
1927             }
1928         } finally {
1929             // Close the ImageDecoder#decode trace.
1930             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
1931         }
1932     }
1933 
1934     /**
1935      *  Create a {@link Drawable} from a {@code Source}.
1936      *
1937      *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
1938      *  the default settings will be used. In order to change any settings, call
1939      *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} instead.</p>
1940      *
1941      *  @param src representing the encoded image.
1942      *  @return Drawable for displaying the image.
1943      *  @throws IOException if {@code src} is not found, is an unsupported
1944      *      format, or cannot be decoded for any reason.
1945      */
1946     @WorkerThread
1947     @NonNull
decodeDrawable(@onNull Source src)1948     public static Drawable decodeDrawable(@NonNull Source src)
1949             throws IOException {
1950         return decodeDrawableImpl(src, null);
1951     }
1952 
1953     /**
1954      *  Create a {@link Bitmap} from a {@code Source}.
1955      *
1956      *  @param src representing the encoded image.
1957      *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
1958      *      default settings on the {@code ImageDecoder}. This will be called on
1959      *      the same thread as {@code decodeBitmap} before that method returns.
1960      *      This is required in order to change any of the default settings.
1961      *  @return Bitmap containing the image.
1962      *  @throws IOException if {@code src} is not found, is an unsupported
1963      *      format, or cannot be decoded for any reason.
1964      */
1965     @WorkerThread
1966     @NonNull
decodeBitmap(@onNull Source src, @NonNull OnHeaderDecodedListener listener)1967     public static Bitmap decodeBitmap(@NonNull Source src,
1968             @NonNull OnHeaderDecodedListener listener) throws IOException {
1969         if (listener == null) {
1970             throw new IllegalArgumentException("listener cannot be null! "
1971                     + "Use decodeBitmap(Source) to not have a listener");
1972         }
1973         return decodeBitmapImpl(src, listener);
1974     }
1975 
1976     @WorkerThread
1977     @NonNull
decodeBitmapImpl(@onNull Source src, @Nullable OnHeaderDecodedListener listener)1978     private static Bitmap decodeBitmapImpl(@NonNull Source src,
1979             @Nullable OnHeaderDecodedListener listener) throws IOException {
1980         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeBitmap");
1981         try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
1982             decoder.mSource = src;
1983             decoder.callHeaderDecoded(listener, src);
1984             try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
1985                 // this call potentially manipulates the decoder so it must be performed prior to
1986                 // decoding the bitmap
1987                 final int srcDensity = decoder.computeDensity(src);
1988                 Bitmap bm = decoder.decodeBitmapInternal();
1989                 bm.setDensity(srcDensity);
1990 
1991                 Rect padding = decoder.mOutPaddingRect;
1992                 if (padding != null) {
1993                     byte[] np = bm.getNinePatchChunk();
1994                     if (np != null && NinePatch.isNinePatchChunk(np)) {
1995                         nGetPadding(decoder.mNativePtr, padding);
1996                     }
1997                 }
1998                 return bm;
1999             }
2000         } finally {
2001             // Close the ImageDecoder#decode trace.
2002             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
2003         }
2004     }
2005 
2006     /**
2007      * This describes the decoder in traces to ease debugging. It has to be called after
2008      * header has been decoded and width/height have been populated. It should be used
2009      * inside a try-with-resources call to automatically complete the trace.
2010      */
traceDecoderSource(ImageDecoder decoder)2011     private static AutoCloseable traceDecoderSource(ImageDecoder decoder) {
2012         final boolean resourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
2013         if (resourceTracingEnabled) {
2014             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
2015         }
2016 
2017         return new AutoCloseable() {
2018             @Override
2019             public void close() throws Exception {
2020                 if (resourceTracingEnabled) {
2021                     Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
2022                 }
2023             }
2024         };
2025     }
2026 
2027     // This method may modify the decoder so it must be called prior to performing the decode
2028     private int computeDensity(@NonNull Source src) {
2029         // if the caller changed the size then we treat the density as unknown
2030         if (this.requestedResize()) {
2031             return Bitmap.DENSITY_NONE;
2032         }
2033 
2034         final int srcDensity = src.getDensity();
2035         if (srcDensity == Bitmap.DENSITY_NONE) {
2036             return srcDensity;
2037         }
2038 
2039         // Scaling up nine-patch divs is imprecise and is better handled
2040         // at draw time. An app won't be relying on the internal Bitmap's
2041         // size, so it is safe to let NinePatchDrawable handle scaling.
2042         // mPostProcessor disables nine-patching, so behave normally if
2043         // it is present.
2044         if (mIsNinePatch && mPostProcessor == null) {
2045             return srcDensity;
2046         }
2047 
2048         // Special stuff for compatibility mode: if the target density is not
2049         // the same as the display density, but the resource -is- the same as
2050         // the display density, then don't scale it down to the target density.
2051         // This allows us to load the system's density-correct resources into
2052         // an application in compatibility mode, without scaling those down
2053         // to the compatibility density only to have them scaled back up when
2054         // drawn to the screen.
2055         Resources res = src.getResources();
2056         if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
2057             return srcDensity;
2058         }
2059 
2060         final int dstDensity = src.computeDstDensity();
2061         if (srcDensity == dstDensity) {
2062             return srcDensity;
2063         }
2064 
2065         // For P and above, only resize if it would be a downscale. Scale up prior
2066         // to P in case the app relies on the Bitmap's size without considering density.
2067         if (srcDensity < dstDensity
2068                 && Compatibility.getTargetSdkVersion() >= Build.VERSION_CODES.P) {
2069             return srcDensity;
2070         }
2071 
2072         float scale = (float) dstDensity / srcDensity;
2073         int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
2074         int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
2075         this.setTargetSize(scaledWidth, scaledHeight);
2076         return dstDensity;
2077     }
2078 
2079     @NonNull
2080     private String getMimeType() {
2081         return nGetMimeType(mNativePtr);
2082     }
2083 
2084     @Nullable
2085     private ColorSpace getColorSpace() {
2086         return nGetColorSpace(mNativePtr);
2087     }
2088 
2089     /**
2090      *  Create a {@link Bitmap} from a {@code Source}.
2091      *
2092      *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
2093      *  the default settings will be used. In order to change any settings, call
2094      *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)} instead.</p>
2095      *
2096      *  @param src representing the encoded image.
2097      *  @return Bitmap containing the image.
2098      *  @throws IOException if {@code src} is not found, is an unsupported
2099      *      format, or cannot be decoded for any reason.
2100      */
2101     @WorkerThread
2102     @NonNull
2103     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
2104         return decodeBitmapImpl(src, null);
2105     }
2106 
2107     private static boolean sIsHevcDecoderSupported = false;
2108     private static boolean sIsHevcDecoderSupportedInitialized = false;
2109     private static final Object sIsHevcDecoderSupportedLock = new Object();
2110 
2111     /*
2112      * Check if HEVC decoder is supported by the device.
2113      */
2114     @SuppressWarnings("AndroidFrameworkCompatChange")
2115     private static boolean isHevcDecoderSupported() {
2116         synchronized (sIsHevcDecoderSupportedLock) {
2117             if (sIsHevcDecoderSupportedInitialized) {
2118                 return sIsHevcDecoderSupported;
2119             }
2120             MediaFormat format = new MediaFormat();
2121             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_HEVC);
2122             MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
2123             sIsHevcDecoderSupported = mcl.findDecoderForFormat(format) != null;
2124             sIsHevcDecoderSupportedInitialized = true;
2125             return sIsHevcDecoderSupported;
2126         }
2127     }
2128 
2129     private static boolean sIsP010SupportedForAV1 = false;
2130     private static boolean sIsP010SupportedForHEVC = false;
2131     private static boolean sIsP010SupportedFlagsInitialized = false;
2132     private static final Object sIsP010SupportedLock = new Object();
2133 
2134     /**
2135      * Checks if the device supports decoding 10-bit AV1.
2136      */
2137     @SuppressWarnings("AndroidFrameworkCompatChange")  // This is not an app-visible API.
2138     private static boolean isP010SupportedForAV1() {
2139         synchronized (sIsP010SupportedLock) {
2140             if (sIsP010SupportedFlagsInitialized) {
2141                 return sIsP010SupportedForAV1;
2142             }
2143             checkP010SupportforAV1HEVC();
2144             return sIsP010SupportedForAV1;
2145         }
2146     }
2147 
2148     /**
2149      * Checks if the device supports decoding 10-bit HEVC.
2150      * This method is called by JNI.
2151      */
2152     @SuppressWarnings("unused")
2153     private static boolean isP010SupportedForHEVC() {
2154         synchronized (sIsP010SupportedLock) {
2155             if (sIsP010SupportedFlagsInitialized) {
2156                 return sIsP010SupportedForHEVC;
2157             }
2158             checkP010SupportforAV1HEVC();
2159             return sIsP010SupportedForHEVC;
2160         }
2161     }
2162 
2163     /**
2164      * Checks if the device supports decoding 10-bit for the given mime type.
2165      */
2166     private static void checkP010SupportforAV1HEVC() {
2167         MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
2168         for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) {
2169             if (mediaCodecInfo.isEncoder()) {
2170                 continue;
2171             }
2172             for (String mediaType : mediaCodecInfo.getSupportedTypes()) {
2173                 if (mediaType.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)
2174                         || mediaType.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
2175                     MediaCodecInfo.CodecCapabilities codecCapabilities =
2176                         mediaCodecInfo.getCapabilitiesForType(mediaType);
2177                     for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) {
2178                         if (codecCapabilities.colorFormats[i]
2179                             == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) {
2180                             if (mediaType.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)) {
2181                                 sIsP010SupportedForAV1 = true;
2182                             } else {
2183                                 sIsP010SupportedForHEVC = true;
2184                             }
2185                         }
2186                     }
2187                 }
2188             }
2189         }
2190         sIsP010SupportedFlagsInitialized = true;
2191     }
2192 
2193     /**
2194      * Private method called by JNI.
2195      */
2196     @SuppressWarnings("unused")
2197     private int postProcessAndRelease(@NonNull Canvas canvas) {
2198         try {
2199             return mPostProcessor.onPostProcess(canvas);
2200         } finally {
2201             canvas.release();
2202         }
2203     }
2204 
2205     /**
2206      * Private method called by JNI.
2207      */
2208     @SuppressWarnings("unused")
2209     private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause)
2210             throws DecodeException {
2211         DecodeException exception = new DecodeException(error, cause, mSource);
2212         if (mOnPartialImageListener == null
2213                 || !mOnPartialImageListener.onPartialImage(exception)) {
2214             throw exception;
2215         }
2216     }
2217 
2218     /**
2219      * Returns a short string describing what passed ImageDecoder is loading -
2220      * it reports image dimensions, desired dimensions (if any) and source resource.
2221      *
2222      * The string appears in perf traces to simplify search for slow or memory intensive
2223      * image loads.
2224      *
2225      * Example: ID#w=300;h=250;dw=150;dh=150;src=Resource{name=@resource}
2226      *
2227      * @hide
2228      */
2229     private static String describeDecoderForTrace(@NonNull ImageDecoder decoder) {
2230         StringBuilder builder = new StringBuilder();
2231         // Source dimensions
2232         builder.append("ID#w=");
2233         builder.append(decoder.mWidth);
2234         builder.append(";h=");
2235         builder.append(decoder.mHeight);
2236         // Desired dimensions (if present)
2237         if (decoder.mDesiredWidth != decoder.mWidth
2238                 || decoder.mDesiredHeight != decoder.mHeight) {
2239             builder.append(";dw=");
2240             builder.append(decoder.mDesiredWidth);
2241             builder.append(";dh=");
2242             builder.append(decoder.mDesiredHeight);
2243         }
2244         // Source description
2245         builder.append(";src=");
2246         builder.append(decoder.mSource);
2247         return builder.toString();
2248     }
2249 
2250     /**
2251      * Records a trace with information about the source being decoded - dimensions,
2252      * desired dimensions and source information.
2253      *
2254      * It significantly eases debugging of slow resource loads on main thread and
2255      * possible large memory consumers.
2256      *
2257      * @hide
2258      */
2259     private static final class ImageDecoderSourceTrace implements AutoCloseable {
2260 
2261         private final boolean mResourceTracingEnabled;
2262 
2263         ImageDecoderSourceTrace(ImageDecoder decoder) {
2264             mResourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
2265             if (mResourceTracingEnabled) {
2266                 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
2267             }
2268         }
2269 
2270         @Override
2271         public void close() {
2272             if (mResourceTracingEnabled) {
2273                 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
2274             }
2275         }
2276     }
2277 
2278     private static native ImageDecoder nCreate(long asset,
2279             boolean preferAnimation, Source src) throws IOException;
2280     private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,
2281             boolean preferAnimation, Source src) throws IOException;
2282     private static native ImageDecoder nCreate(byte[] data, int offset, int length,
2283             boolean preferAnimation, Source src) throws IOException;
2284     private static native ImageDecoder nCreate(InputStream is, byte[] storage,
2285             boolean preferAnimation, Source src) throws IOException;
2286     // The fd must be seekable.
2287     private static native ImageDecoder nCreate(FileDescriptor fd, long length,
2288             boolean preferAnimation, Source src) throws IOException;
2289     @NonNull
2290     private static native Bitmap nDecodeBitmap(long nativePtr,
2291             @NonNull ImageDecoder decoder,
2292             boolean doPostProcess,
2293             int width, int height,
2294             @Nullable Rect cropRect, boolean mutable,
2295             int allocator, boolean unpremulRequired,
2296             boolean conserveMemory, boolean decodeAsAlphaMask,
2297             long desiredColorSpace, boolean extended)
2298         throws IOException;
2299     private static native Size nGetSampledSize(long nativePtr,
2300                                                int sampleSize);
2301     private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
2302     private static native void nClose(long nativePtr);
2303     private static native String nGetMimeType(long nativePtr);
2304     private static native ColorSpace nGetColorSpace(long nativePtr);
2305 }
2306