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