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