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