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