Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.graphics; |
| 18 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 19 | import static android.system.OsConstants.SEEK_CUR; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 20 | import static android.system.OsConstants.SEEK_SET; |
| 21 | |
Leon Scroggins III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 22 | import static java.lang.annotation.RetentionPolicy.SOURCE; |
| 23 | |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 24 | import android.annotation.AnyThread; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 25 | import android.annotation.IntDef; |
Leon Scroggins III | d97e264 | 2018-04-03 11:55:55 -0400 | [diff] [blame] | 26 | import android.annotation.IntRange; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 27 | import android.annotation.NonNull; |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 28 | import android.annotation.Nullable; |
Leon Scroggins III | d97e264 | 2018-04-03 11:55:55 -0400 | [diff] [blame] | 29 | import android.annotation.Px; |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 30 | import android.annotation.TestApi; |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 31 | import android.annotation.WorkerThread; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 32 | import android.content.ContentResolver; |
| 33 | import android.content.res.AssetFileDescriptor; |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 34 | import android.content.res.AssetManager; |
Leon Scroggins III | 513031d | 2018-02-15 15:31:45 -0500 | [diff] [blame] | 35 | import android.content.res.AssetManager.AssetInputStream; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 36 | import android.content.res.Resources; |
Leon Scroggins III | 671cce2 | 2018-01-14 16:52:17 -0500 | [diff] [blame] | 37 | import android.graphics.drawable.AnimatedImageDrawable; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 38 | import android.graphics.drawable.BitmapDrawable; |
Leon Scroggins III | a38b213 | 2018-02-23 12:27:27 -0500 | [diff] [blame] | 39 | import android.graphics.drawable.Drawable; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 40 | import android.graphics.drawable.NinePatchDrawable; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 41 | import android.net.Uri; |
Leon Scroggins III | 8290eab | 2018-03-19 10:51:44 -0400 | [diff] [blame] | 42 | import android.os.Build; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 43 | import android.system.ErrnoException; |
| 44 | import android.system.Os; |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 45 | import android.util.DisplayMetrics; |
Leon Scroggins III | a38b213 | 2018-02-23 12:27:27 -0500 | [diff] [blame] | 46 | import android.util.Size; |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 47 | import android.util.TypedValue; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 48 | |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 49 | import dalvik.system.CloseGuard; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 50 | |
Leon Scroggins III | a38b213 | 2018-02-23 12:27:27 -0500 | [diff] [blame] | 51 | import libcore.io.IoUtils; |
| 52 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 53 | import java.io.File; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 54 | import java.io.FileDescriptor; |
| 55 | import java.io.FileInputStream; |
| 56 | import java.io.FileNotFoundException; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 57 | import java.io.IOException; |
| 58 | import java.io.InputStream; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 59 | import java.lang.annotation.Retention; |
Leon Scroggins III | a38b213 | 2018-02-23 12:27:27 -0500 | [diff] [blame] | 60 | import java.nio.ByteBuffer; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 61 | import java.util.concurrent.atomic.AtomicBoolean; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 62 | |
| 63 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 64 | * <p>A class for converting encoded images (like {@code PNG}, {@code JPEG}, |
| 65 | * {@code WEBP}, {@code GIF}, or {@code HEIF}) into {@link Drawable} or |
| 66 | * {@link Bitmap} objects. |
| 67 | * |
| 68 | * <p>To use it, first create a {@link Source Source} using one of the |
| 69 | * {@code createSource} overloads. For example, to decode from a {@link File}, call |
| 70 | * {@link #createSource(File)} and pass the result to {@link #decodeDrawable(Source)} |
| 71 | * or {@link #decodeBitmap(Source)}: |
| 72 | * |
| 73 | * <pre class="prettyprint"> |
| 74 | * File file = new File(...); |
| 75 | * ImageDecoder.Source source = ImageDecoder.createSource(file); |
| 76 | * Drawable drawable = ImageDecoder.decodeDrawable(source); |
| 77 | * </pre> |
| 78 | * |
| 79 | * <p>To change the default settings, pass the {@link Source Source} and an |
| 80 | * {@link OnHeaderDecodedListener OnHeaderDecodedListener} to |
| 81 | * {@link #decodeDrawable(Source, OnHeaderDecodedListener)} or |
| 82 | * {@link #decodeBitmap(Source, OnHeaderDecodedListener)}. For example, to |
| 83 | * create a sampled image with half the width and height of the original image, |
| 84 | * call {@link #setTargetSampleSize setTargetSampleSize(2)} inside |
| 85 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}: |
| 86 | * |
| 87 | * <pre class="prettyprint"> |
| 88 | * OnHeaderDecodedListener listener = new OnHeaderDecodedListener() { |
| 89 | * public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) { |
| 90 | * decoder.setTargetSampleSize(2); |
| 91 | * } |
| 92 | * }; |
| 93 | * Drawable drawable = ImageDecoder.decodeDrawable(source, listener); |
| 94 | * </pre> |
| 95 | * |
| 96 | * <p>The {@link ImageInfo ImageInfo} contains information about the encoded image, like |
| 97 | * its width and height, and the {@link Source Source} can be used to match to a particular |
| 98 | * {@link Source Source} if a single {@link OnHeaderDecodedListener OnHeaderDecodedListener} |
| 99 | * is used with multiple {@link Source Source} objects. |
| 100 | * |
| 101 | * <p>The {@link OnHeaderDecodedListener OnHeaderDecodedListener} can also be implemented |
| 102 | * as a lambda: |
| 103 | * |
| 104 | * <pre class="prettyprint"> |
| 105 | * Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> { |
| 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) -> { |
| 134 | * decoder.setPostProcessor((canvas) -> { |
| 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) -> { |
| 160 | * decoder.setOnPartialImageListener((DecodeException e) -> { |
| 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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 168 | */ |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 169 | public final class ImageDecoder implements AutoCloseable { |
Leon Scroggins III | 8290eab | 2018-03-19 10:51:44 -0400 | [diff] [blame] | 170 | /** @hide **/ |
| 171 | public static int sApiLevel; |
| 172 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 173 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 174 | * Source of encoded image data. |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 175 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 176 | * <p>References the data that will be used to decode a {@link Drawable} |
| 177 | * or {@link Bitmap} in {@link #decodeDrawable decodeDrawable} or |
| 178 | * {@link #decodeBitmap decodeBitmap}. Constructing a {@code Source} (with |
| 179 | * one of the overloads of {@code createSource}) can be done on any thread |
| 180 | * because the construction simply captures values. The real work is done |
| 181 | * in {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}. |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 182 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 183 | * <p>A {@code Source} object can be reused to create multiple versions of the |
| 184 | * same image. For example, to decode a full size image and its thumbnail, |
| 185 | * the same {@code Source} can be used once with no |
| 186 | * {@link OnHeaderDecodedListener OnHeaderDecodedListener} and once with an |
| 187 | * implementation of {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded} |
| 188 | * that calls {@link #setTargetSize} with smaller dimensions. One {@code Source} |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 189 | * even used simultaneously in multiple threads.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 190 | */ |
| 191 | public static abstract class Source { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 192 | private Source() {} |
| 193 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 194 | /* @hide */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 195 | @Nullable |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 196 | Resources getResources() { return null; } |
| 197 | |
| 198 | /* @hide */ |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 199 | int getDensity() { return Bitmap.DENSITY_NONE; } |
| 200 | |
| 201 | /* @hide */ |
Leon Scroggins III | 046a99e | 2018-01-31 14:59:29 -0500 | [diff] [blame] | 202 | final int computeDstDensity() { |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 203 | Resources res = getResources(); |
| 204 | if (res == null) { |
| 205 | return Bitmap.getDefaultDensity(); |
| 206 | } |
| 207 | |
| 208 | return res.getDisplayMetrics().densityDpi; |
| 209 | } |
| 210 | |
| 211 | /* @hide */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 212 | @NonNull |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 213 | abstract ImageDecoder createImageDecoder() throws IOException; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 214 | }; |
| 215 | |
| 216 | private static class ByteArraySource extends Source { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 217 | ByteArraySource(@NonNull byte[] data, int offset, int length) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 218 | mData = data; |
| 219 | mOffset = offset; |
| 220 | mLength = length; |
| 221 | }; |
| 222 | private final byte[] mData; |
| 223 | private final int mOffset; |
| 224 | private final int mLength; |
| 225 | |
| 226 | @Override |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 227 | public ImageDecoder createImageDecoder() throws IOException { |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 228 | return nCreate(mData, mOffset, mLength, this); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 229 | } |
| 230 | } |
| 231 | |
| 232 | private static class ByteBufferSource extends Source { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 233 | ByteBufferSource(@NonNull ByteBuffer buffer) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 234 | mBuffer = buffer; |
| 235 | } |
| 236 | private final ByteBuffer mBuffer; |
| 237 | |
| 238 | @Override |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 239 | public ImageDecoder createImageDecoder() throws IOException { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 240 | if (!mBuffer.isDirect() && mBuffer.hasArray()) { |
| 241 | int offset = mBuffer.arrayOffset() + mBuffer.position(); |
| 242 | int length = mBuffer.limit() - mBuffer.position(); |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 243 | return nCreate(mBuffer.array(), offset, length, this); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 244 | } |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 245 | ByteBuffer buffer = mBuffer.slice(); |
| 246 | return nCreate(buffer, buffer.position(), buffer.limit(), this); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 247 | } |
| 248 | } |
| 249 | |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 250 | private static class ContentResolverSource extends Source { |
Leon Scroggins III | 046a99e | 2018-01-31 14:59:29 -0500 | [diff] [blame] | 251 | ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri, |
| 252 | @Nullable Resources res) { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 253 | mResolver = resolver; |
| 254 | mUri = uri; |
Leon Scroggins III | 046a99e | 2018-01-31 14:59:29 -0500 | [diff] [blame] | 255 | mResources = res; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 256 | } |
| 257 | |
| 258 | private final ContentResolver mResolver; |
| 259 | private final Uri mUri; |
Leon Scroggins III | 046a99e | 2018-01-31 14:59:29 -0500 | [diff] [blame] | 260 | private final Resources mResources; |
| 261 | |
| 262 | @Nullable |
| 263 | Resources getResources() { return mResources; } |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 264 | |
| 265 | @Override |
| 266 | public ImageDecoder createImageDecoder() throws IOException { |
| 267 | AssetFileDescriptor assetFd = null; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 268 | try { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 269 | if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) { |
| 270 | assetFd = mResolver.openTypedAssetFileDescriptor(mUri, |
| 271 | "image/*", null); |
| 272 | } else { |
| 273 | assetFd = mResolver.openAssetFileDescriptor(mUri, "r"); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 274 | } |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 275 | } catch (FileNotFoundException e) { |
| 276 | // Some images cannot be opened as AssetFileDescriptors (e.g. |
| 277 | // bmp, ico). Open them as InputStreams. |
| 278 | InputStream is = mResolver.openInputStream(mUri); |
| 279 | if (is == null) { |
| 280 | throw new FileNotFoundException(mUri.toString()); |
| 281 | } |
| 282 | |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 283 | return createFromStream(is, true, this); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 284 | } |
| 285 | |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 286 | final FileDescriptor fd = assetFd.getFileDescriptor(); |
| 287 | final long offset = assetFd.getStartOffset(); |
| 288 | |
| 289 | ImageDecoder decoder = null; |
| 290 | try { |
| 291 | try { |
| 292 | Os.lseek(fd, offset, SEEK_SET); |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 293 | decoder = nCreate(fd, this); |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 294 | } catch (ErrnoException e) { |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 295 | decoder = createFromStream(new FileInputStream(fd), true, this); |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 296 | } |
| 297 | } finally { |
| 298 | if (decoder == null) { |
| 299 | IoUtils.closeQuietly(assetFd); |
| 300 | } else { |
| 301 | decoder.mAssetFd = assetFd; |
| 302 | } |
| 303 | } |
| 304 | return decoder; |
| 305 | } |
| 306 | } |
| 307 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 308 | @NonNull |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 309 | private static ImageDecoder createFromFile(@NonNull File file, |
| 310 | @NonNull Source source) throws IOException { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 311 | 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 III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 316 | return createFromStream(stream, true, source); |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 317 | } |
| 318 | |
| 319 | ImageDecoder decoder = null; |
| 320 | try { |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 321 | decoder = nCreate(fd, source); |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 322 | } finally { |
| 323 | if (decoder == null) { |
| 324 | IoUtils.closeQuietly(stream); |
| 325 | } else { |
| 326 | decoder.mInputStream = stream; |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 327 | decoder.mOwnsInputStream = true; |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 328 | } |
| 329 | } |
| 330 | return decoder; |
| 331 | } |
| 332 | |
| 333 | @NonNull |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 334 | private static ImageDecoder createFromStream(@NonNull InputStream is, |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 335 | boolean closeInputStream, Source source) throws IOException { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 336 | // Arbitrary size matches BitmapFactory. |
| 337 | byte[] storage = new byte[16 * 1024]; |
| 338 | ImageDecoder decoder = null; |
| 339 | try { |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 340 | decoder = nCreate(is, storage, source); |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 341 | } finally { |
| 342 | if (decoder == null) { |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 343 | if (closeInputStream) { |
| 344 | IoUtils.closeQuietly(is); |
| 345 | } |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 346 | } else { |
| 347 | decoder.mInputStream = is; |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 348 | decoder.mOwnsInputStream = closeInputStream; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 349 | decoder.mTempStorage = storage; |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | return decoder; |
| 354 | } |
| 355 | |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 356 | /** |
| 357 | * For backwards compatibility, this does *not* close the InputStream. |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 358 | * |
| 359 | * Further, unlike other Sources, this one is not reusable. |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 360 | */ |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 361 | private static class InputStreamSource extends Source { |
| 362 | InputStreamSource(Resources res, InputStream is, int inputDensity) { |
| 363 | if (is == null) { |
| 364 | throw new IllegalArgumentException("The InputStream cannot be null"); |
| 365 | } |
| 366 | mResources = res; |
| 367 | mInputStream = is; |
| 368 | mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; |
| 369 | } |
| 370 | |
| 371 | final Resources mResources; |
| 372 | InputStream mInputStream; |
| 373 | final int mInputDensity; |
| 374 | |
| 375 | @Override |
| 376 | public Resources getResources() { return mResources; } |
| 377 | |
| 378 | @Override |
| 379 | public int getDensity() { return mInputDensity; } |
| 380 | |
| 381 | @Override |
| 382 | public ImageDecoder createImageDecoder() throws IOException { |
| 383 | |
| 384 | synchronized (this) { |
| 385 | if (mInputStream == null) { |
| 386 | throw new IOException("Cannot reuse InputStreamSource"); |
| 387 | } |
| 388 | InputStream is = mInputStream; |
| 389 | mInputStream = null; |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 390 | return createFromStream(is, false, this); |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 391 | } |
| 392 | } |
| 393 | } |
| 394 | |
Leon Scroggins III | 513031d | 2018-02-15 15:31:45 -0500 | [diff] [blame] | 395 | /** |
| 396 | * Takes ownership of the AssetInputStream. |
| 397 | * |
| 398 | * @hide |
| 399 | */ |
| 400 | public static class AssetInputStreamSource extends Source { |
| 401 | public AssetInputStreamSource(@NonNull AssetInputStream ais, |
| 402 | @NonNull Resources res, @NonNull TypedValue value) { |
| 403 | mAssetInputStream = ais; |
| 404 | mResources = res; |
| 405 | |
| 406 | if (value.density == TypedValue.DENSITY_DEFAULT) { |
| 407 | mDensity = DisplayMetrics.DENSITY_DEFAULT; |
| 408 | } else if (value.density != TypedValue.DENSITY_NONE) { |
| 409 | mDensity = value.density; |
| 410 | } else { |
| 411 | mDensity = Bitmap.DENSITY_NONE; |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | private AssetInputStream mAssetInputStream; |
| 416 | private final Resources mResources; |
| 417 | private final int mDensity; |
| 418 | |
| 419 | @Override |
| 420 | public Resources getResources() { return mResources; } |
| 421 | |
| 422 | @Override |
| 423 | public int getDensity() { |
| 424 | return mDensity; |
| 425 | } |
| 426 | |
| 427 | @Override |
| 428 | public ImageDecoder createImageDecoder() throws IOException { |
Leon Scroggins III | 513031d | 2018-02-15 15:31:45 -0500 | [diff] [blame] | 429 | synchronized (this) { |
| 430 | if (mAssetInputStream == null) { |
| 431 | throw new IOException("Cannot reuse AssetInputStreamSource"); |
| 432 | } |
| 433 | AssetInputStream ais = mAssetInputStream; |
| 434 | mAssetInputStream = null; |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 435 | return createFromAsset(ais, this); |
Leon Scroggins III | 513031d | 2018-02-15 15:31:45 -0500 | [diff] [blame] | 436 | } |
| 437 | } |
| 438 | } |
| 439 | |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 440 | private static class ResourceSource extends Source { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 441 | ResourceSource(@NonNull Resources res, int resId) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 442 | mResources = res; |
| 443 | mResId = resId; |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 444 | mResDensity = Bitmap.DENSITY_NONE; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 445 | } |
| 446 | |
| 447 | final Resources mResources; |
| 448 | final int mResId; |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 449 | int mResDensity; |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 450 | private Object mLock = new Object(); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 451 | |
| 452 | @Override |
| 453 | public Resources getResources() { return mResources; } |
| 454 | |
| 455 | @Override |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 456 | public int getDensity() { |
| 457 | synchronized (mLock) { |
| 458 | return mResDensity; |
| 459 | } |
| 460 | } |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 461 | |
| 462 | @Override |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 463 | public ImageDecoder createImageDecoder() throws IOException { |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 464 | TypedValue value = new TypedValue(); |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 465 | // This is just used in order to access the underlying Asset and |
| 466 | // keep it alive. |
| 467 | InputStream is = mResources.openRawResource(mResId, value); |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 468 | |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 469 | synchronized (mLock) { |
| 470 | if (value.density == TypedValue.DENSITY_DEFAULT) { |
| 471 | mResDensity = DisplayMetrics.DENSITY_DEFAULT; |
| 472 | } else if (value.density != TypedValue.DENSITY_NONE) { |
| 473 | mResDensity = value.density; |
| 474 | } |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 475 | } |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 476 | |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 477 | return createFromAsset((AssetInputStream) is, this); |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 478 | } |
| 479 | } |
| 480 | |
| 481 | /** |
| 482 | * ImageDecoder will own the AssetInputStream. |
| 483 | */ |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 484 | private static ImageDecoder createFromAsset(AssetInputStream ais, |
| 485 | Source source) throws IOException { |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 486 | ImageDecoder decoder = null; |
| 487 | try { |
| 488 | long asset = ais.getNativeAsset(); |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 489 | decoder = nCreate(asset, source); |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 490 | } finally { |
| 491 | if (decoder == null) { |
| 492 | IoUtils.closeQuietly(ais); |
| 493 | } else { |
| 494 | decoder.mInputStream = ais; |
| 495 | decoder.mOwnsInputStream = true; |
| 496 | } |
| 497 | } |
| 498 | return decoder; |
| 499 | } |
| 500 | |
| 501 | private static class AssetSource extends Source { |
| 502 | AssetSource(@NonNull AssetManager assets, @NonNull String fileName) { |
| 503 | mAssets = assets; |
| 504 | mFileName = fileName; |
| 505 | } |
| 506 | |
| 507 | private final AssetManager mAssets; |
| 508 | private final String mFileName; |
| 509 | |
| 510 | @Override |
| 511 | public ImageDecoder createImageDecoder() throws IOException { |
| 512 | InputStream is = mAssets.open(mFileName); |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 513 | return createFromAsset((AssetInputStream) is, this); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 514 | } |
| 515 | } |
| 516 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 517 | private static class FileSource extends Source { |
| 518 | FileSource(@NonNull File file) { |
| 519 | mFile = file; |
| 520 | } |
| 521 | |
| 522 | private final File mFile; |
| 523 | |
| 524 | @Override |
| 525 | public ImageDecoder createImageDecoder() throws IOException { |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 526 | return createFromFile(mFile, this); |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 527 | } |
| 528 | } |
| 529 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 530 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 531 | * Information about an encoded image. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 532 | */ |
| 533 | public static class ImageInfo { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 534 | private final Size mSize; |
| 535 | private ImageDecoder mDecoder; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 536 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 537 | private ImageInfo(@NonNull ImageDecoder decoder) { |
| 538 | mSize = new Size(decoder.mWidth, decoder.mHeight); |
| 539 | mDecoder = decoder; |
Leon Scroggins III | 1fad09d | 2017-12-22 12:54:20 -0500 | [diff] [blame] | 540 | } |
| 541 | |
| 542 | /** |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 543 | * Size of the image, without scaling or cropping. |
Leon Scroggins III | 1fad09d | 2017-12-22 12:54:20 -0500 | [diff] [blame] | 544 | */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 545 | @NonNull |
| 546 | public Size getSize() { |
| 547 | return mSize; |
| 548 | } |
| 549 | |
| 550 | /** |
| 551 | * The mimeType of the image. |
| 552 | */ |
| 553 | @NonNull |
Leon Scroggins III | 1fad09d | 2017-12-22 12:54:20 -0500 | [diff] [blame] | 554 | public String getMimeType() { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 555 | return mDecoder.getMimeType(); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 556 | } |
Leon Scroggins III | 127d31a | 2018-01-19 12:29:47 -0500 | [diff] [blame] | 557 | |
| 558 | /** |
| 559 | * Whether the image is animated. |
| 560 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 561 | * <p>If {@code true}, {@link #decodeDrawable decodeDrawable} will |
| 562 | * return an {@link AnimatedImageDrawable}.</p> |
Leon Scroggins III | 127d31a | 2018-01-19 12:29:47 -0500 | [diff] [blame] | 563 | */ |
| 564 | public boolean isAnimated() { |
| 565 | return mDecoder.mAnimated; |
| 566 | } |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 567 | |
| 568 | /** |
| 569 | * If known, the color space the decoded bitmap will have. Note that the |
| 570 | * output color space is not guaranteed to be the color space the bitmap |
| 571 | * is encoded with. If not known (when the config is |
| 572 | * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error, |
| 573 | * it is set to null. |
| 574 | */ |
| 575 | @Nullable |
| 576 | public ColorSpace getColorSpace() { |
| 577 | return mDecoder.getColorSpace(); |
| 578 | } |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 579 | }; |
| 580 | |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 581 | /** @removed |
| 582 | * @deprecated Subsumed by {@link #DecodeException}. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 583 | */ |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 584 | @java.lang.Deprecated |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 585 | public static class IncompleteException extends IOException {}; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 586 | |
| 587 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 588 | * Interface for changing the default settings of a decode. |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 589 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 590 | * <p>Supply an instance to |
| 591 | * {@link #decodeDrawable(Source, OnHeaderDecodedListener) decodeDrawable} |
| 592 | * or {@link #decodeBitmap(Source, OnHeaderDecodedListener) decodeBitmap}, |
| 593 | * which will call {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded} |
| 594 | * (in the same thread) once the size is known. The implementation of |
| 595 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded} can then |
| 596 | * change the decode settings as desired. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 597 | */ |
| 598 | public static interface OnHeaderDecodedListener { |
| 599 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 600 | * Called by {@link ImageDecoder} when the header has been decoded and |
| 601 | * the image size is known. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 602 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 603 | * @param decoder the object performing the decode, for changing |
| 604 | * its default settings. |
| 605 | * @param info information about the encoded image. |
| 606 | * @param source object that created {@code decoder}. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 607 | */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 608 | public void onHeaderDecoded(@NonNull ImageDecoder decoder, |
| 609 | @NonNull ImageInfo info, @NonNull Source source); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 610 | |
| 611 | }; |
| 612 | |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 613 | /** @removed |
| 614 | * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}. |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 615 | */ |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 616 | @java.lang.Deprecated |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 617 | public static final int ERROR_SOURCE_EXCEPTION = 1; |
| 618 | |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 619 | /** @removed |
| 620 | * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}. |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 621 | */ |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 622 | @java.lang.Deprecated |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 623 | public static final int ERROR_SOURCE_INCOMPLETE = 2; |
| 624 | |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 625 | /** @removed |
| 626 | * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}. |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 627 | */ |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 628 | @java.lang.Deprecated |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 629 | public static final int ERROR_SOURCE_ERROR = 3; |
| 630 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 631 | /** |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 632 | * Information about an interrupted decode. |
| 633 | */ |
| 634 | public static final class DecodeException extends IOException { |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 635 | /** |
| 636 | * An Exception was thrown reading the {@link Source}. |
| 637 | */ |
| 638 | public static final int SOURCE_EXCEPTION = 1; |
| 639 | |
| 640 | /** |
| 641 | * The encoded data was incomplete. |
| 642 | */ |
| 643 | public static final int SOURCE_INCOMPLETE = 2; |
| 644 | |
| 645 | /** |
| 646 | * The encoded data contained an error. |
| 647 | */ |
| 648 | public static final int SOURCE_MALFORMED_DATA = 3; |
| 649 | |
| 650 | /** @hide **/ |
| 651 | @Retention(SOURCE) |
| 652 | @IntDef(value = { SOURCE_EXCEPTION, SOURCE_INCOMPLETE, SOURCE_MALFORMED_DATA }, |
| 653 | prefix = {"SOURCE_"}) |
| 654 | public @interface Error {}; |
| 655 | |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 656 | @Error final int mError; |
| 657 | @NonNull final Source mSource; |
| 658 | |
| 659 | DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) { |
| 660 | super(errorMessage(error, cause), cause); |
| 661 | mError = error; |
| 662 | mSource = source; |
| 663 | } |
| 664 | |
| 665 | /** |
| 666 | * Private method called by JNI. |
| 667 | */ |
| 668 | @SuppressWarnings("unused") |
| 669 | DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause, |
| 670 | @NonNull Source source) { |
| 671 | super(msg + errorMessage(error, cause), cause); |
| 672 | mError = error; |
| 673 | mSource = source; |
| 674 | } |
| 675 | |
| 676 | /** |
| 677 | * Retrieve the reason that decoding was interrupted. |
| 678 | * |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 679 | * <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 680 | * {@link java.lang.Throwable} can be retrieved with |
| 681 | * {@link java.lang.Throwable#getCause}.</p> |
| 682 | */ |
| 683 | @Error |
| 684 | public int getError() { |
| 685 | return mError; |
| 686 | } |
| 687 | |
| 688 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 689 | * Retrieve the {@link Source Source} that was interrupted. |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 690 | * |
| 691 | * <p>This can be used for equality checking to find the Source which |
| 692 | * failed to completely decode.</p> |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 693 | */ |
| 694 | @NonNull |
| 695 | public Source getSource() { |
| 696 | return mSource; |
| 697 | } |
| 698 | |
| 699 | private static String errorMessage(@Error int error, @Nullable Throwable cause) { |
| 700 | switch (error) { |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 701 | case SOURCE_EXCEPTION: |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 702 | return "Exception in input: " + cause; |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 703 | case SOURCE_INCOMPLETE: |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 704 | return "Input was incomplete."; |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 705 | case SOURCE_MALFORMED_DATA: |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 706 | return "Input contained an error."; |
| 707 | default: |
| 708 | return ""; |
| 709 | } |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 714 | * Interface for inspecting a {@link DecodeException DecodeException} |
| 715 | * and potentially preventing it from being thrown. |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 716 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 717 | * <p>If an instance is passed to |
| 718 | * {@link #setOnPartialImageListener setOnPartialImageListener}, a |
| 719 | * {@link DecodeException DecodeException} that would otherwise have been |
| 720 | * thrown can be inspected inside |
| 721 | * {@link OnPartialImageListener#onPartialImage onPartialImage}. |
| 722 | * If {@link OnPartialImageListener#onPartialImage onPartialImage} returns |
| 723 | * {@code true}, a partial image will be created. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 724 | */ |
Leon Scroggins III | edf26d6 | 2018-01-09 16:55:24 -0500 | [diff] [blame] | 725 | public static interface OnPartialImageListener { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 726 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 727 | * Called by {@link ImageDecoder} when there is only a partial image to |
| 728 | * display. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 729 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 730 | * <p>If decoding is interrupted after having decoded a partial image, |
| 731 | * this method will be called. The implementation can inspect the |
| 732 | * {@link DecodeException DecodeException} and optionally finish the |
| 733 | * rest of the decode creation process to create a partial {@link Drawable} |
| 734 | * or {@link Bitmap}. |
Leon Scroggins III | edf26d6 | 2018-01-09 16:55:24 -0500 | [diff] [blame] | 735 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 736 | * @param exception exception containing information about the |
| 737 | * decode interruption. |
| 738 | * @return {@code true} to create and return a {@link Drawable} or |
| 739 | * {@link Bitmap} with partial data. {@code false} (which is the |
| 740 | * default) to abort the decode and throw {@code e}. Any undecoded |
| 741 | * lines in the image will be blank. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 742 | */ |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 743 | boolean onPartialImage(@NonNull DecodeException exception); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 744 | }; |
| 745 | |
| 746 | // Fields |
Leon Scroggins III | 671cce2 | 2018-01-14 16:52:17 -0500 | [diff] [blame] | 747 | private long mNativePtr; |
| 748 | private final int mWidth; |
| 749 | private final int mHeight; |
| 750 | private final boolean mAnimated; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 751 | |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 752 | private int mDesiredWidth; |
| 753 | private int mDesiredHeight; |
| 754 | private int mAllocator = ALLOCATOR_DEFAULT; |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 755 | private boolean mUnpremultipliedRequired = false; |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 756 | 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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 763 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 764 | private PostProcessor mPostProcessor; |
Leon Scroggins III | edf26d6 | 2018-01-09 16:55:24 -0500 | [diff] [blame] | 765 | private OnPartialImageListener mOnPartialImageListener; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 766 | |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 767 | // Objects for interacting with the input. |
| 768 | private InputStream mInputStream; |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 769 | private boolean mOwnsInputStream; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 770 | private byte[] mTempStorage; |
| 771 | private AssetFileDescriptor mAssetFd; |
| 772 | private final AtomicBoolean mClosed = new AtomicBoolean(); |
| 773 | private final CloseGuard mCloseGuard = CloseGuard.get(); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 774 | |
| 775 | /** |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 776 | * Private constructor called by JNI. {@link #close} must be |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 777 | * called after decoding to delete native resources. |
| 778 | */ |
| 779 | @SuppressWarnings("unused") |
Leon Scroggins III | 671cce2 | 2018-01-14 16:52:17 -0500 | [diff] [blame] | 780 | private ImageDecoder(long nativePtr, int width, int height, |
| 781 | boolean animated) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 782 | mNativePtr = nativePtr; |
| 783 | mWidth = width; |
| 784 | mHeight = height; |
| 785 | mDesiredWidth = width; |
| 786 | mDesiredHeight = height; |
Leon Scroggins III | 671cce2 | 2018-01-14 16:52:17 -0500 | [diff] [blame] | 787 | mAnimated = animated; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 788 | 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 III | 127d31a | 2018-01-19 12:29:47 -0500 | [diff] [blame] | 798 | // Avoid closing these in finalizer. |
| 799 | mInputStream = null; |
| 800 | mAssetFd = null; |
| 801 | |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 802 | close(); |
| 803 | } finally { |
| 804 | super.finalize(); |
| 805 | } |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 806 | } |
| 807 | |
| 808 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 809 | * Create a new {@link Source Source} from a resource. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 810 | * |
| 811 | * @param res the {@link Resources} object containing the image data. |
| 812 | * @param resId resource ID of the image data. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 813 | * @return a new Source object, which can be passed to |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 814 | * {@link #decodeDrawable decodeDrawable} or |
| 815 | * {@link #decodeBitmap decodeBitmap}. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 816 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 817 | @AnyThread |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 818 | @NonNull |
Leon Scroggins III | 1eccac8 | 2018-03-16 15:04:04 -0400 | [diff] [blame] | 819 | public static Source createSource(@NonNull Resources res, int resId) |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 820 | { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 821 | return new ResourceSource(res, resId); |
| 822 | } |
| 823 | |
| 824 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 825 | * 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 III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 833 | * |
| 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 III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 837 | * {@link #decodeDrawable decodeDrawable} or |
| 838 | * {@link #decodeBitmap decodeBitmap}. |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 839 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 840 | @AnyThread |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 841 | @NonNull |
| 842 | public static Source createSource(@NonNull ContentResolver cr, |
| 843 | @NonNull Uri uri) { |
Leon Scroggins III | 046a99e | 2018-01-31 14:59:29 -0500 | [diff] [blame] | 844 | return new ContentResolverSource(cr, uri, null); |
| 845 | } |
| 846 | |
| 847 | /** |
| 848 | * Provide Resources for density scaling. |
| 849 | * |
| 850 | * @hide |
| 851 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 852 | @AnyThread |
Leon Scroggins III | 046a99e | 2018-01-31 14:59:29 -0500 | [diff] [blame] | 853 | @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 III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 857 | } |
| 858 | |
| 859 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 860 | * Create a new {@link Source Source} from a file in the "assets" directory. |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 861 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 862 | @AnyThread |
Leon Scroggins III | 121ef98 | 2018-03-16 15:40:54 -0400 | [diff] [blame] | 863 | @NonNull |
| 864 | public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) { |
| 865 | return new AssetSource(assets, fileName); |
| 866 | } |
| 867 | |
| 868 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 869 | * Create a new {@link Source Source} from a byte array. |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 870 | * |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 871 | * @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 III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 875 | * @return a new Source object, which can be passed to |
| 876 | * {@link #decodeDrawable decodeDrawable} or |
| 877 | * {@link #decodeBitmap decodeBitmap}. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 878 | * @throws NullPointerException if data is null. |
| 879 | * @throws ArrayIndexOutOfBoundsException if offset and length are |
| 880 | * not within data. |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 881 | * @hide |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 882 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 883 | @AnyThread |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 884 | @NonNull |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 885 | 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 III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 899 | * See {@link #createSource(byte[], int, int). |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 900 | * @hide |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 901 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 902 | @AnyThread |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 903 | @NonNull |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 904 | public static Source createSource(@NonNull byte[] data) { |
| 905 | return createSource(data, 0, data.length); |
| 906 | } |
| 907 | |
| 908 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 909 | * Create a new {@link Source Source} from a {@link java.nio.ByteBuffer}. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 910 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 911 | * <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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 913 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 914 | * <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 III | 4367534 | 2018-02-23 14:01:09 -0500 | [diff] [blame] | 916 | * 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 III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 919 | * |
| 920 | * @return a new Source object, which can be passed to |
| 921 | * {@link #decodeDrawable decodeDrawable} or |
| 922 | * {@link #decodeBitmap decodeBitmap}. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 923 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 924 | @AnyThread |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 925 | @NonNull |
| 926 | public static Source createSource(@NonNull ByteBuffer buffer) { |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 927 | return new ByteBufferSource(buffer); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 928 | } |
| 929 | |
| 930 | /** |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 931 | * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 932 | * |
| 933 | * <p>Unlike other Sources, this one cannot be reused.</p> |
| 934 | * |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 935 | * @hide |
| 936 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 937 | @AnyThread |
| 938 | @NonNull |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 939 | 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 III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 945 | * |
| 946 | * <p>Unlike other Sources, this one cannot be reused.</p> |
| 947 | * |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 948 | * @hide |
| 949 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 950 | @AnyThread |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 951 | @TestApi |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 952 | @NonNull |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 953 | public static Source createSource(Resources res, InputStream is, int density) { |
| 954 | return new InputStreamSource(res, is, density); |
| 955 | } |
| 956 | |
| 957 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 958 | * 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 III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 963 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 964 | @AnyThread |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 965 | @NonNull |
| 966 | public static Source createSource(@NonNull File file) { |
| 967 | return new FileSource(file); |
| 968 | } |
| 969 | |
| 970 | /** |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 971 | * Return the width and height of a given sample size. |
| 972 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 973 | * <p>This takes an input that functions like |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 974 | * {@link BitmapFactory.Options#inSampleSize}. It returns a width and |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 975 | * height that can be achieved by sampling the encoded image. Other widths |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 976 | * and heights may be supported, but will require an additional (internal) |
| 977 | * scaling step. Such internal scaling is *not* supported with |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 978 | * {@link #setUnpremultipliedRequired} set to {@code true}.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 979 | * |
| 980 | * @param sampleSize Sampling rate of the encoded image. |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 981 | * @return {@link android.util.Size} of the width and height after |
| 982 | * sampling. |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 983 | * |
| 984 | * @hide |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 985 | */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 986 | @NonNull |
| 987 | public Size getSampledSize(int sampleSize) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 988 | if (sampleSize <= 0) { |
| 989 | throw new IllegalArgumentException("sampleSize must be positive! " |
| 990 | + "provided " + sampleSize); |
| 991 | } |
| 992 | if (mNativePtr == 0) { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 993 | throw new IllegalStateException("ImageDecoder is closed!"); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 994 | } |
| 995 | |
| 996 | return nGetSampledSize(mNativePtr, sampleSize); |
| 997 | } |
| 998 | |
| 999 | // Modifiers |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1000 | /** @removed |
| 1001 | * @deprecated Renamed to {@link #setTargetSize}. |
| 1002 | */ |
| 1003 | @java.lang.Deprecated |
| 1004 | public ImageDecoder setResize(int width, int height) { |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1005 | this.setTargetSize(width, height); |
| 1006 | return this; |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1007 | } |
| 1008 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1009 | /** |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1010 | * 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 III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1013 | * 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 III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1018 | * |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1019 | * <p>Only the last call to this or {@link #setTargetSampleSize} is |
| 1020 | * respected.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1021 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1022 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1023 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1024 | * |
Leon Scroggins III | d97e264 | 2018-04-03 11:55:55 -0400 | [diff] [blame] | 1025 | * @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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1027 | */ |
Leon Scroggins III | d97e264 | 2018-04-03 11:55:55 -0400 | [diff] [blame] | 1028 | public void setTargetSize(@Px @IntRange(from = 1) int width, |
| 1029 | @Px @IntRange(from = 1) int height) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1030 | 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 III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1039 | /** @removed |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1040 | * @deprecated Renamed to {@link #setTargetSampleSize}. |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1041 | */ |
| 1042 | @java.lang.Deprecated |
| 1043 | public ImageDecoder setResize(int sampleSize) { |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1044 | this.setTargetSampleSize(sampleSize); |
| 1045 | return this; |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1046 | } |
| 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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1074 | /** |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1075 | * Set the target size with a sampleSize. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1076 | * |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1077 | * <p>By default, the output size will match the size of the encoded |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1078 | * image, which can be retrieved from the {@link ImageInfo ImageInfo} in |
| 1079 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1080 | * |
| 1081 | * <p>Requests the decoder to subsample the original image, returning a |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1082 | * smaller image to save memory. The {@code sampleSize} is the number of pixels |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1083 | * in either dimension that correspond to a single pixel in the output. |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1084 | * For example, {@code sampleSize == 4} returns an image that is 1/4 the |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1085 | * 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 III | d97e264 | 2018-04-03 11:55:55 -0400 | [diff] [blame] | 1089 | * <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 III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1095 | * <p>Only the last call to this or {@link #setTargetSize} is respected.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1096 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1097 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1098 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1099 | * |
Leon Scroggins III | d97e264 | 2018-04-03 11:55:55 -0400 | [diff] [blame] | 1100 | * @param sampleSize sampling rate of the encoded image. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1101 | */ |
Leon Scroggins III | d97e264 | 2018-04-03 11:55:55 -0400 | [diff] [blame] | 1102 | public void setTargetSampleSize(@IntRange(from = 1) int sampleSize) { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1103 | Size size = this.getSampledSize(sampleSize); |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1104 | int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth()); |
| 1105 | int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight()); |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1106 | this.setTargetSize(targetWidth, targetHeight); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1107 | } |
| 1108 | |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1109 | private boolean requestedResize() { |
| 1110 | return mWidth != mDesiredWidth || mHeight != mDesiredHeight; |
| 1111 | } |
| 1112 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1113 | // 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 III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1120 | * {@link #setMutableRequired setMutableRequired(true)} or |
| 1121 | * {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)}. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1122 | */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1123 | public static final int ALLOCATOR_DEFAULT = 0; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1124 | |
| 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 III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1131 | public static final int ALLOCATOR_SOFTWARE = 1; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1132 | |
| 1133 | /** |
| 1134 | * Use shared memory for the pixel memory. |
| 1135 | * |
| 1136 | * Useful for sharing across processes. |
| 1137 | */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1138 | public static final int ALLOCATOR_SHARED_MEMORY = 2; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1139 | |
| 1140 | /** |
| 1141 | * Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}. |
| 1142 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1143 | * When this is combined with incompatible options, like |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1144 | * {@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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1148 | */ |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1149 | public static final int ALLOCATOR_HARDWARE = 3; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1150 | |
| 1151 | /** @hide **/ |
| 1152 | @Retention(SOURCE) |
Leon Scroggins III | 62f0800 | 2018-02-26 12:32:52 -0500 | [diff] [blame] | 1153 | @IntDef(value = { ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE, |
| 1154 | ALLOCATOR_SHARED_MEMORY, ALLOCATOR_HARDWARE }, |
| 1155 | prefix = {"ALLOCATOR_"}) |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1156 | public @interface Allocator {}; |
| 1157 | |
| 1158 | /** |
| 1159 | * Choose the backing for the pixel memory. |
| 1160 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1161 | * <p>This is ignored for animated drawables.</p> |
| 1162 | * |
| 1163 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1164 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1165 | * |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1166 | * @param allocator Type of allocator to use. |
| 1167 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1168 | public void setAllocator(@Allocator int allocator) { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1169 | if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1170 | throw new IllegalArgumentException("invalid allocator " + allocator); |
| 1171 | } |
| 1172 | mAllocator = allocator; |
| 1173 | } |
| 1174 | |
| 1175 | /** |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1176 | * Return the allocator for the pixel memory. |
| 1177 | */ |
| 1178 | @Allocator |
| 1179 | public int getAllocator() { |
| 1180 | return mAllocator; |
| 1181 | } |
| 1182 | |
| 1183 | /** |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1184 | * Specify whether the {@link Bitmap} should have unpremultiplied pixels. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1185 | * |
Leon Scroggins III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1186 | * <p>By default, ImageDecoder will create a {@link Bitmap} with |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1187 | * premultiplied pixels, which is required for drawing with the |
| 1188 | * {@link android.view.View} system (i.e. to a {@link Canvas}). Calling |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1189 | * this method with a value of {@code true} will result in |
| 1190 | * {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1191 | * 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 III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1195 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1196 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1197 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1198 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1199 | public void setUnpremultipliedRequired(boolean unpremultipliedRequired) { |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1200 | mUnpremultipliedRequired = unpremultipliedRequired; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1201 | } |
| 1202 | |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1203 | /** @removed |
| 1204 | * @deprecated Renamed to {@link #setUnpremultipliedRequired}. |
| 1205 | */ |
| 1206 | @java.lang.Deprecated |
| 1207 | public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) { |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1208 | this.setUnpremultipliedRequired(unpremultipliedRequired); |
| 1209 | return this; |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1210 | } |
| 1211 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1212 | /** |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1213 | * Return whether the {@link Bitmap} will have unpremultiplied pixels. |
| 1214 | */ |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1215 | public boolean isUnpremultipliedRequired() { |
| 1216 | return mUnpremultipliedRequired; |
| 1217 | } |
| 1218 | |
| 1219 | /** @removed |
| 1220 | * @deprecated Renamed to {@link #isUnpremultipliedRequired}. |
| 1221 | */ |
| 1222 | @java.lang.Deprecated |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1223 | public boolean getRequireUnpremultiplied() { |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1224 | return this.isUnpremultipliedRequired(); |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1225 | } |
| 1226 | |
| 1227 | /** |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1228 | * Modify the image after decoding and scaling. |
| 1229 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1230 | * <p>This allows adding effects prior to returning a {@link Drawable} or |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1231 | * {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap}, |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1232 | * this is the only way to process the image after decoding.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1233 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1234 | * <p>If combined with {@link #setTargetSize} and/or {@link #setCrop}, |
| 1235 | * {@link PostProcessor#onPostProcess} occurs last.</p> |
| 1236 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1237 | * <p>If set on a nine-patch image, the nine-patch data is ignored.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1238 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1239 | * <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 III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1242 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1243 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1244 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1245 | * |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1246 | */ |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1247 | public void setPostProcessor(@Nullable PostProcessor postProcessor) { |
| 1248 | mPostProcessor = postProcessor; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1249 | } |
| 1250 | |
| 1251 | /** |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1252 | * Return the {@link PostProcessor} currently set. |
| 1253 | */ |
| 1254 | @Nullable |
| 1255 | public PostProcessor getPostProcessor() { |
| 1256 | return mPostProcessor; |
| 1257 | } |
| 1258 | |
| 1259 | /** |
Leon Scroggins III | edf26d6 | 2018-01-09 16:55:24 -0500 | [diff] [blame] | 1260 | * Set (replace) the {@link OnPartialImageListener} on this object. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1261 | * |
Leon Scroggins III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1262 | * <p>Will be called if there is an error in the input. Without one, an |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1263 | * error will result in an {@code Exception} being thrown.</p> |
Leon Scroggins III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1264 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1265 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1266 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1267 | * |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1268 | */ |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1269 | public void setOnPartialImageListener(@Nullable OnPartialImageListener listener) { |
| 1270 | mOnPartialImageListener = listener; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1271 | } |
| 1272 | |
| 1273 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1274 | * Return the {@link OnPartialImageListener OnPartialImageListener} currently set. |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1275 | */ |
| 1276 | @Nullable |
| 1277 | public OnPartialImageListener getOnPartialImageListener() { |
| 1278 | return mOnPartialImageListener; |
| 1279 | } |
| 1280 | |
| 1281 | /** |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1282 | * Crop the output to {@code subset} of the (possibly) scaled image. |
| 1283 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1284 | * <p>{@code subset} must be contained within the size set by |
Leon Scroggins III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1285 | * {@link #setTargetSize} or the bounds of the image if setTargetSize was |
| 1286 | * not called. Otherwise an {@link IllegalStateException} will be thrown by |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1287 | * {@link #decodeDrawable decodeDrawable}/{@link #decodeBitmap decodeBitmap}.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1288 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1289 | * <p>NOT intended as a replacement for |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1290 | * {@link BitmapRegionDecoder#decodeRegion BitmapRegionDecoder.decodeRegion()}. |
| 1291 | * This supports all formats, but merely crops the output.</p> |
Leon Scroggins III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1292 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1293 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1294 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1295 | * |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1296 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1297 | public void setCrop(@Nullable Rect subset) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1298 | mCropRect = subset; |
| 1299 | } |
| 1300 | |
| 1301 | /** |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1302 | * Return the cropping rectangle, if set. |
| 1303 | */ |
| 1304 | @Nullable |
| 1305 | public Rect getCrop() { |
| 1306 | return mCropRect; |
| 1307 | } |
| 1308 | |
| 1309 | /** |
Leon Scroggins III | 2f1b06b | 2018-02-15 10:40:33 -0500 | [diff] [blame] | 1310 | * 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 III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1315 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1316 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1317 | * |
Leon Scroggins III | 2f1b06b | 2018-02-15 10:40:33 -0500 | [diff] [blame] | 1318 | * @hide |
| 1319 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1320 | public void setOutPaddingRect(@NonNull Rect outPadding) { |
Leon Scroggins III | 2f1b06b | 2018-02-15 10:40:33 -0500 | [diff] [blame] | 1321 | mOutPaddingRect = outPadding; |
| 1322 | } |
| 1323 | |
| 1324 | /** |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1325 | * Specify whether the {@link Bitmap} should be mutable. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1326 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1327 | * <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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1330 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1331 | * <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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1335 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1336 | * <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable decodeDrawable}, |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1337 | * 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 III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1340 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1341 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1342 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1343 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1344 | public void setMutableRequired(boolean mutable) { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1345 | mMutable = mutable; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1346 | } |
| 1347 | |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1348 | /** @removed |
| 1349 | * @deprecated Renamed to {@link #setMutableRequired}. |
| 1350 | */ |
| 1351 | @java.lang.Deprecated |
| 1352 | public ImageDecoder setMutable(boolean mutable) { |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1353 | this.setMutableRequired(mutable); |
| 1354 | return this; |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1355 | } |
| 1356 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1357 | /** |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1358 | * Return whether the decoded {@link Bitmap} will be mutable. |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1359 | */ |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1360 | public boolean isMutableRequired() { |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1361 | return mMutable; |
| 1362 | } |
| 1363 | |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1364 | /** @removed |
| 1365 | * @deprecated Renamed to {@link #isMutableRequired}. |
| 1366 | */ |
| 1367 | @java.lang.Deprecated |
| 1368 | public boolean getMutable() { |
| 1369 | return this.isMutableRequired(); |
| 1370 | } |
| 1371 | |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1372 | /** |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1373 | * Specify whether to potentially save RAM at the expense of quality. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1374 | * |
Leon Scroggins III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1375 | * <p>Setting this to {@code true} may result in a {@link Bitmap} with a |
Leon Scroggins III | a38b213 | 2018-02-23 12:27:27 -0500 | [diff] [blame] | 1376 | * 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 III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1382 | * the memory used.</p> |
| 1383 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1384 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1385 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1386 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1387 | public void setConserveMemory(boolean conserveMemory) { |
Leon Scroggins III | a38b213 | 2018-02-23 12:27:27 -0500 | [diff] [blame] | 1388 | mConserveMemory = conserveMemory; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1389 | } |
| 1390 | |
| 1391 | /** |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1392 | * 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 III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1403 | * Specify whether to potentially treat the output as an alpha mask. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1404 | * |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1405 | * <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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1408 | * |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1409 | * <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1410 | * combine them will result in {@link #decodeDrawable decodeDrawable}/ |
| 1411 | * {@link #decodeBitmap decodeBitmap} throwing an |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1412 | * {@link java.lang.IllegalStateException}.</p> |
Leon Scroggins III | 5f7e948 | 2018-02-23 13:22:29 -0500 | [diff] [blame] | 1413 | * |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1414 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1415 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1416 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1417 | public void setDecodeAsAlphaMaskEnabled(boolean enabled) { |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1418 | mDecodeAsAlphaMask = enabled; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1419 | } |
| 1420 | |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1421 | /** @removed |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1422 | * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}. |
| 1423 | */ |
| 1424 | @java.lang.Deprecated |
| 1425 | public ImageDecoder setDecodeAsAlphaMask(boolean enabled) { |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1426 | this.setDecodeAsAlphaMaskEnabled(enabled); |
| 1427 | return this; |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1428 | } |
| 1429 | |
| 1430 | /** @removed |
| 1431 | * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}. |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1432 | */ |
| 1433 | @java.lang.Deprecated |
| 1434 | public ImageDecoder setAsAlphaMask(boolean asAlphaMask) { |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1435 | this.setDecodeAsAlphaMask(asAlphaMask); |
| 1436 | return this; |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1437 | } |
| 1438 | |
| 1439 | /** |
| 1440 | * Return whether to treat single channel input as alpha. |
| 1441 | * |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1442 | * <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 III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1446 | */ |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1447 | public boolean isDecodeAsAlphaMaskEnabled() { |
| 1448 | return mDecodeAsAlphaMask; |
| 1449 | } |
| 1450 | |
| 1451 | /** @removed |
| 1452 | * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}. |
| 1453 | */ |
| 1454 | @java.lang.Deprecated |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1455 | public boolean getDecodeAsAlphaMask() { |
| 1456 | return mDecodeAsAlphaMask; |
| 1457 | } |
| 1458 | |
| 1459 | /** @removed |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1460 | * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}. |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1461 | */ |
| 1462 | @java.lang.Deprecated |
| 1463 | public boolean getAsAlphaMask() { |
| 1464 | return this.getDecodeAsAlphaMask(); |
| 1465 | } |
| 1466 | |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1467 | /** |
| 1468 | * Specify the desired {@link ColorSpace} for the output. |
| 1469 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1470 | * <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 III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1476 | * |
| 1477 | * <p>{@link Bitmap.Config#RGBA_F16} always uses the |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1478 | * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space. |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1479 | * 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 III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1484 | * be thrown by {@link #decodeDrawable decodeDrawable}/ |
| 1485 | * {@link #decodeBitmap decodeBitmap} when setting a non-RGB color space |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1486 | * 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 III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1493 | * |
| 1494 | * <p>Like all setters on ImageDecoder, this must be called inside |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1495 | * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p> |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1496 | */ |
Leon Scroggins III | d9b53a0 | 2018-03-27 07:47:07 -0400 | [diff] [blame] | 1497 | public void setTargetColorSpace(ColorSpace colorSpace) { |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1498 | mDesiredColorSpace = colorSpace; |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1499 | } |
| 1500 | |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1501 | /** |
| 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 III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1509 | @Override |
| 1510 | public void close() { |
| 1511 | mCloseGuard.close(); |
| 1512 | if (!mClosed.compareAndSet(false, true)) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1513 | return; |
| 1514 | } |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1515 | nClose(mNativePtr); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1516 | mNativePtr = 0; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1517 | |
Leon Scroggins III | 0b49f57 | 2018-01-23 11:38:23 -0500 | [diff] [blame] | 1518 | if (mOwnsInputStream) { |
| 1519 | IoUtils.closeQuietly(mInputStream); |
| 1520 | } |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1521 | IoUtils.closeQuietly(mAssetFd); |
| 1522 | |
| 1523 | mInputStream = null; |
| 1524 | mAssetFd = null; |
| 1525 | mTempStorage = null; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1526 | } |
| 1527 | |
| 1528 | private void checkState() { |
| 1529 | if (mNativePtr == 0) { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1530 | throw new IllegalStateException("Cannot use closed ImageDecoder!"); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1531 | } |
| 1532 | |
| 1533 | checkSubset(mDesiredWidth, mDesiredHeight, mCropRect); |
| 1534 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1535 | if (mAllocator == ALLOCATOR_HARDWARE) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1536 | if (mMutable) { |
| 1537 | throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!"); |
| 1538 | } |
Leon Scroggins III | 746a9fe | 2018-03-08 10:32:10 -0500 | [diff] [blame] | 1539 | if (mDecodeAsAlphaMask) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1540 | throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!"); |
| 1541 | } |
| 1542 | } |
| 1543 | |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1544 | if (mPostProcessor != null && mUnpremultipliedRequired) { |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1545 | throw new IllegalStateException("Cannot draw to unpremultiplied pixels!"); |
| 1546 | } |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1547 | |
| 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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1558 | } |
| 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 III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1570 | @WorkerThread |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1571 | @NonNull |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1572 | private Bitmap decodeBitmapInternal() throws IOException { |
Leon Scroggins III | 8c9d8f2 | 2018-01-14 14:41:46 -0500 | [diff] [blame] | 1573 | checkState(); |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 1574 | return nDecodeBitmap(mNativePtr, this, mPostProcessor != null, |
| 1575 | mDesiredWidth, mDesiredHeight, mCropRect, |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1576 | mMutable, mAllocator, mUnpremultipliedRequired, |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1577 | mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace); |
Leon Scroggins III | 8c9d8f2 | 2018-01-14 14:41:46 -0500 | [diff] [blame] | 1578 | } |
| 1579 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1580 | 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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1592 | /** |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1593 | * Create a {@link Drawable} from a {@code Source}. |
| 1594 | * |
| 1595 | * @param src representing the encoded image. |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1596 | * @param listener for learning the {@link ImageInfo ImageInfo} and changing any |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1597 | * default settings on the {@code ImageDecoder}. This will be called on |
| 1598 | * the same thread as {@code decodeDrawable} before that method returns. |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1599 | * This is required in order to change any of the default settings. |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1600 | * @return Drawable for displaying the image. |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1601 | * @throws IOException if {@code src} is not found, is an unsupported |
| 1602 | * format, or cannot be decoded for any reason. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1603 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1604 | @WorkerThread |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1605 | @NonNull |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1606 | public static Drawable decodeDrawable(@NonNull Source src, |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1607 | @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 III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1615 | @WorkerThread |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1616 | @NonNull |
| 1617 | private static Drawable decodeDrawableImpl(@NonNull Source src, |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1618 | @Nullable OnHeaderDecodedListener listener) throws IOException { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1619 | try (ImageDecoder decoder = src.createImageDecoder()) { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1620 | decoder.mSource = src; |
| 1621 | decoder.callHeaderDecoded(listener, src); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1622 | |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1623 | if (decoder.mUnpremultipliedRequired) { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1624 | // 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 III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1629 | |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1630 | if (decoder.mMutable) { |
| 1631 | throw new IllegalStateException("Cannot decode a mutable " + |
| 1632 | "Drawable!"); |
| 1633 | } |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1634 | |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1635 | // 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 III | 671cce2 | 2018-01-14 16:52:17 -0500 | [diff] [blame] | 1638 | if (decoder.mAnimated) { |
| 1639 | // AnimatedImageDrawable calls postProcessAndRelease only if |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1640 | // mPostProcessor exists. |
| 1641 | ImageDecoder postProcessPtr = decoder.mPostProcessor == null ? |
Leon Scroggins III | 671cce2 | 2018-01-14 16:52:17 -0500 | [diff] [blame] | 1642 | null : decoder; |
| 1643 | Drawable d = new AnimatedImageDrawable(decoder.mNativePtr, |
| 1644 | postProcessPtr, decoder.mDesiredWidth, |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1645 | decoder.mDesiredHeight, srcDensity, |
| 1646 | src.computeDstDensity(), decoder.mCropRect, |
Leon Scroggins III | 671cce2 | 2018-01-14 16:52:17 -0500 | [diff] [blame] | 1647 | 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 III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1654 | Bitmap bm = decoder.decodeBitmapInternal(); |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1655 | bm.setDensity(srcDensity); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1656 | |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1657 | Resources res = src.getResources(); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1658 | byte[] np = bm.getNinePatchChunk(); |
| 1659 | if (np != null && NinePatch.isNinePatchChunk(np)) { |
| 1660 | Rect opticalInsets = new Rect(); |
| 1661 | bm.getOpticalInsets(opticalInsets); |
Leon Scroggins III | 2f1b06b | 2018-02-15 10:40:33 -0500 | [diff] [blame] | 1662 | Rect padding = decoder.mOutPaddingRect; |
| 1663 | if (padding == null) { |
| 1664 | padding = new Rect(); |
| 1665 | } |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1666 | nGetPadding(decoder.mNativePtr, padding); |
| 1667 | return new NinePatchDrawable(res, bm, np, padding, |
| 1668 | opticalInsets, null); |
| 1669 | } |
| 1670 | |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1671 | return new BitmapDrawable(res, bm); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1672 | } |
| 1673 | } |
| 1674 | |
| 1675 | /** |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1676 | * Create a {@link Drawable} from a {@code Source}. |
| 1677 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1678 | * <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener}, |
| 1679 | * the default settings will be used. In order to change any settings, call |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1680 | * {@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 III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1686 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1687 | @WorkerThread |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1688 | @NonNull |
| 1689 | public static Drawable decodeDrawable(@NonNull Source src) |
| 1690 | throws IOException { |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1691 | return decodeDrawableImpl(src, null); |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1692 | } |
| 1693 | |
| 1694 | /** |
| 1695 | * Create a {@link Bitmap} from a {@code Source}. |
| 1696 | * |
| 1697 | * @param src representing the encoded image. |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1698 | * @param listener for learning the {@link ImageInfo ImageInfo} and changing any |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1699 | * default settings on the {@code ImageDecoder}. This will be called on |
| 1700 | * the same thread as {@code decodeBitmap} before that method returns. |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1701 | * This is required in order to change any of the default settings. |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1702 | * @return Bitmap containing the image. |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1703 | * @throws IOException if {@code src} is not found, is an unsupported |
| 1704 | * format, or cannot be decoded for any reason. |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1705 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1706 | @WorkerThread |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1707 | @NonNull |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1708 | public static Bitmap decodeBitmap(@NonNull Source src, |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1709 | @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 III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1717 | @WorkerThread |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1718 | @NonNull |
| 1719 | private static Bitmap decodeBitmapImpl(@NonNull Source src, |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1720 | @Nullable OnHeaderDecodedListener listener) throws IOException { |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1721 | try (ImageDecoder decoder = src.createImageDecoder()) { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1722 | decoder.mSource = src; |
| 1723 | decoder.callHeaderDecoded(listener, src); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1724 | |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1725 | // 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 III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1728 | Bitmap bm = decoder.decodeBitmapInternal(); |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1729 | bm.setDensity(srcDensity); |
Leon Scroggins III | 2f1b06b | 2018-02-15 10:40:33 -0500 | [diff] [blame] | 1730 | |
| 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 Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1739 | return bm; |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1740 | } |
| 1741 | } |
| 1742 | |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1743 | // 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 III | 8290eab | 2018-03-19 10:51:44 -0400 | [diff] [blame] | 1763 | // 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 Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1765 | final int dstDensity = src.computeDstDensity(); |
Leon Scroggins III | 8290eab | 2018-03-19 10:51:44 -0400 | [diff] [blame] | 1766 | if (srcDensity == Bitmap.DENSITY_NONE || srcDensity == dstDensity |
| 1767 | || (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P)) { |
| 1768 | return srcDensity; |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1769 | } |
| 1770 | |
Leon Scroggins III | 8290eab | 2018-03-19 10:51:44 -0400 | [diff] [blame] | 1771 | 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 III | 6448119 | 2018-03-28 09:17:42 -0400 | [diff] [blame] | 1774 | decoder.setTargetSize(scaledWidth, scaledHeight); |
Leon Scroggins III | 8290eab | 2018-03-19 10:51:44 -0400 | [diff] [blame] | 1775 | return dstDensity; |
Derek Sollenberger | 66c6d78 | 2018-01-12 16:40:52 -0500 | [diff] [blame] | 1776 | } |
| 1777 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1778 | @NonNull |
Leon Scroggins III | 1fad09d | 2017-12-22 12:54:20 -0500 | [diff] [blame] | 1779 | private String getMimeType() { |
| 1780 | return nGetMimeType(mNativePtr); |
| 1781 | } |
| 1782 | |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1783 | @Nullable |
| 1784 | private ColorSpace getColorSpace() { |
| 1785 | return nGetColorSpace(mNativePtr); |
| 1786 | } |
| 1787 | |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1788 | /** |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1789 | * Create a {@link Bitmap} from a {@code Source}. |
| 1790 | * |
Leon Scroggins III | d84c652 | 2018-03-30 14:56:07 -0400 | [diff] [blame] | 1791 | * <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener}, |
| 1792 | * the default settings will be used. In order to change any settings, call |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1793 | * {@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 III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1799 | */ |
Leon Scroggins III | 0a87cb3 | 2018-03-29 15:54:59 -0400 | [diff] [blame] | 1800 | @WorkerThread |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1801 | @NonNull |
| 1802 | public static Bitmap decodeBitmap(@NonNull Source src) throws IOException { |
Leon Scroggins III | c9aa856 | 2018-02-26 10:28:03 -0500 | [diff] [blame] | 1803 | return decodeBitmapImpl(src, null); |
Leon Scroggins III | b1cc8e6 | 2017-12-22 11:56:59 -0500 | [diff] [blame] | 1804 | } |
| 1805 | |
Leon Scroggins III | 8c9d8f2 | 2018-01-14 14:41:46 -0500 | [diff] [blame] | 1806 | /** |
| 1807 | * Private method called by JNI. |
| 1808 | */ |
| 1809 | @SuppressWarnings("unused") |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1810 | private int postProcessAndRelease(@NonNull Canvas canvas) { |
Leon Scroggins III | 8c9d8f2 | 2018-01-14 14:41:46 -0500 | [diff] [blame] | 1811 | try { |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1812 | return mPostProcessor.onPostProcess(canvas); |
Leon Scroggins III | 8c9d8f2 | 2018-01-14 14:41:46 -0500 | [diff] [blame] | 1813 | } finally { |
| 1814 | canvas.release(); |
| 1815 | } |
| 1816 | } |
| 1817 | |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1818 | /** |
| 1819 | * Private method called by JNI. |
| 1820 | */ |
| 1821 | @SuppressWarnings("unused") |
Leon Scroggins III | cf7294f | 2018-03-22 09:21:29 -0400 | [diff] [blame] | 1822 | private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause) |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 1823 | throws DecodeException { |
| 1824 | DecodeException exception = new DecodeException(error, cause, mSource); |
| 1825 | if (mOnPartialImageListener == null |
| 1826 | || !mOnPartialImageListener.onPartialImage(exception)) { |
| 1827 | throw exception; |
| 1828 | } |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1829 | } |
| 1830 | |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 1831 | 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 III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1838 | // The fd must be seekable. |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 1839 | private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException; |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1840 | @NonNull |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1841 | private static native Bitmap nDecodeBitmap(long nativePtr, |
Leon Scroggins III | 1d2bf2b | 2018-03-14 16:07:43 -0400 | [diff] [blame] | 1842 | @NonNull ImageDecoder decoder, |
| 1843 | boolean doPostProcess, |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1844 | int width, int height, |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1845 | @Nullable Rect cropRect, boolean mutable, |
Leon Scroggins III | d62f272 | 2018-03-29 14:52:29 -0400 | [diff] [blame] | 1846 | int allocator, boolean unpremulRequired, |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1847 | boolean conserveMemory, boolean decodeAsAlphaMask, |
| 1848 | @Nullable ColorSpace desiredColorSpace) |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1849 | throws IOException; |
Leon Scroggins III | e5de9aa | 2018-01-10 20:56:51 -0500 | [diff] [blame] | 1850 | private static native Size nGetSampledSize(long nativePtr, |
| 1851 | int sampleSize); |
| 1852 | private static native void nGetPadding(long nativePtr, @NonNull Rect outRect); |
Leon Scroggins III | ed074fd | 2017-12-11 13:47:23 -0500 | [diff] [blame] | 1853 | private static native void nClose(long nativePtr); |
Leon Scroggins III | 1fad09d | 2017-12-22 12:54:20 -0500 | [diff] [blame] | 1854 | private static native String nGetMimeType(long nativePtr); |
Leon Scroggins III | 1a69f45 | 2018-03-29 09:48:47 -0400 | [diff] [blame] | 1855 | private static native ColorSpace nGetColorSpace(long nativePtr); |
Leon Scroggins III | 0c01dbf | 2017-10-20 14:08:11 -0400 | [diff] [blame] | 1856 | } |