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