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
17package android.graphics;
18
19import static android.system.OsConstants.SEEK_CUR;
20import static android.system.OsConstants.SEEK_SET;
21
22import static java.lang.annotation.RetentionPolicy.SOURCE;
23
24import android.annotation.AnyThread;
25import android.annotation.IntDef;
26import android.annotation.IntRange;
27import android.annotation.NonNull;
28import android.annotation.Nullable;
29import android.annotation.Px;
30import android.annotation.TestApi;
31import android.annotation.WorkerThread;
32import android.content.ContentResolver;
33import android.content.res.AssetFileDescriptor;
34import android.content.res.AssetManager;
35import android.content.res.AssetManager.AssetInputStream;
36import android.content.res.Resources;
37import android.graphics.drawable.AnimatedImageDrawable;
38import android.graphics.drawable.BitmapDrawable;
39import android.graphics.drawable.Drawable;
40import android.graphics.drawable.NinePatchDrawable;
41import android.net.Uri;
42import android.os.Build;
43import android.system.ErrnoException;
44import android.system.Os;
45import android.util.DisplayMetrics;
46import android.util.Size;
47import android.util.TypedValue;
48
49import dalvik.system.CloseGuard;
50
51import libcore.io.IoUtils;
52
53import java.io.File;
54import java.io.FileDescriptor;
55import java.io.FileInputStream;
56import java.io.FileNotFoundException;
57import java.io.IOException;
58import java.io.InputStream;
59import java.lang.annotation.Retention;
60import java.nio.ByteBuffer;
61import 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 */
169public 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 {
192        private Source() {}
193
194        /* @hide */
195        @Nullable
196        Resources getResources() { return null; }
197
198        /* @hide */
199        int getDensity() { return Bitmap.DENSITY_NONE; }
200
201        /* @hide */
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
213        abstract ImageDecoder createImageDecoder() throws IOException;
214    };
215
216    private static class ByteArraySource extends Source {
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
227        public ImageDecoder createImageDecoder() throws IOException {
228            return nCreate(mData, mOffset, mLength, this);
229        }
230    }
231
232    private static class ByteBufferSource extends Source {
233        ByteBufferSource(@NonNull ByteBuffer buffer) {
234            mBuffer = buffer;
235        }
236        private final ByteBuffer mBuffer;
237
238        @Override
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 {
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
263        Resources getResources() { return mResources; }
264
265        @Override
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
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
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 {
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
376        public Resources getResources() { return mResources; }
377
378        @Override
379        public int getDensity() { return mInputDensity; }
380
381        @Override
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 {
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
420        public Resources getResources() { return mResources; }
421
422        @Override
423        public int getDensity() {
424            return mDensity;
425        }
426
427        @Override
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 {
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
453        public Resources getResources() { return mResources; }
454
455        @Override
456        public int getDensity() {
457            synchronized (mLock) {
458                return mResDensity;
459            }
460        }
461
462        @Override
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     */
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 {
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
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 {
518        FileSource(@NonNull File file) {
519            mFile = file;
520        }
521
522        private final File mFile;
523
524        @Override
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
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
546        public Size getSize() {
547            return mSize;
548        }
549
550        /**
551         * The mimeType of the image.
552         */
553        @NonNull
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         */
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
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         */
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
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")
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
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
695        public Source getSource() {
696            return mSource;
697        }
698
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         */
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")
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
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
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
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
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
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
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
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
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
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
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
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
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
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     */
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
1045    public ImageDecoder setResize(int sampleSize) {
1046        this.setTargetSampleSize(sampleSize);
1047        return this;
1048    }
1049
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     */
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
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     */
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
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     */
1201    public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
1202        mUnpremultipliedRequired = unpremultipliedRequired;
1203    }
1204
1205    /** @removed
1206     * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
1207     */
1208    @Deprecated
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     */
1217    public boolean isUnpremultipliedRequired() {
1218        return mUnpremultipliedRequired;
1219    }
1220
1221    /** @removed
1222     * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
1223     */
1224    @Deprecated
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     */
1249    public void setPostProcessor(@Nullable PostProcessor postProcessor) {
1250        mPostProcessor = postProcessor;
1251    }
1252
1253    /**
1254     *  Return the {@link PostProcessor} currently set.
1255     */
1256    @Nullable
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     */
1271    public void setOnPartialImageListener(@Nullable OnPartialImageListener listener) {
1272        mOnPartialImageListener = listener;
1273    }
1274
1275    /**
1276     *  Return the {@link OnPartialImageListener OnPartialImageListener} currently set.
1277     */
1278    @Nullable
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     */
1299    public void setCrop(@Nullable Rect subset) {
1300        mCropRect = subset;
1301    }
1302
1303    /**
1304     *  Return the cropping rectangle, if set.
1305     */
1306    @Nullable
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     */
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     */
1346    public void setMutableRequired(boolean mutable) {
1347        mMutable = mutable;
1348    }
1349
1350    /** @removed
1351     * @deprecated Renamed to {@link #setMutableRequired}.
1352     */
1353    @Deprecated
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     */
1362    public boolean isMutableRequired() {
1363        return mMutable;
1364    }
1365
1366    /** @removed
1367     * @deprecated Renamed to {@link #isMutableRequired}.
1368     */
1369    @Deprecated
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     */
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
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
1423    public void setConserveMemory(boolean conserveMemory) {
1424        mConserveMemory = conserveMemory;
1425    }
1426
1427    /** @removed
1428     * @deprecated Replaced by {@link #getMemorySizePolicy}.
1429     */
1430    @Deprecated
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     */
1450    public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
1451        mDecodeAsAlphaMask = enabled;
1452    }
1453
1454    /** @removed
1455     * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1456     */
1457    @Deprecated
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
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     */
1480    public boolean isDecodeAsAlphaMaskEnabled() {
1481        return mDecodeAsAlphaMask;
1482    }
1483
1484    /** @removed
1485     * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1486     */
1487    @Deprecated
1488    public boolean getDecodeAsAlphaMask() {
1489        return mDecodeAsAlphaMask;
1490    }
1491
1492    /** @removed
1493     * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1494     */
1495    @Deprecated
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     */
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
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
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
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
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
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
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
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
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
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
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
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
1828    private String getMimeType() {
1829        return nGetMimeType(mNativePtr);
1830    }
1831
1832    @Nullable
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
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")
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")
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
1880    private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
1881    private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
1882                                               int limit, Source src) throws IOException;
1883    private static native ImageDecoder nCreate(byte[] data, int offset, int length,
1884                                               Source src) throws IOException;
1885    private static native ImageDecoder nCreate(InputStream is, byte[] storage,
1886                                               Source src) throws IOException;
1887    // The fd must be seekable.
1888    private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
1889    @NonNull
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;
1899    private static native Size nGetSampledSize(long nativePtr,
1900                                               int sampleSize);
1901    private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
1902    private static native void nClose(long nativePtr);
1903    private static native String nGetMimeType(long nativePtr);
1904    private static native ColorSpace nGetColorSpace(long nativePtr);
1905}
1906