blob: 4210c5caa6909462589c146e404fb3f8940493c0 [file] [log] [blame]
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001/*
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
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -050019import static android.system.OsConstants.SEEK_CUR;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050020import static android.system.OsConstants.SEEK_SET;
21
Leon Scroggins III5f7e9482018-02-23 13:22:29 -050022import static java.lang.annotation.RetentionPolicy.SOURCE;
23
Leon Scroggins III0a87cb32018-03-29 15:54:59 -040024import android.annotation.AnyThread;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040025import android.annotation.IntDef;
Leon Scroggins IIId97e2642018-04-03 11:55:55 -040026import android.annotation.IntRange;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040027import android.annotation.NonNull;
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -050028import android.annotation.Nullable;
Leon Scroggins IIId97e2642018-04-03 11:55:55 -040029import android.annotation.Px;
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -040030import android.annotation.TestApi;
Leon Scroggins III0a87cb32018-03-29 15:54:59 -040031import android.annotation.WorkerThread;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050032import android.content.ContentResolver;
33import android.content.res.AssetFileDescriptor;
Leon Scroggins III121ef982018-03-16 15:40:54 -040034import android.content.res.AssetManager;
Leon Scroggins III513031d2018-02-15 15:31:45 -050035import android.content.res.AssetManager.AssetInputStream;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040036import android.content.res.Resources;
Leon Scroggins III671cce22018-01-14 16:52:17 -050037import android.graphics.drawable.AnimatedImageDrawable;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040038import android.graphics.drawable.BitmapDrawable;
Leon Scroggins IIIa38b2132018-02-23 12:27:27 -050039import android.graphics.drawable.Drawable;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040040import android.graphics.drawable.NinePatchDrawable;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050041import android.net.Uri;
Leon Scroggins III8290eab2018-03-19 10:51:44 -040042import android.os.Build;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050043import android.system.ErrnoException;
44import android.system.Os;
Derek Sollenberger66c6d782018-01-12 16:40:52 -050045import android.util.DisplayMetrics;
Leon Scroggins IIIa38b2132018-02-23 12:27:27 -050046import android.util.Size;
Derek Sollenberger66c6d782018-01-12 16:40:52 -050047import android.util.TypedValue;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050048
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050049import dalvik.system.CloseGuard;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040050
Leon Scroggins IIIa38b2132018-02-23 12:27:27 -050051import libcore.io.IoUtils;
52
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -050053import java.io.File;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050054import java.io.FileDescriptor;
55import java.io.FileInputStream;
56import java.io.FileNotFoundException;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040057import java.io.IOException;
58import java.io.InputStream;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040059import java.lang.annotation.Retention;
Leon Scroggins IIIa38b2132018-02-23 12:27:27 -050060import java.nio.ByteBuffer;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -050061import java.util.concurrent.atomic.AtomicBoolean;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040062
63/**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -040064 * <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>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400168 */
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500169public final class ImageDecoder implements AutoCloseable {
Leon Scroggins III8290eab2018-03-19 10:51:44 -0400170 /** @hide **/
171 public static int sApiLevel;
172
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400173 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400174 * Source of encoded image data.
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400175 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400176 * <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}.
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400182 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400183 * <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}
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400189 * even used simultaneously in multiple threads.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400190 */
191 public static abstract class Source {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500192 private Source() {}
193
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400194 /* @hide */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500195 @Nullable
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400196 Resources getResources() { return null; }
197
198 /* @hide */
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500199 int getDensity() { return Bitmap.DENSITY_NONE; }
200
201 /* @hide */
Leon Scroggins III046a99e2018-01-31 14:59:29 -0500202 final int computeDstDensity() {
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500203 Resources res = getResources();
204 if (res == null) {
205 return Bitmap.getDefaultDensity();
206 }
207
208 return res.getDisplayMetrics().densityDpi;
209 }
210
211 /* @hide */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500212 @NonNull
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500213 abstract ImageDecoder createImageDecoder() throws IOException;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400214 };
215
216 private static class ByteArraySource extends Source {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500217 ByteArraySource(@NonNull byte[] data, int offset, int length) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400218 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
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500227 public ImageDecoder createImageDecoder() throws IOException {
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400228 return nCreate(mData, mOffset, mLength, this);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400229 }
230 }
231
232 private static class ByteBufferSource extends Source {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500233 ByteBufferSource(@NonNull ByteBuffer buffer) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400234 mBuffer = buffer;
235 }
236 private final ByteBuffer mBuffer;
237
238 @Override
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500239 public ImageDecoder createImageDecoder() throws IOException {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400240 if (!mBuffer.isDirect() && mBuffer.hasArray()) {
241 int offset = mBuffer.arrayOffset() + mBuffer.position();
242 int length = mBuffer.limit() - mBuffer.position();
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400243 return nCreate(mBuffer.array(), offset, length, this);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400244 }
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400245 ByteBuffer buffer = mBuffer.slice();
246 return nCreate(buffer, buffer.position(), buffer.limit(), this);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400247 }
248 }
249
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500250 private static class ContentResolverSource extends Source {
Leon Scroggins III046a99e2018-01-31 14:59:29 -0500251 ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri,
252 @Nullable Resources res) {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500253 mResolver = resolver;
254 mUri = uri;
Leon Scroggins III046a99e2018-01-31 14:59:29 -0500255 mResources = res;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500256 }
257
258 private final ContentResolver mResolver;
259 private final Uri mUri;
Leon Scroggins III046a99e2018-01-31 14:59:29 -0500260 private final Resources mResources;
261
262 @Nullable
263 Resources getResources() { return mResources; }
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500264
265 @Override
266 public ImageDecoder createImageDecoder() throws IOException {
267 AssetFileDescriptor assetFd = null;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400268 try {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500269 if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
270 assetFd = mResolver.openTypedAssetFileDescriptor(mUri,
271 "image/*", null);
272 } else {
273 assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400274 }
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500275 } 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
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400283 return createFromStream(is, true, this);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400284 }
285
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500286 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);
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400293 decoder = nCreate(fd, this);
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500294 } catch (ErrnoException e) {
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400295 decoder = createFromStream(new FileInputStream(fd), true, this);
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500296 }
297 } finally {
298 if (decoder == null) {
299 IoUtils.closeQuietly(assetFd);
300 } else {
301 decoder.mAssetFd = assetFd;
302 }
303 }
304 return decoder;
305 }
306 }
307
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500308 @NonNull
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400309 private static ImageDecoder createFromFile(@NonNull File file,
310 @NonNull Source source) throws IOException {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500311 FileInputStream stream = new FileInputStream(file);
312 FileDescriptor fd = stream.getFD();
313 try {
314 Os.lseek(fd, 0, SEEK_CUR);
315 } catch (ErrnoException e) {
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400316 return createFromStream(stream, true, source);
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500317 }
318
319 ImageDecoder decoder = null;
320 try {
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400321 decoder = nCreate(fd, source);
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500322 } finally {
323 if (decoder == null) {
324 IoUtils.closeQuietly(stream);
325 } else {
326 decoder.mInputStream = stream;
Leon Scroggins III0b49f572018-01-23 11:38:23 -0500327 decoder.mOwnsInputStream = true;
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500328 }
329 }
330 return decoder;
331 }
332
333 @NonNull
Leon Scroggins III0b49f572018-01-23 11:38:23 -0500334 private static ImageDecoder createFromStream(@NonNull InputStream is,
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400335 boolean closeInputStream, Source source) throws IOException {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500336 // Arbitrary size matches BitmapFactory.
337 byte[] storage = new byte[16 * 1024];
338 ImageDecoder decoder = null;
339 try {
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400340 decoder = nCreate(is, storage, source);
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500341 } finally {
342 if (decoder == null) {
Leon Scroggins III0b49f572018-01-23 11:38:23 -0500343 if (closeInputStream) {
344 IoUtils.closeQuietly(is);
345 }
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500346 } else {
347 decoder.mInputStream = is;
Leon Scroggins III0b49f572018-01-23 11:38:23 -0500348 decoder.mOwnsInputStream = closeInputStream;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500349 decoder.mTempStorage = storage;
350 }
351 }
352
353 return decoder;
354 }
355
Leon Scroggins III0b49f572018-01-23 11:38:23 -0500356 /**
357 * For backwards compatibility, this does *not* close the InputStream.
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400358 *
359 * Further, unlike other Sources, this one is not reusable.
Leon Scroggins III0b49f572018-01-23 11:38:23 -0500360 */
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500361 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;
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400390 return createFromStream(is, false, this);
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500391 }
392 }
393 }
394
Leon Scroggins III513031d2018-02-15 15:31:45 -0500395 /**
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 {
Leon Scroggins III513031d2018-02-15 15:31:45 -0500429 synchronized (this) {
430 if (mAssetInputStream == null) {
431 throw new IOException("Cannot reuse AssetInputStreamSource");
432 }
433 AssetInputStream ais = mAssetInputStream;
434 mAssetInputStream = null;
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400435 return createFromAsset(ais, this);
Leon Scroggins III513031d2018-02-15 15:31:45 -0500436 }
437 }
438 }
439
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500440 private static class ResourceSource extends Source {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500441 ResourceSource(@NonNull Resources res, int resId) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400442 mResources = res;
443 mResId = resId;
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500444 mResDensity = Bitmap.DENSITY_NONE;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400445 }
446
447 final Resources mResources;
448 final int mResId;
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500449 int mResDensity;
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400450 private Object mLock = new Object();
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400451
452 @Override
453 public Resources getResources() { return mResources; }
454
455 @Override
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400456 public int getDensity() {
457 synchronized (mLock) {
458 return mResDensity;
459 }
460 }
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500461
462 @Override
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500463 public ImageDecoder createImageDecoder() throws IOException {
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500464 TypedValue value = new TypedValue();
Leon Scroggins III121ef982018-03-16 15:40:54 -0400465 // This is just used in order to access the underlying Asset and
466 // keep it alive.
467 InputStream is = mResources.openRawResource(mResId, value);
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500468
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400469 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 }
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400475 }
Leon Scroggins III121ef982018-03-16 15:40:54 -0400476
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400477 return createFromAsset((AssetInputStream) is, this);
Leon Scroggins III121ef982018-03-16 15:40:54 -0400478 }
479 }
480
481 /**
482 * ImageDecoder will own the AssetInputStream.
483 */
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400484 private static ImageDecoder createFromAsset(AssetInputStream ais,
485 Source source) throws IOException {
Leon Scroggins III121ef982018-03-16 15:40:54 -0400486 ImageDecoder decoder = null;
487 try {
488 long asset = ais.getNativeAsset();
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400489 decoder = nCreate(asset, source);
Leon Scroggins III121ef982018-03-16 15:40:54 -0400490 } 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);
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400513 return createFromAsset((AssetInputStream) is, this);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400514 }
515 }
516
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500517 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 {
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400526 return createFromFile(mFile, this);
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500527 }
528 }
529
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400530 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400531 * Information about an encoded image.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400532 */
533 public static class ImageInfo {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500534 private final Size mSize;
535 private ImageDecoder mDecoder;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400536
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500537 private ImageInfo(@NonNull ImageDecoder decoder) {
538 mSize = new Size(decoder.mWidth, decoder.mHeight);
539 mDecoder = decoder;
Leon Scroggins III1fad09d2017-12-22 12:54:20 -0500540 }
541
542 /**
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500543 * Size of the image, without scaling or cropping.
Leon Scroggins III1fad09d2017-12-22 12:54:20 -0500544 */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500545 @NonNull
546 public Size getSize() {
547 return mSize;
548 }
549
550 /**
551 * The mimeType of the image.
552 */
553 @NonNull
Leon Scroggins III1fad09d2017-12-22 12:54:20 -0500554 public String getMimeType() {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500555 return mDecoder.getMimeType();
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400556 }
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500557
558 /**
559 * Whether the image is animated.
560 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400561 * <p>If {@code true}, {@link #decodeDrawable decodeDrawable} will
562 * return an {@link AnimatedImageDrawable}.</p>
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500563 */
564 public boolean isAnimated() {
565 return mDecoder.mAnimated;
566 }
Leon Scroggins III1a69f452018-03-29 09:48:47 -0400567
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 }
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400579 };
580
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400581 /** @removed
582 * @deprecated Subsumed by {@link #DecodeException}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400583 */
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400584 @java.lang.Deprecated
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500585 public static class IncompleteException extends IOException {};
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400586
587 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400588 * Interface for changing the default settings of a decode.
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400589 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400590 * <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.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400597 */
598 public static interface OnHeaderDecodedListener {
599 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400600 * Called by {@link ImageDecoder} when the header has been decoded and
601 * the image size is known.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400602 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400603 * @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}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400607 */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500608 public void onHeaderDecoded(@NonNull ImageDecoder decoder,
609 @NonNull ImageInfo info, @NonNull Source source);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400610
611 };
612
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400613 /** @removed
614 * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500615 */
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400616 @java.lang.Deprecated
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500617 public static final int ERROR_SOURCE_EXCEPTION = 1;
618
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400619 /** @removed
620 * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500621 */
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400622 @java.lang.Deprecated
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500623 public static final int ERROR_SOURCE_INCOMPLETE = 2;
624
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400625 /** @removed
626 * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500627 */
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400628 @java.lang.Deprecated
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500629 public static final int ERROR_SOURCE_ERROR = 3;
630
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500631 /**
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400632 * Information about an interrupted decode.
633 */
634 public static final class DecodeException extends IOException {
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400635 /**
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
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400656 @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 *
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400679 * <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400680 * {@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 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400689 * Retrieve the {@link Source Source} that was interrupted.
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400690 *
691 * <p>This can be used for equality checking to find the Source which
692 * failed to completely decode.</p>
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400693 */
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) {
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400701 case SOURCE_EXCEPTION:
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400702 return "Exception in input: " + cause;
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400703 case SOURCE_INCOMPLETE:
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400704 return "Input was incomplete.";
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -0400705 case SOURCE_MALFORMED_DATA:
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400706 return "Input contained an error.";
707 default:
708 return "";
709 }
710 }
711 }
712
713 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400714 * Interface for inspecting a {@link DecodeException DecodeException}
715 * and potentially preventing it from being thrown.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500716 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400717 * <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.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400724 */
Leon Scroggins IIIedf26d62018-01-09 16:55:24 -0500725 public static interface OnPartialImageListener {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400726 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400727 * Called by {@link ImageDecoder} when there is only a partial image to
728 * display.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400729 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400730 * <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}.
Leon Scroggins IIIedf26d62018-01-09 16:55:24 -0500735 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400736 * @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.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400742 */
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400743 boolean onPartialImage(@NonNull DecodeException exception);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400744 };
745
746 // Fields
Leon Scroggins III671cce22018-01-14 16:52:17 -0500747 private long mNativePtr;
748 private final int mWidth;
749 private final int mHeight;
750 private final boolean mAnimated;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400751
Leon Scroggins III1a69f452018-03-29 09:48:47 -0400752 private int mDesiredWidth;
753 private int mDesiredHeight;
754 private int mAllocator = ALLOCATOR_DEFAULT;
Leon Scroggins IIId62f2722018-03-29 14:52:29 -0400755 private boolean mUnpremultipliedRequired = false;
Leon Scroggins III1a69f452018-03-29 09:48:47 -0400756 private boolean mMutable = false;
757 private boolean mConserveMemory = false;
758 private boolean mDecodeAsAlphaMask = false;
759 private ColorSpace mDesiredColorSpace = null;
760 private Rect mCropRect;
761 private Rect mOutPaddingRect;
762 private Source mSource;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400763
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500764 private PostProcessor mPostProcessor;
Leon Scroggins IIIedf26d62018-01-09 16:55:24 -0500765 private OnPartialImageListener mOnPartialImageListener;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400766
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500767 // Objects for interacting with the input.
768 private InputStream mInputStream;
Leon Scroggins III0b49f572018-01-23 11:38:23 -0500769 private boolean mOwnsInputStream;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500770 private byte[] mTempStorage;
771 private AssetFileDescriptor mAssetFd;
772 private final AtomicBoolean mClosed = new AtomicBoolean();
773 private final CloseGuard mCloseGuard = CloseGuard.get();
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400774
775 /**
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500776 * Private constructor called by JNI. {@link #close} must be
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400777 * called after decoding to delete native resources.
778 */
779 @SuppressWarnings("unused")
Leon Scroggins III671cce22018-01-14 16:52:17 -0500780 private ImageDecoder(long nativePtr, int width, int height,
781 boolean animated) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400782 mNativePtr = nativePtr;
783 mWidth = width;
784 mHeight = height;
785 mDesiredWidth = width;
786 mDesiredHeight = height;
Leon Scroggins III671cce22018-01-14 16:52:17 -0500787 mAnimated = animated;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500788 mCloseGuard.open("close");
789 }
790
791 @Override
792 protected void finalize() throws Throwable {
793 try {
794 if (mCloseGuard != null) {
795 mCloseGuard.warnIfOpen();
796 }
797
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500798 // Avoid closing these in finalizer.
799 mInputStream = null;
800 mAssetFd = null;
801
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500802 close();
803 } finally {
804 super.finalize();
805 }
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400806 }
807
808 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400809 * Create a new {@link Source Source} from a resource.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400810 *
811 * @param res the {@link Resources} object containing the image data.
812 * @param resId resource ID of the image data.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400813 * @return a new Source object, which can be passed to
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400814 * {@link #decodeDrawable decodeDrawable} or
815 * {@link #decodeBitmap decodeBitmap}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400816 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400817 @AnyThread
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500818 @NonNull
Leon Scroggins III1eccac82018-03-16 15:04:04 -0400819 public static Source createSource(@NonNull Resources res, int resId)
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500820 {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400821 return new ResourceSource(res, resId);
822 }
823
824 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400825 * Create a new {@link Source Source} from a {@link android.net.Uri}.
826 *
827 * <h5>Accepts the following URI schemes:</h5>
828 * <ul>
829 * <li>content ({@link ContentResolver#SCHEME_CONTENT})</li>
830 * <li>android.resource ({@link ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
831 * <li>file ({@link ContentResolver#SCHEME_FILE})</li>
832 * </ul>
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500833 *
834 * @param cr to retrieve from.
835 * @param uri of the image file.
836 * @return a new Source object, which can be passed to
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400837 * {@link #decodeDrawable decodeDrawable} or
838 * {@link #decodeBitmap decodeBitmap}.
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500839 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400840 @AnyThread
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500841 @NonNull
842 public static Source createSource(@NonNull ContentResolver cr,
843 @NonNull Uri uri) {
Leon Scroggins III046a99e2018-01-31 14:59:29 -0500844 return new ContentResolverSource(cr, uri, null);
845 }
846
847 /**
848 * Provide Resources for density scaling.
849 *
850 * @hide
851 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400852 @AnyThread
Leon Scroggins III046a99e2018-01-31 14:59:29 -0500853 @NonNull
854 public static Source createSource(@NonNull ContentResolver cr,
855 @NonNull Uri uri, @Nullable Resources res) {
856 return new ContentResolverSource(cr, uri, res);
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500857 }
858
859 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400860 * Create a new {@link Source Source} from a file in the "assets" directory.
Leon Scroggins III121ef982018-03-16 15:40:54 -0400861 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400862 @AnyThread
Leon Scroggins III121ef982018-03-16 15:40:54 -0400863 @NonNull
864 public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) {
865 return new AssetSource(assets, fileName);
866 }
867
868 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400869 * Create a new {@link Source Source} from a byte array.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500870 *
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400871 * @param data byte array of compressed image data.
872 * @param offset offset into data for where the decoder should begin
873 * parsing.
874 * @param length number of bytes, beginning at offset, to parse.
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400875 * @return a new Source object, which can be passed to
876 * {@link #decodeDrawable decodeDrawable} or
877 * {@link #decodeBitmap decodeBitmap}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400878 * @throws NullPointerException if data is null.
879 * @throws ArrayIndexOutOfBoundsException if offset and length are
880 * not within data.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500881 * @hide
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400882 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400883 @AnyThread
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500884 @NonNull
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400885 public static Source createSource(@NonNull byte[] data, int offset,
886 int length) throws ArrayIndexOutOfBoundsException {
887 if (data == null) {
888 throw new NullPointerException("null byte[] in createSource!");
889 }
890 if (offset < 0 || length < 0 || offset >= data.length ||
891 offset + length > data.length) {
892 throw new ArrayIndexOutOfBoundsException(
893 "invalid offset/length!");
894 }
895 return new ByteArraySource(data, offset, length);
896 }
897
898 /**
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -0500899 * See {@link #createSource(byte[], int, int).
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500900 * @hide
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -0500901 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400902 @AnyThread
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500903 @NonNull
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -0500904 public static Source createSource(@NonNull byte[] data) {
905 return createSource(data, 0, data.length);
906 }
907
908 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400909 * Create a new {@link Source Source} from a {@link java.nio.ByteBuffer}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400910 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400911 * <p>Decoding will start from {@link java.nio.ByteBuffer#position() buffer.position()}.
912 * The position of {@code buffer} will not be affected.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400913 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400914 * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
915 * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
Leon Scroggins III43675342018-02-23 14:01:09 -0500916 * will continue reading from the {@code buffer}, so its contents must not
917 * be modified, even after the {@code AnimatedImageDrawable} is returned.
918 * {@code buffer}'s contents should never be modified during decode.</p>
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400919 *
920 * @return a new Source object, which can be passed to
921 * {@link #decodeDrawable decodeDrawable} or
922 * {@link #decodeBitmap decodeBitmap}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400923 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400924 @AnyThread
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500925 @NonNull
926 public static Source createSource(@NonNull ByteBuffer buffer) {
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400927 return new ByteBufferSource(buffer);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400928 }
929
930 /**
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500931 * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400932 *
933 * <p>Unlike other Sources, this one cannot be reused.</p>
934 *
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500935 * @hide
936 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400937 @AnyThread
938 @NonNull
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500939 public static Source createSource(Resources res, InputStream is) {
940 return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
941 }
942
943 /**
944 * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400945 *
946 * <p>Unlike other Sources, this one cannot be reused.</p>
947 *
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500948 * @hide
949 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400950 @AnyThread
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -0400951 @TestApi
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400952 @NonNull
Derek Sollenberger66c6d782018-01-12 16:40:52 -0500953 public static Source createSource(Resources res, InputStream is, int density) {
954 return new InputStreamSource(res, is, density);
955 }
956
957 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -0400958 * Create a new {@link Source Source} from a {@link java.io.File}.
959 *
960 * @return a new Source object, which can be passed to
961 * {@link #decodeDrawable decodeDrawable} or
962 * {@link #decodeBitmap decodeBitmap}.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500963 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -0400964 @AnyThread
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500965 @NonNull
966 public static Source createSource(@NonNull File file) {
967 return new FileSource(file);
968 }
969
970 /**
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400971 * Return the width and height of a given sample size.
972 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500973 * <p>This takes an input that functions like
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400974 * {@link BitmapFactory.Options#inSampleSize}. It returns a width and
Leon Scroggins III64481192018-03-28 09:17:42 -0400975 * height that can be achieved by sampling the encoded image. Other widths
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400976 * and heights may be supported, but will require an additional (internal)
977 * scaling step. Such internal scaling is *not* supported with
Leon Scroggins IIId62f2722018-03-29 14:52:29 -0400978 * {@link #setUnpremultipliedRequired} set to {@code true}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400979 *
980 * @param sampleSize Sampling rate of the encoded image.
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500981 * @return {@link android.util.Size} of the width and height after
982 * sampling.
Leon Scroggins III64481192018-03-28 09:17:42 -0400983 *
984 * @hide
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400985 */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -0500986 @NonNull
987 public Size getSampledSize(int sampleSize) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400988 if (sampleSize <= 0) {
989 throw new IllegalArgumentException("sampleSize must be positive! "
990 + "provided " + sampleSize);
991 }
992 if (mNativePtr == 0) {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -0500993 throw new IllegalStateException("ImageDecoder is closed!");
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400994 }
995
996 return nGetSampledSize(mNativePtr, sampleSize);
997 }
998
999 // Modifiers
Leon Scroggins III64481192018-03-28 09:17:42 -04001000 /** @removed
1001 * @deprecated Renamed to {@link #setTargetSize}.
1002 */
1003 @java.lang.Deprecated
1004 public ImageDecoder setResize(int width, int height) {
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001005 this.setTargetSize(width, height);
1006 return this;
Leon Scroggins III64481192018-03-28 09:17:42 -04001007 }
1008
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001009 /**
Leon Scroggins III64481192018-03-28 09:17:42 -04001010 * Specify the size of the output {@link Drawable} or {@link Bitmap}.
1011 *
1012 * <p>By default, the output size will match the size of the encoded
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001013 * image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1014 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
1015 *
1016 * <p>This will sample or scale the output to an arbitrary size that may
1017 * be smaller or larger than the encoded size.</p>
Leon Scroggins III64481192018-03-28 09:17:42 -04001018 *
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001019 * <p>Only the last call to this or {@link #setTargetSampleSize} is
1020 * respected.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001021 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001022 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001023 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001024 *
Leon Scroggins IIId97e2642018-04-03 11:55:55 -04001025 * @param width width in pixels of the output, must be greater than 0
1026 * @param height height in pixels of the output, must be greater than 0
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001027 */
Leon Scroggins IIId97e2642018-04-03 11:55:55 -04001028 public void setTargetSize(@Px @IntRange(from = 1) int width,
1029 @Px @IntRange(from = 1) int height) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001030 if (width <= 0 || height <= 0) {
1031 throw new IllegalArgumentException("Dimensions must be positive! "
1032 + "provided (" + width + ", " + height + ")");
1033 }
1034
1035 mDesiredWidth = width;
1036 mDesiredHeight = height;
1037 }
1038
Leon Scroggins III64481192018-03-28 09:17:42 -04001039 /** @removed
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001040 * @deprecated Renamed to {@link #setTargetSampleSize}.
Leon Scroggins III64481192018-03-28 09:17:42 -04001041 */
1042 @java.lang.Deprecated
1043 public ImageDecoder setResize(int sampleSize) {
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001044 this.setTargetSampleSize(sampleSize);
1045 return this;
Leon Scroggins III64481192018-03-28 09:17:42 -04001046 }
1047
1048 private int getTargetDimension(int original, int sampleSize, int computed) {
1049 // Sampling will never result in a smaller size than 1.
1050 if (sampleSize >= original) {
1051 return 1;
1052 }
1053
1054 // Use integer divide to find the desired size. If that is what
1055 // getSampledSize computed, that is the size to use.
1056 int target = original / sampleSize;
1057 if (computed == target) {
1058 return computed;
1059 }
1060
1061 // If sampleSize does not divide evenly into original, the decoder
1062 // may round in either direction. It just needs to get a result that
1063 // is close.
1064 int reverse = computed * sampleSize;
1065 if (Math.abs(reverse - original) < sampleSize) {
1066 // This is the size that can be decoded most efficiently.
1067 return computed;
1068 }
1069
1070 // The decoder could not get close (e.g. it is a DNG image).
1071 return target;
1072 }
1073
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001074 /**
Leon Scroggins III64481192018-03-28 09:17:42 -04001075 * Set the target size with a sampleSize.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001076 *
Leon Scroggins III64481192018-03-28 09:17:42 -04001077 * <p>By default, the output size will match the size of the encoded
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001078 * image, which can be retrieved from the {@link ImageInfo ImageInfo} in
1079 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III64481192018-03-28 09:17:42 -04001080 *
1081 * <p>Requests the decoder to subsample the original image, returning a
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001082 * smaller image to save memory. The {@code sampleSize} is the number of pixels
Leon Scroggins III64481192018-03-28 09:17:42 -04001083 * in either dimension that correspond to a single pixel in the output.
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001084 * For example, {@code sampleSize == 4} returns an image that is 1/4 the
Leon Scroggins III64481192018-03-28 09:17:42 -04001085 * width/height of the original, and 1/16 the number of pixels.</p>
1086 *
1087 * <p>Must be greater than or equal to 1.</p>
1088 *
Leon Scroggins IIId97e2642018-04-03 11:55:55 -04001089 * <p>This has the same effect as calling {@link #setTargetSize} with
1090 * dimensions based on the {@code sampleSize}. Unlike dividing the original
1091 * width and height by the {@code sampleSize} manually, calling this method
1092 * allows {@code ImageDecoder} to round in the direction that it can do most
1093 * efficiently.</p>
1094 *
Leon Scroggins III64481192018-03-28 09:17:42 -04001095 * <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001096 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001097 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001098 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001099 *
Leon Scroggins IIId97e2642018-04-03 11:55:55 -04001100 * @param sampleSize sampling rate of the encoded image.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001101 */
Leon Scroggins IIId97e2642018-04-03 11:55:55 -04001102 public void setTargetSampleSize(@IntRange(from = 1) int sampleSize) {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001103 Size size = this.getSampledSize(sampleSize);
Leon Scroggins III64481192018-03-28 09:17:42 -04001104 int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
1105 int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001106 this.setTargetSize(targetWidth, targetHeight);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001107 }
1108
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001109 private boolean requestedResize() {
1110 return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
1111 }
1112
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001113 // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
1114 /**
1115 * Use the default allocation for the pixel memory.
1116 *
1117 * Will typically result in a {@link Bitmap.Config#HARDWARE}
1118 * allocation, but may be software for small images. In addition, this will
1119 * switch to software when HARDWARE is incompatible, e.g.
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001120 * {@link #setMutableRequired setMutableRequired(true)} or
1121 * {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001122 */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001123 public static final int ALLOCATOR_DEFAULT = 0;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001124
1125 /**
1126 * Use a software allocation for the pixel memory.
1127 *
1128 * Useful for drawing to a software {@link Canvas} or for
1129 * accessing the pixels on the final output.
1130 */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001131 public static final int ALLOCATOR_SOFTWARE = 1;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001132
1133 /**
1134 * Use shared memory for the pixel memory.
1135 *
1136 * Useful for sharing across processes.
1137 */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001138 public static final int ALLOCATOR_SHARED_MEMORY = 2;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001139
1140 /**
1141 * Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
1142 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001143 * When this is combined with incompatible options, like
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001144 * {@link #setMutableRequired setMutableRequired(true)} or
1145 * {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)},
1146 * {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}
1147 * will throw an {@link java.lang.IllegalStateException}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001148 */
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001149 public static final int ALLOCATOR_HARDWARE = 3;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001150
1151 /** @hide **/
1152 @Retention(SOURCE)
Leon Scroggins III62f08002018-02-26 12:32:52 -05001153 @IntDef(value = { ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE,
1154 ALLOCATOR_SHARED_MEMORY, ALLOCATOR_HARDWARE },
1155 prefix = {"ALLOCATOR_"})
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001156 public @interface Allocator {};
1157
1158 /**
1159 * Choose the backing for the pixel memory.
1160 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001161 * <p>This is ignored for animated drawables.</p>
1162 *
1163 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001164 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001165 *
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001166 * @param allocator Type of allocator to use.
1167 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001168 public void setAllocator(@Allocator int allocator) {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001169 if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001170 throw new IllegalArgumentException("invalid allocator " + allocator);
1171 }
1172 mAllocator = allocator;
1173 }
1174
1175 /**
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001176 * Return the allocator for the pixel memory.
1177 */
1178 @Allocator
1179 public int getAllocator() {
1180 return mAllocator;
1181 }
1182
1183 /**
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001184 * Specify whether the {@link Bitmap} should have unpremultiplied pixels.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001185 *
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001186 * <p>By default, ImageDecoder will create a {@link Bitmap} with
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001187 * premultiplied pixels, which is required for drawing with the
1188 * {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001189 * this method with a value of {@code true} will result in
1190 * {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001191 * pixels. See {@link Bitmap#isPremultiplied Bitmap.isPremultiplied()}.
1192 * This is incompatible with {@link #decodeDrawable decodeDrawable};
1193 * attempting to decode an unpremultiplied {@link Drawable} will throw an
1194 * {@link java.lang.IllegalStateException}. </p>
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001195 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001196 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001197 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001198 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001199 public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001200 mUnpremultipliedRequired = unpremultipliedRequired;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001201 }
1202
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001203 /** @removed
1204 * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
1205 */
1206 @java.lang.Deprecated
1207 public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001208 this.setUnpremultipliedRequired(unpremultipliedRequired);
1209 return this;
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001210 }
1211
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001212 /**
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001213 * Return whether the {@link Bitmap} will have unpremultiplied pixels.
1214 */
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001215 public boolean isUnpremultipliedRequired() {
1216 return mUnpremultipliedRequired;
1217 }
1218
1219 /** @removed
1220 * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
1221 */
1222 @java.lang.Deprecated
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001223 public boolean getRequireUnpremultiplied() {
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001224 return this.isUnpremultipliedRequired();
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001225 }
1226
1227 /**
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001228 * Modify the image after decoding and scaling.
1229 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001230 * <p>This allows adding effects prior to returning a {@link Drawable} or
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001231 * {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001232 * this is the only way to process the image after decoding.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001233 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001234 * <p>If combined with {@link #setTargetSize} and/or {@link #setCrop},
1235 * {@link PostProcessor#onPostProcess} occurs last.</p>
1236 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001237 * <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001238 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001239 * <p>For an animated image, the drawing commands drawn on the
1240 * {@link Canvas} will be recorded immediately and then applied to each
1241 * frame.</p>
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001242 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001243 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001244 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001245 *
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001246 */
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001247 public void setPostProcessor(@Nullable PostProcessor postProcessor) {
1248 mPostProcessor = postProcessor;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001249 }
1250
1251 /**
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001252 * Return the {@link PostProcessor} currently set.
1253 */
1254 @Nullable
1255 public PostProcessor getPostProcessor() {
1256 return mPostProcessor;
1257 }
1258
1259 /**
Leon Scroggins IIIedf26d62018-01-09 16:55:24 -05001260 * Set (replace) the {@link OnPartialImageListener} on this object.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001261 *
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001262 * <p>Will be called if there is an error in the input. Without one, an
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001263 * error will result in an {@code Exception} being thrown.</p>
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001264 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001265 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001266 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001267 *
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001268 */
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001269 public void setOnPartialImageListener(@Nullable OnPartialImageListener listener) {
1270 mOnPartialImageListener = listener;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001271 }
1272
1273 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001274 * Return the {@link OnPartialImageListener OnPartialImageListener} currently set.
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001275 */
1276 @Nullable
1277 public OnPartialImageListener getOnPartialImageListener() {
1278 return mOnPartialImageListener;
1279 }
1280
1281 /**
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001282 * Crop the output to {@code subset} of the (possibly) scaled image.
1283 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001284 * <p>{@code subset} must be contained within the size set by
Leon Scroggins III64481192018-03-28 09:17:42 -04001285 * {@link #setTargetSize} or the bounds of the image if setTargetSize was
1286 * not called. Otherwise an {@link IllegalStateException} will be thrown by
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001287 * {@link #decodeDrawable decodeDrawable}/{@link #decodeBitmap decodeBitmap}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001288 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001289 * <p>NOT intended as a replacement for
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001290 * {@link BitmapRegionDecoder#decodeRegion BitmapRegionDecoder.decodeRegion()}.
1291 * This supports all formats, but merely crops the output.</p>
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001292 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001293 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001294 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001295 *
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001296 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001297 public void setCrop(@Nullable Rect subset) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001298 mCropRect = subset;
1299 }
1300
1301 /**
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001302 * Return the cropping rectangle, if set.
1303 */
1304 @Nullable
1305 public Rect getCrop() {
1306 return mCropRect;
1307 }
1308
1309 /**
Leon Scroggins III2f1b06b2018-02-15 10:40:33 -05001310 * Set a Rect for retrieving nine patch padding.
1311 *
1312 * If the image is a nine patch, this Rect will be set to the padding
1313 * rectangle during decode. Otherwise it will not be modified.
1314 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001315 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001316 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001317 *
Leon Scroggins III2f1b06b2018-02-15 10:40:33 -05001318 * @hide
1319 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001320 public void setOutPaddingRect(@NonNull Rect outPadding) {
Leon Scroggins III2f1b06b2018-02-15 10:40:33 -05001321 mOutPaddingRect = outPadding;
1322 }
1323
1324 /**
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001325 * Specify whether the {@link Bitmap} should be mutable.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001326 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001327 * <p>By default, a {@link Bitmap} created by {@link #decodeBitmap decodeBitmap}
1328 * will be immutable i.e. {@link Bitmap#isMutable() Bitmap.isMutable()} returns
1329 * {@code false}. This can be changed with {@code setMutableRequired(true)}.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001330 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001331 * <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
1332 * because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
1333 * Attempting to combine them will throw an
1334 * {@link java.lang.IllegalStateException}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001335 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001336 * <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable decodeDrawable},
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001337 * which would require retrieving the Bitmap from the returned Drawable in
1338 * order to modify. Attempting to decode a mutable {@link Drawable} will
1339 * throw an {@link java.lang.IllegalStateException}.</p>
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001340 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001341 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001342 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001343 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001344 public void setMutableRequired(boolean mutable) {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001345 mMutable = mutable;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001346 }
1347
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001348 /** @removed
1349 * @deprecated Renamed to {@link #setMutableRequired}.
1350 */
1351 @java.lang.Deprecated
1352 public ImageDecoder setMutable(boolean mutable) {
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001353 this.setMutableRequired(mutable);
1354 return this;
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001355 }
1356
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001357 /**
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001358 * Return whether the decoded {@link Bitmap} will be mutable.
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001359 */
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001360 public boolean isMutableRequired() {
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001361 return mMutable;
1362 }
1363
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001364 /** @removed
1365 * @deprecated Renamed to {@link #isMutableRequired}.
1366 */
1367 @java.lang.Deprecated
1368 public boolean getMutable() {
1369 return this.isMutableRequired();
1370 }
1371
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001372 /**
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001373 * Specify whether to potentially save RAM at the expense of quality.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001374 *
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001375 * <p>Setting this to {@code true} may result in a {@link Bitmap} with a
Leon Scroggins IIIa38b2132018-02-23 12:27:27 -05001376 * denser {@link Bitmap.Config}, depending on the image. For example, an
1377 * opaque {@link Bitmap} with 8 bits or precision for each of its red,
1378 * green and blue components would decode to
1379 * {@link Bitmap.Config#ARGB_8888} by default, but setting this to
1380 * {@code true} will result in decoding to {@link Bitmap.Config#RGB_565}.
1381 * This necessarily lowers the quality of the output, but saves half
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001382 * the memory used.</p>
1383 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001384 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001385 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001386 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001387 public void setConserveMemory(boolean conserveMemory) {
Leon Scroggins IIIa38b2132018-02-23 12:27:27 -05001388 mConserveMemory = conserveMemory;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001389 }
1390
1391 /**
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001392 * Return whether this object will try to save RAM at the expense of quality.
1393 *
1394 * <p>This returns whether {@link #setConserveMemory} was set to {@code true}.
1395 * It may still return {@code true} even if the {@code ImageDecoder} does not
1396 * have a way to save RAM at the expense of quality for this image.</p>
1397 */
1398 public boolean getConserveMemory() {
1399 return mConserveMemory;
1400 }
1401
1402 /**
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001403 * Specify whether to potentially treat the output as an alpha mask.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001404 *
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001405 * <p>If this is set to {@code true} and the image is encoded in a format
1406 * with only one channel, treat that channel as alpha. Otherwise this call has
1407 * no effect.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001408 *
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001409 * <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001410 * combine them will result in {@link #decodeDrawable decodeDrawable}/
1411 * {@link #decodeBitmap decodeBitmap} throwing an
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001412 * {@link java.lang.IllegalStateException}.</p>
Leon Scroggins III5f7e9482018-02-23 13:22:29 -05001413 *
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001414 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001415 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001416 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001417 public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001418 mDecodeAsAlphaMask = enabled;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001419 }
1420
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001421 /** @removed
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001422 * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
1423 */
1424 @java.lang.Deprecated
1425 public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001426 this.setDecodeAsAlphaMaskEnabled(enabled);
1427 return this;
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001428 }
1429
1430 /** @removed
1431 * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001432 */
1433 @java.lang.Deprecated
1434 public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001435 this.setDecodeAsAlphaMask(asAlphaMask);
1436 return this;
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001437 }
1438
1439 /**
1440 * Return whether to treat single channel input as alpha.
1441 *
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001442 * <p>This returns whether {@link #setDecodeAsAlphaMaskEnabled} was set to
1443 * {@code true}. It may still return {@code true} even if the image has
1444 * more than one channel and therefore will not be treated as an alpha
1445 * mask.</p>
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001446 */
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001447 public boolean isDecodeAsAlphaMaskEnabled() {
1448 return mDecodeAsAlphaMask;
1449 }
1450
1451 /** @removed
1452 * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
1453 */
1454 @java.lang.Deprecated
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001455 public boolean getDecodeAsAlphaMask() {
1456 return mDecodeAsAlphaMask;
1457 }
1458
1459 /** @removed
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001460 * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001461 */
1462 @java.lang.Deprecated
1463 public boolean getAsAlphaMask() {
1464 return this.getDecodeAsAlphaMask();
1465 }
1466
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001467 /**
1468 * Specify the desired {@link ColorSpace} for the output.
1469 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001470 * <p>If non-null, the decoder will try to decode into {@code colorSpace}.
1471 * If it is null, which is the default, or the request cannot be met, the
1472 * decoder will pick either the color space embedded in the image or the
1473 * {@link ColorSpace} best suited for the requested image configuration
1474 * (for instance {@link ColorSpace.Named#SRGB sRGB} for the
1475 * {@link Bitmap.Config#ARGB_8888} configuration).</p>
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001476 *
1477 * <p>{@link Bitmap.Config#RGBA_F16} always uses the
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001478 * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space.
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001479 * Bitmaps in other configurations without an embedded color space are
1480 * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
1481 *
1482 * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
1483 * currently supported. An <code>IllegalArgumentException</code> will
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001484 * be thrown by {@link #decodeDrawable decodeDrawable}/
1485 * {@link #decodeBitmap decodeBitmap} when setting a non-RGB color space
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001486 * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
1487 *
1488 * <p class="note">The specified color space's transfer function must be
1489 * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
1490 * <code>IllegalArgumentException</code> will be thrown by the decode methods
1491 * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
1492 * specified color space returns null.</p>
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001493 *
1494 * <p>Like all setters on ImageDecoder, this must be called inside
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001495 * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001496 */
Leon Scroggins IIId9b53a02018-03-27 07:47:07 -04001497 public void setTargetColorSpace(ColorSpace colorSpace) {
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001498 mDesiredColorSpace = colorSpace;
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001499 }
1500
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001501 /**
1502 * Closes this resource, relinquishing any underlying resources. This method
1503 * is invoked automatically on objects managed by the try-with-resources
1504 * statement.
1505 *
1506 * <p>This is an implementation detail of {@link ImageDecoder}, and should
1507 * never be called manually.</p>
1508 */
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001509 @Override
1510 public void close() {
1511 mCloseGuard.close();
1512 if (!mClosed.compareAndSet(false, true)) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001513 return;
1514 }
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001515 nClose(mNativePtr);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001516 mNativePtr = 0;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001517
Leon Scroggins III0b49f572018-01-23 11:38:23 -05001518 if (mOwnsInputStream) {
1519 IoUtils.closeQuietly(mInputStream);
1520 }
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001521 IoUtils.closeQuietly(mAssetFd);
1522
1523 mInputStream = null;
1524 mAssetFd = null;
1525 mTempStorage = null;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001526 }
1527
1528 private void checkState() {
1529 if (mNativePtr == 0) {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001530 throw new IllegalStateException("Cannot use closed ImageDecoder!");
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001531 }
1532
1533 checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
1534
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001535 if (mAllocator == ALLOCATOR_HARDWARE) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001536 if (mMutable) {
1537 throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
1538 }
Leon Scroggins III746a9fe2018-03-08 10:32:10 -05001539 if (mDecodeAsAlphaMask) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001540 throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
1541 }
1542 }
1543
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001544 if (mPostProcessor != null && mUnpremultipliedRequired) {
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001545 throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
1546 }
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001547
1548 if (mDesiredColorSpace != null) {
1549 if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) {
1550 throw new IllegalArgumentException("The target color space must use the "
1551 + "RGB color model - provided: " + mDesiredColorSpace);
1552 }
1553 if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) {
1554 throw new IllegalArgumentException("The target color space must use an "
1555 + "ICC parametric transfer function - provided: " + mDesiredColorSpace);
1556 }
1557 }
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001558 }
1559
1560 private static void checkSubset(int width, int height, Rect r) {
1561 if (r == null) {
1562 return;
1563 }
1564 if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
1565 throw new IllegalStateException("Subset " + r + " not contained by "
1566 + "scaled image bounds: (" + width + " x " + height + ")");
1567 }
1568 }
1569
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001570 @WorkerThread
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001571 @NonNull
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001572 private Bitmap decodeBitmapInternal() throws IOException {
Leon Scroggins III8c9d8f22018-01-14 14:41:46 -05001573 checkState();
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -04001574 return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
1575 mDesiredWidth, mDesiredHeight, mCropRect,
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001576 mMutable, mAllocator, mUnpremultipliedRequired,
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001577 mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace);
Leon Scroggins III8c9d8f22018-01-14 14:41:46 -05001578 }
1579
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001580 private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
1581 @NonNull Source src) {
1582 if (listener != null) {
1583 ImageInfo info = new ImageInfo(this);
1584 try {
1585 listener.onHeaderDecoded(this, info, src);
1586 } finally {
1587 info.mDecoder = null;
1588 }
1589 }
1590 }
1591
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001592 /**
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001593 * Create a {@link Drawable} from a {@code Source}.
1594 *
1595 * @param src representing the encoded image.
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001596 * @param listener for learning the {@link ImageInfo ImageInfo} and changing any
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001597 * default settings on the {@code ImageDecoder}. This will be called on
1598 * the same thread as {@code decodeDrawable} before that method returns.
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001599 * This is required in order to change any of the default settings.
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001600 * @return Drawable for displaying the image.
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001601 * @throws IOException if {@code src} is not found, is an unsupported
1602 * format, or cannot be decoded for any reason.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001603 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001604 @WorkerThread
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001605 @NonNull
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001606 public static Drawable decodeDrawable(@NonNull Source src,
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001607 @NonNull OnHeaderDecodedListener listener) throws IOException {
1608 if (listener == null) {
1609 throw new IllegalArgumentException("listener cannot be null! "
1610 + "Use decodeDrawable(Source) to not have a listener");
1611 }
1612 return decodeDrawableImpl(src, listener);
1613 }
1614
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001615 @WorkerThread
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001616 @NonNull
1617 private static Drawable decodeDrawableImpl(@NonNull Source src,
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001618 @Nullable OnHeaderDecodedListener listener) throws IOException {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001619 try (ImageDecoder decoder = src.createImageDecoder()) {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001620 decoder.mSource = src;
1621 decoder.callHeaderDecoded(listener, src);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001622
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001623 if (decoder.mUnpremultipliedRequired) {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001624 // Though this could be supported (ignored) for opaque images,
1625 // it seems better to always report this error.
1626 throw new IllegalStateException("Cannot decode a Drawable " +
1627 "with unpremultiplied pixels!");
1628 }
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001629
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001630 if (decoder.mMutable) {
1631 throw new IllegalStateException("Cannot decode a mutable " +
1632 "Drawable!");
1633 }
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001634
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001635 // this call potentially manipulates the decoder so it must be performed prior to
1636 // decoding the bitmap and after decode set the density on the resulting bitmap
1637 final int srcDensity = computeDensity(src, decoder);
Leon Scroggins III671cce22018-01-14 16:52:17 -05001638 if (decoder.mAnimated) {
1639 // AnimatedImageDrawable calls postProcessAndRelease only if
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001640 // mPostProcessor exists.
1641 ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
Leon Scroggins III671cce22018-01-14 16:52:17 -05001642 null : decoder;
1643 Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
1644 postProcessPtr, decoder.mDesiredWidth,
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001645 decoder.mDesiredHeight, srcDensity,
1646 src.computeDstDensity(), decoder.mCropRect,
Leon Scroggins III671cce22018-01-14 16:52:17 -05001647 decoder.mInputStream, decoder.mAssetFd);
1648 // d has taken ownership of these objects.
1649 decoder.mInputStream = null;
1650 decoder.mAssetFd = null;
1651 return d;
1652 }
1653
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001654 Bitmap bm = decoder.decodeBitmapInternal();
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001655 bm.setDensity(srcDensity);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001656
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001657 Resources res = src.getResources();
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001658 byte[] np = bm.getNinePatchChunk();
1659 if (np != null && NinePatch.isNinePatchChunk(np)) {
1660 Rect opticalInsets = new Rect();
1661 bm.getOpticalInsets(opticalInsets);
Leon Scroggins III2f1b06b2018-02-15 10:40:33 -05001662 Rect padding = decoder.mOutPaddingRect;
1663 if (padding == null) {
1664 padding = new Rect();
1665 }
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001666 nGetPadding(decoder.mNativePtr, padding);
1667 return new NinePatchDrawable(res, bm, np, padding,
1668 opticalInsets, null);
1669 }
1670
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001671 return new BitmapDrawable(res, bm);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001672 }
1673 }
1674
1675 /**
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001676 * Create a {@link Drawable} from a {@code Source}.
1677 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001678 * <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
1679 * the default settings will be used. In order to change any settings, call
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001680 * {@link #decodeDrawable(Source, OnHeaderDecodedListener)} instead.</p>
1681 *
1682 * @param src representing the encoded image.
1683 * @return Drawable for displaying the image.
1684 * @throws IOException if {@code src} is not found, is an unsupported
1685 * format, or cannot be decoded for any reason.
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001686 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001687 @WorkerThread
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001688 @NonNull
1689 public static Drawable decodeDrawable(@NonNull Source src)
1690 throws IOException {
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001691 return decodeDrawableImpl(src, null);
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001692 }
1693
1694 /**
1695 * Create a {@link Bitmap} from a {@code Source}.
1696 *
1697 * @param src representing the encoded image.
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001698 * @param listener for learning the {@link ImageInfo ImageInfo} and changing any
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001699 * default settings on the {@code ImageDecoder}. This will be called on
1700 * the same thread as {@code decodeBitmap} before that method returns.
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001701 * This is required in order to change any of the default settings.
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001702 * @return Bitmap containing the image.
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001703 * @throws IOException if {@code src} is not found, is an unsupported
1704 * format, or cannot be decoded for any reason.
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001705 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001706 @WorkerThread
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001707 @NonNull
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001708 public static Bitmap decodeBitmap(@NonNull Source src,
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001709 @NonNull OnHeaderDecodedListener listener) throws IOException {
1710 if (listener == null) {
1711 throw new IllegalArgumentException("listener cannot be null! "
1712 + "Use decodeBitmap(Source) to not have a listener");
1713 }
1714 return decodeBitmapImpl(src, listener);
1715 }
1716
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001717 @WorkerThread
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001718 @NonNull
1719 private static Bitmap decodeBitmapImpl(@NonNull Source src,
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001720 @Nullable OnHeaderDecodedListener listener) throws IOException {
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001721 try (ImageDecoder decoder = src.createImageDecoder()) {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001722 decoder.mSource = src;
1723 decoder.callHeaderDecoded(listener, src);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001724
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001725 // this call potentially manipulates the decoder so it must be performed prior to
1726 // decoding the bitmap
1727 final int srcDensity = computeDensity(src, decoder);
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001728 Bitmap bm = decoder.decodeBitmapInternal();
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001729 bm.setDensity(srcDensity);
Leon Scroggins III2f1b06b2018-02-15 10:40:33 -05001730
1731 Rect padding = decoder.mOutPaddingRect;
1732 if (padding != null) {
1733 byte[] np = bm.getNinePatchChunk();
1734 if (np != null && NinePatch.isNinePatchChunk(np)) {
1735 nGetPadding(decoder.mNativePtr, padding);
1736 }
1737 }
1738
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001739 return bm;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001740 }
1741 }
1742
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001743 // This method may modify the decoder so it must be called prior to performing the decode
1744 private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) {
1745 // if the caller changed the size then we treat the density as unknown
1746 if (decoder.requestedResize()) {
1747 return Bitmap.DENSITY_NONE;
1748 }
1749
1750 // Special stuff for compatibility mode: if the target density is not
1751 // the same as the display density, but the resource -is- the same as
1752 // the display density, then don't scale it down to the target density.
1753 // This allows us to load the system's density-correct resources into
1754 // an application in compatibility mode, without scaling those down
1755 // to the compatibility density only to have them scaled back up when
1756 // drawn to the screen.
1757 Resources res = src.getResources();
1758 final int srcDensity = src.getDensity();
1759 if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
1760 return srcDensity;
1761 }
1762
Leon Scroggins III8290eab2018-03-19 10:51:44 -04001763 // For P and above, only resize if it would be a downscale. Scale up prior
1764 // to P in case the app relies on the Bitmap's size without considering density.
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001765 final int dstDensity = src.computeDstDensity();
Leon Scroggins III8290eab2018-03-19 10:51:44 -04001766 if (srcDensity == Bitmap.DENSITY_NONE || srcDensity == dstDensity
1767 || (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P)) {
1768 return srcDensity;
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001769 }
1770
Leon Scroggins III8290eab2018-03-19 10:51:44 -04001771 float scale = (float) dstDensity / srcDensity;
1772 int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
1773 int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
Leon Scroggins III64481192018-03-28 09:17:42 -04001774 decoder.setTargetSize(scaledWidth, scaledHeight);
Leon Scroggins III8290eab2018-03-19 10:51:44 -04001775 return dstDensity;
Derek Sollenberger66c6d782018-01-12 16:40:52 -05001776 }
1777
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001778 @NonNull
Leon Scroggins III1fad09d2017-12-22 12:54:20 -05001779 private String getMimeType() {
1780 return nGetMimeType(mNativePtr);
1781 }
1782
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001783 @Nullable
1784 private ColorSpace getColorSpace() {
1785 return nGetColorSpace(mNativePtr);
1786 }
1787
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001788 /**
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001789 * Create a {@link Bitmap} from a {@code Source}.
1790 *
Leon Scroggins IIId84c6522018-03-30 14:56:07 -04001791 * <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
1792 * the default settings will be used. In order to change any settings, call
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001793 * {@link #decodeBitmap(Source, OnHeaderDecodedListener)} instead.</p>
1794 *
1795 * @param src representing the encoded image.
1796 * @return Bitmap containing the image.
1797 * @throws IOException if {@code src} is not found, is an unsupported
1798 * format, or cannot be decoded for any reason.
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001799 */
Leon Scroggins III0a87cb32018-03-29 15:54:59 -04001800 @WorkerThread
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001801 @NonNull
1802 public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
Leon Scroggins IIIc9aa8562018-02-26 10:28:03 -05001803 return decodeBitmapImpl(src, null);
Leon Scroggins IIIb1cc8e62017-12-22 11:56:59 -05001804 }
1805
Leon Scroggins III8c9d8f22018-01-14 14:41:46 -05001806 /**
1807 * Private method called by JNI.
1808 */
1809 @SuppressWarnings("unused")
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001810 private int postProcessAndRelease(@NonNull Canvas canvas) {
Leon Scroggins III8c9d8f22018-01-14 14:41:46 -05001811 try {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001812 return mPostProcessor.onPostProcess(canvas);
Leon Scroggins III8c9d8f22018-01-14 14:41:46 -05001813 } finally {
1814 canvas.release();
1815 }
1816 }
1817
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001818 /**
1819 * Private method called by JNI.
1820 */
1821 @SuppressWarnings("unused")
Leon Scroggins IIIcf7294f2018-03-22 09:21:29 -04001822 private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause)
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -04001823 throws DecodeException {
1824 DecodeException exception = new DecodeException(error, cause, mSource);
1825 if (mOnPartialImageListener == null
1826 || !mOnPartialImageListener.onPartialImage(exception)) {
1827 throw exception;
1828 }
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001829 }
1830
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -04001831 private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
1832 private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
1833 int limit, Source src) throws IOException;
1834 private static native ImageDecoder nCreate(byte[] data, int offset, int length,
1835 Source src) throws IOException;
1836 private static native ImageDecoder nCreate(InputStream is, byte[] storage,
1837 Source src) throws IOException;
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001838 // The fd must be seekable.
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -04001839 private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001840 @NonNull
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001841 private static native Bitmap nDecodeBitmap(long nativePtr,
Leon Scroggins III1d2bf2b2018-03-14 16:07:43 -04001842 @NonNull ImageDecoder decoder,
1843 boolean doPostProcess,
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001844 int width, int height,
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001845 @Nullable Rect cropRect, boolean mutable,
Leon Scroggins IIId62f2722018-03-29 14:52:29 -04001846 int allocator, boolean unpremulRequired,
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001847 boolean conserveMemory, boolean decodeAsAlphaMask,
1848 @Nullable ColorSpace desiredColorSpace)
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001849 throws IOException;
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -05001850 private static native Size nGetSampledSize(long nativePtr,
1851 int sampleSize);
1852 private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
Leon Scroggins IIIed074fd2017-12-11 13:47:23 -05001853 private static native void nClose(long nativePtr);
Leon Scroggins III1fad09d2017-12-22 12:54:20 -05001854 private static native String nGetMimeType(long nativePtr);
Leon Scroggins III1a69f452018-03-29 09:48:47 -04001855 private static native ColorSpace nGetColorSpace(long nativePtr);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001856}