1 /* 2 * Copyright (C) 2006 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 android.annotation.CheckResult; 20 import android.annotation.ColorInt; 21 import android.annotation.ColorLong; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.WorkerThread; 25 import android.compat.annotation.UnsupportedAppUsage; 26 import android.hardware.HardwareBuffer; 27 import android.os.Build; 28 import android.os.Parcel; 29 import android.os.ParcelFileDescriptor; 30 import android.os.Parcelable; 31 import android.os.SharedMemory; 32 import android.os.StrictMode; 33 import android.os.Trace; 34 import android.util.DisplayMetrics; 35 import android.util.Half; 36 import android.util.Log; 37 import android.view.ThreadedRenderer; 38 39 import dalvik.annotation.optimization.CriticalNative; 40 41 import libcore.util.NativeAllocationRegistry; 42 43 import java.io.IOException; 44 import java.io.OutputStream; 45 import java.lang.ref.WeakReference; 46 import java.nio.Buffer; 47 import java.nio.ByteBuffer; 48 import java.nio.IntBuffer; 49 import java.nio.ShortBuffer; 50 51 public final class Bitmap implements Parcelable { 52 private static final String TAG = "Bitmap"; 53 54 /** 55 * Indicates that the bitmap was created for an unknown pixel density. 56 * 57 * @see Bitmap#getDensity() 58 * @see Bitmap#setDensity(int) 59 */ 60 public static final int DENSITY_NONE = 0; 61 62 // Estimated size of the Bitmap native allocation, not including 63 // pixel data. 64 private static final long NATIVE_ALLOCATION_SIZE = 32; 65 66 // Convenience for JNI access 67 @UnsupportedAppUsage 68 private final long mNativePtr; 69 70 /** 71 * Represents whether the Bitmap's content is requested to be pre-multiplied. 72 * Note that isPremultiplied() does not directly return this value, because 73 * isPremultiplied() may never return true for a 565 Bitmap or a bitmap 74 * without alpha. 75 * 76 * setPremultiplied() does directly set the value so that setConfig() and 77 * setPremultiplied() aren't order dependent, despite being setters. 78 * 79 * The native bitmap's premultiplication state is kept up to date by 80 * pushing down this preference for every config change. 81 */ 82 private boolean mRequestPremultiplied; 83 84 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769491) 85 private byte[] mNinePatchChunk; // may be null 86 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 87 private NinePatch.InsetStruct mNinePatchInsets; // may be null 88 @UnsupportedAppUsage 89 private int mWidth; 90 @UnsupportedAppUsage 91 private int mHeight; 92 private WeakReference<HardwareBuffer> mHardwareBuffer; 93 private boolean mRecycled; 94 95 private ColorSpace mColorSpace; 96 private Gainmap mGainmap; 97 98 /*package*/ int mDensity = getDefaultDensity(); 99 100 private static volatile int sDefaultDensity = -1; 101 102 /** 103 * For backwards compatibility, allows the app layer to change the default 104 * density when running old apps. 105 * @hide 106 */ 107 @UnsupportedAppUsage setDefaultDensity(int density)108 public static void setDefaultDensity(int density) { 109 sDefaultDensity = density; 110 } 111 112 @SuppressWarnings("deprecation") 113 @UnsupportedAppUsage getDefaultDensity()114 static int getDefaultDensity() { 115 if (sDefaultDensity >= 0) { 116 return sDefaultDensity; 117 } 118 sDefaultDensity = DisplayMetrics.DENSITY_DEVICE; 119 return sDefaultDensity; 120 } 121 122 /** 123 * Private constructor that must receive an already allocated native bitmap 124 * int (pointer). 125 */ 126 // JNI now calls the version below this one. This is preserved due to UnsupportedAppUsage. 127 @UnsupportedAppUsage(maxTargetSdk = 28) Bitmap(long nativeBitmap, int width, int height, int density, boolean requestPremultiplied, byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets)128 Bitmap(long nativeBitmap, int width, int height, int density, 129 boolean requestPremultiplied, byte[] ninePatchChunk, 130 NinePatch.InsetStruct ninePatchInsets) { 131 this(nativeBitmap, width, height, density, requestPremultiplied, ninePatchChunk, 132 ninePatchInsets, true); 133 } 134 135 // called from JNI and Bitmap_Delegate. Bitmap(long nativeBitmap, int width, int height, int density, boolean requestPremultiplied, byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets, boolean fromMalloc)136 Bitmap(long nativeBitmap, int width, int height, int density, 137 boolean requestPremultiplied, byte[] ninePatchChunk, 138 NinePatch.InsetStruct ninePatchInsets, boolean fromMalloc) { 139 if (nativeBitmap == 0) { 140 throw new RuntimeException("internal error: native bitmap is 0"); 141 } 142 143 mWidth = width; 144 mHeight = height; 145 mRequestPremultiplied = requestPremultiplied; 146 147 mNinePatchChunk = ninePatchChunk; 148 mNinePatchInsets = ninePatchInsets; 149 if (density >= 0) { 150 mDensity = density; 151 } 152 153 mNativePtr = nativeBitmap; 154 155 final int allocationByteCount = getAllocationByteCount(); 156 NativeAllocationRegistry registry; 157 if (fromMalloc) { 158 registry = NativeAllocationRegistry.createMalloced( 159 Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount); 160 } else { 161 registry = NativeAllocationRegistry.createNonmalloced( 162 Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount); 163 } 164 registry.registerNativeAllocation(this, nativeBitmap); 165 } 166 167 /** 168 * Return the pointer to the native object. 169 * 170 * @hide 171 * Must be public for access from android.graphics.pdf, 172 * but must not be called from outside the UI module. 173 */ getNativeInstance()174 public long getNativeInstance() { 175 return mNativePtr; 176 } 177 178 /** 179 * Native bitmap has been reconfigured, so set premult and cached 180 * width/height values 181 */ 182 @SuppressWarnings("unused") // called from JNI 183 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) reinit(int width, int height, boolean requestPremultiplied)184 void reinit(int width, int height, boolean requestPremultiplied) { 185 mWidth = width; 186 mHeight = height; 187 mRequestPremultiplied = requestPremultiplied; 188 mColorSpace = null; 189 } 190 191 /** 192 * <p>Returns the density for this bitmap.</p> 193 * 194 * <p>The default density is the same density as the current display, 195 * unless the current application does not support different screen 196 * densities in which case it is 197 * {@link android.util.DisplayMetrics#DENSITY_DEFAULT}. Note that 198 * compatibility mode is determined by the application that was initially 199 * loaded into a process -- applications that share the same process should 200 * all have the same compatibility, or ensure they explicitly set the 201 * density of their bitmaps appropriately.</p> 202 * 203 * @return A scaling factor of the default density or {@link #DENSITY_NONE} 204 * if the scaling factor is unknown. 205 * 206 * @see #setDensity(int) 207 * @see android.util.DisplayMetrics#DENSITY_DEFAULT 208 * @see android.util.DisplayMetrics#densityDpi 209 * @see #DENSITY_NONE 210 */ getDensity()211 public int getDensity() { 212 if (mRecycled) { 213 Log.w(TAG, "Called getDensity() on a recycle()'d bitmap! This is undefined behavior!"); 214 } 215 return mDensity; 216 } 217 218 /** 219 * <p>Specifies the density for this bitmap. When the bitmap is 220 * drawn to a Canvas that also has a density, it will be scaled 221 * appropriately.</p> 222 * 223 * @param density The density scaling factor to use with this bitmap or 224 * {@link #DENSITY_NONE} if the density is unknown. 225 * 226 * @see #getDensity() 227 * @see android.util.DisplayMetrics#DENSITY_DEFAULT 228 * @see android.util.DisplayMetrics#densityDpi 229 * @see #DENSITY_NONE 230 */ setDensity(int density)231 public void setDensity(int density) { 232 mDensity = density; 233 } 234 235 /** 236 * <p>Modifies the bitmap to have a specified width, height, and {@link 237 * Config}, without affecting the underlying allocation backing the bitmap. 238 * Bitmap pixel data is not re-initialized for the new configuration.</p> 239 * 240 * <p>This method can be used to avoid allocating a new bitmap, instead 241 * reusing an existing bitmap's allocation for a new configuration of equal 242 * or lesser size. If the Bitmap's allocation isn't large enough to support 243 * the new configuration, an IllegalArgumentException will be thrown and the 244 * bitmap will not be modified.</p> 245 * 246 * <p>The result of {@link #getByteCount()} will reflect the new configuration, 247 * while {@link #getAllocationByteCount()} will reflect that of the initial 248 * configuration.</p> 249 * 250 * <p>Note: This may change this result of hasAlpha(). When converting to 565, 251 * the new bitmap will always be considered opaque. When converting from 565, 252 * the new bitmap will be considered non-opaque, and will respect the value 253 * set by setPremultiplied().</p> 254 * 255 * <p>WARNING: This method should NOT be called on a bitmap currently in use 256 * by the view system, Canvas, or the AndroidBitmap NDK API. It does not 257 * make guarantees about how the underlying pixel buffer is remapped to the 258 * new config, just that the allocation is reused. Additionally, the view 259 * system does not account for bitmap properties being modifying during use, 260 * e.g. while attached to drawables.</p> 261 * 262 * <p>In order to safely ensure that a Bitmap is no longer in use by the 263 * View system it is necessary to wait for a draw pass to occur after 264 * invalidate()'ing any view that had previously drawn the Bitmap in the last 265 * draw pass due to hardware acceleration's caching of draw commands. As 266 * an example, here is how this can be done for an ImageView: 267 * <pre class="prettyprint"> 268 * ImageView myImageView = ...; 269 * final Bitmap myBitmap = ...; 270 * myImageView.setImageDrawable(null); 271 * myImageView.post(new Runnable() { 272 * public void run() { 273 * // myBitmap is now no longer in use by the ImageView 274 * // and can be safely reconfigured. 275 * myBitmap.reconfigure(...); 276 * } 277 * }); 278 * </pre></p> 279 * 280 * @see #setWidth(int) 281 * @see #setHeight(int) 282 * @see #setConfig(Config) 283 */ reconfigure(int width, int height, @NonNull Config config)284 public void reconfigure(int width, int height, @NonNull Config config) { 285 checkRecycled("Can't call reconfigure() on a recycled bitmap"); 286 if (width <= 0 || height <= 0) { 287 throw new IllegalArgumentException("width and height must be > 0"); 288 } 289 if (!isMutable()) { 290 throw new IllegalStateException("only mutable bitmaps may be reconfigured"); 291 } 292 293 nativeReconfigure(mNativePtr, width, height, config.nativeInt, mRequestPremultiplied); 294 mWidth = width; 295 mHeight = height; 296 mColorSpace = null; 297 } 298 299 /** 300 * <p>Convenience method for calling {@link #reconfigure(int, int, Config)} 301 * with the current height and config.</p> 302 * 303 * <p>WARNING: this method should not be used on bitmaps currently used by 304 * the view system, see {@link #reconfigure(int, int, Config)} for more 305 * details.</p> 306 * 307 * @see #reconfigure(int, int, Config) 308 * @see #setHeight(int) 309 * @see #setConfig(Config) 310 */ setWidth(int width)311 public void setWidth(int width) { 312 reconfigure(width, getHeight(), getConfig()); 313 } 314 315 /** 316 * <p>Convenience method for calling {@link #reconfigure(int, int, Config)} 317 * with the current width and config.</p> 318 * 319 * <p>WARNING: this method should not be used on bitmaps currently used by 320 * the view system, see {@link #reconfigure(int, int, Config)} for more 321 * details.</p> 322 * 323 * @see #reconfigure(int, int, Config) 324 * @see #setWidth(int) 325 * @see #setConfig(Config) 326 */ setHeight(int height)327 public void setHeight(int height) { 328 reconfigure(getWidth(), height, getConfig()); 329 } 330 331 /** 332 * <p>Convenience method for calling {@link #reconfigure(int, int, Config)} 333 * with the current height and width.</p> 334 * 335 * <p>WARNING: this method should not be used on bitmaps currently used by 336 * the view system, see {@link #reconfigure(int, int, Config)} for more 337 * details.</p> 338 * 339 * @see #reconfigure(int, int, Config) 340 * @see #setWidth(int) 341 * @see #setHeight(int) 342 */ setConfig(@onNull Config config)343 public void setConfig(@NonNull Config config) { 344 reconfigure(getWidth(), getHeight(), config); 345 } 346 347 /** 348 * Sets the nine patch chunk. 349 * 350 * @param chunk The definition of the nine patch 351 */ 352 @UnsupportedAppUsage setNinePatchChunk(byte[] chunk)353 private void setNinePatchChunk(byte[] chunk) { 354 mNinePatchChunk = chunk; 355 } 356 357 /** 358 * Free the native object associated with this bitmap, and clear the 359 * reference to the pixel data. This will not free the pixel data synchronously; 360 * it simply allows it to be garbage collected if there are no other references. 361 * The bitmap is marked as "dead", meaning it will throw an exception if 362 * getPixels() or setPixels() is called, and will draw nothing. This operation 363 * cannot be reversed, so it should only be called if you are sure there are no 364 * further uses for the bitmap. This is an advanced call, and normally need 365 * not be called, since the normal GC process will free up this memory when 366 * there are no more references to this bitmap. 367 */ recycle()368 public void recycle() { 369 if (!mRecycled) { 370 nativeRecycle(mNativePtr); 371 mNinePatchChunk = null; 372 mRecycled = true; 373 mHardwareBuffer = null; 374 } 375 } 376 377 /** 378 * Returns true if this bitmap has been recycled. If so, then it is an error 379 * to try to access its pixels, and the bitmap will not draw. 380 * 381 * @return true if the bitmap has been recycled 382 */ isRecycled()383 public final boolean isRecycled() { 384 return mRecycled; 385 } 386 387 /** 388 * Returns the generation ID of this bitmap. The generation ID changes 389 * whenever the bitmap is modified. This can be used as an efficient way to 390 * check if a bitmap has changed. 391 * 392 * @return The current generation ID for this bitmap. 393 */ getGenerationId()394 public int getGenerationId() { 395 if (mRecycled) { 396 Log.w(TAG, "Called getGenerationId() on a recycle()'d bitmap! This is undefined behavior!"); 397 } 398 return nativeGenerationId(mNativePtr); 399 } 400 401 /** 402 * This is called by methods that want to throw an exception if the bitmap 403 * has already been recycled. 404 * @hide 405 */ checkRecycled(String errorMessage)406 void checkRecycled(String errorMessage) { 407 if (mRecycled) { 408 throw new IllegalStateException(errorMessage); 409 } 410 } 411 412 /** 413 * This is called by methods that want to throw an exception if the bitmap 414 * is {@link Config#HARDWARE}. 415 */ checkHardware(String errorMessage)416 private void checkHardware(String errorMessage) { 417 if (getConfig() == Config.HARDWARE) { 418 throw new IllegalStateException(errorMessage); 419 } 420 } 421 422 /** 423 * Common code for checking that x and y are >= 0 424 * 425 * @param x x coordinate to ensure is >= 0 426 * @param y y coordinate to ensure is >= 0 427 */ checkXYSign(int x, int y)428 private static void checkXYSign(int x, int y) { 429 if (x < 0) { 430 throw new IllegalArgumentException("x must be >= 0"); 431 } 432 if (y < 0) { 433 throw new IllegalArgumentException("y must be >= 0"); 434 } 435 } 436 437 /** 438 * Common code for checking that width and height are > 0 439 * 440 * @param width width to ensure is > 0 441 * @param height height to ensure is > 0 442 */ checkWidthHeight(int width, int height)443 private static void checkWidthHeight(int width, int height) { 444 if (width <= 0) { 445 throw new IllegalArgumentException("width must be > 0"); 446 } 447 if (height <= 0) { 448 throw new IllegalArgumentException("height must be > 0"); 449 } 450 } 451 452 /** 453 * Possible bitmap configurations. A bitmap configuration describes 454 * how pixels are stored. This affects the quality (color depth) as 455 * well as the ability to display transparent/translucent colors. 456 */ 457 public enum Config { 458 // these native values must match up with the enum in SkBitmap.h 459 460 /** 461 * Each pixel is stored as a single translucency (alpha) channel. 462 * This is very useful to efficiently store masks for instance. 463 * No color information is stored. 464 * With this configuration, each pixel requires 1 byte of memory. 465 */ 466 ALPHA_8(1), 467 468 /** 469 * Each pixel is stored on 2 bytes and only the RGB channels are 470 * encoded: red is stored with 5 bits of precision (32 possible 471 * values), green is stored with 6 bits of precision (64 possible 472 * values) and blue is stored with 5 bits of precision. 473 * 474 * This configuration can produce slight visual artifacts depending 475 * on the configuration of the source. For instance, without 476 * dithering, the result might show a greenish tint. To get better 477 * results dithering should be applied. 478 * 479 * This configuration may be useful when using opaque bitmaps 480 * that do not require high color fidelity. 481 * 482 * <p>Use this formula to pack into 16 bits:</p> 483 * <pre class="prettyprint"> 484 * short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f); 485 * </pre> 486 */ 487 RGB_565(3), 488 489 /** 490 * Each pixel is stored on 2 bytes. The three RGB color channels 491 * and the alpha channel (translucency) are stored with a 4 bits 492 * precision (16 possible values.) 493 * 494 * This configuration is mostly useful if the application needs 495 * to store translucency information but also needs to save 496 * memory. 497 * 498 * It is recommended to use {@link #ARGB_8888} instead of this 499 * configuration. 500 * 501 * Note: as of {@link android.os.Build.VERSION_CODES#KITKAT}, 502 * any bitmap created with this configuration will be created 503 * using {@link #ARGB_8888} instead. 504 * 505 * @deprecated Because of the poor quality of this configuration, 506 * it is advised to use {@link #ARGB_8888} instead. 507 */ 508 @Deprecated 509 ARGB_4444(4), 510 511 /** 512 * Each pixel is stored on 4 bytes. Each channel (RGB and alpha 513 * for translucency) is stored with 8 bits of precision (256 514 * possible values.) 515 * 516 * This configuration is very flexible and offers the best 517 * quality. It should be used whenever possible. 518 * 519 * <p>Use this formula to pack into 32 bits:</p> 520 * <pre class="prettyprint"> 521 * int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff); 522 * </pre> 523 */ 524 ARGB_8888(5), 525 526 /** 527 * Each pixel is stored on 8 bytes. Each channel (RGB and alpha 528 * for translucency) is stored as a 529 * {@link android.util.Half half-precision floating point value}. 530 * 531 * This configuration is particularly suited for wide-gamut and 532 * HDR content. 533 * 534 * <p>Use this formula to pack into 64 bits:</p> 535 * <pre class="prettyprint"> 536 * long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff); 537 * </pre> 538 */ 539 RGBA_F16(6), 540 541 /** 542 * Special configuration, when bitmap is stored only in graphic memory. 543 * Bitmaps in this configuration are always immutable. 544 * 545 * It is optimal for cases, when the only operation with the bitmap is to draw it on a 546 * screen. 547 */ 548 HARDWARE(7), 549 550 /** 551 * Each pixel is stored on 4 bytes. Each RGB channel is stored with 10 bits of precision 552 * (1024 possible values). There is an additional alpha channel that is stored with 2 bits 553 * of precision (4 possible values). 554 * 555 * This configuration is suited for wide-gamut and HDR content which does not require alpha 556 * blending, such that the memory cost is the same as ARGB_8888 while enabling higher color 557 * precision. 558 * 559 * <p>Use this formula to pack into 32 bits:</p> 560 * <pre class="prettyprint"> 561 * int color = (A & 0x3) << 30 | (B & 0x3ff) << 20 | (G & 0x3ff) << 10 | (R & 0x3ff); 562 * </pre> 563 */ 564 RGBA_1010102(8); 565 566 @UnsupportedAppUsage 567 final int nativeInt; 568 569 private static Config sConfigs[] = { 570 null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE, RGBA_1010102 571 }; 572 Config(int ni)573 Config(int ni) { 574 this.nativeInt = ni; 575 } 576 577 @UnsupportedAppUsage nativeToConfig(int ni)578 static Config nativeToConfig(int ni) { 579 return sConfigs[ni]; 580 } 581 } 582 583 /** 584 * <p>Copy the bitmap's pixels into the specified buffer (allocated by the 585 * caller). An exception is thrown if the buffer is not large enough to 586 * hold all of the pixels (taking into account the number of bytes per 587 * pixel) or if the Buffer subclass is not one of the support types 588 * (ByteBuffer, ShortBuffer, IntBuffer).</p> 589 * <p>The content of the bitmap is copied into the buffer as-is. This means 590 * that if this bitmap stores its pixels pre-multiplied 591 * (see {@link #isPremultiplied()}, the values in the buffer will also be 592 * pre-multiplied. The pixels remain in the color space of the bitmap.</p> 593 * <p>After this method returns, the current position of the buffer is 594 * updated: the position is incremented by the number of elements written 595 * in the buffer.</p> 596 * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE} 597 */ copyPixelsToBuffer(@onNull Buffer dst)598 public void copyPixelsToBuffer(@NonNull Buffer dst) { 599 checkHardware("unable to copyPixelsToBuffer, " 600 + "pixel access is not supported on Config#HARDWARE bitmaps"); 601 int elements = dst.remaining(); 602 int shift; 603 if (dst instanceof ByteBuffer) { 604 shift = 0; 605 } else if (dst instanceof ShortBuffer) { 606 shift = 1; 607 } else if (dst instanceof IntBuffer) { 608 shift = 2; 609 } else { 610 throw new RuntimeException("unsupported Buffer subclass"); 611 } 612 613 long bufferSize = (long)elements << shift; 614 long pixelSize = getByteCount(); 615 616 if (bufferSize < pixelSize) { 617 throw new RuntimeException("Buffer not large enough for pixels"); 618 } 619 620 nativeCopyPixelsToBuffer(mNativePtr, dst); 621 622 // now update the buffer's position 623 int position = dst.position(); 624 position += pixelSize >> shift; 625 dst.position(position); 626 } 627 628 /** 629 * <p>Copy the pixels from the buffer, beginning at the current position, 630 * overwriting the bitmap's pixels. The data in the buffer is not changed 631 * in any way (unlike setPixels(), which converts from unpremultipled 32bit 632 * to whatever the bitmap's native format is. The pixels in the source 633 * buffer are assumed to be in the bitmap's color space.</p> 634 * <p>After this method returns, the current position of the buffer is 635 * updated: the position is incremented by the number of elements read from 636 * the buffer. If you need to read the bitmap from the buffer again you must 637 * first rewind the buffer.</p> 638 * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE} 639 */ copyPixelsFromBuffer(@onNull Buffer src)640 public void copyPixelsFromBuffer(@NonNull Buffer src) { 641 checkRecycled("copyPixelsFromBuffer called on recycled bitmap"); 642 checkHardware("unable to copyPixelsFromBuffer, Config#HARDWARE bitmaps are immutable"); 643 644 int elements = src.remaining(); 645 int shift; 646 if (src instanceof ByteBuffer) { 647 shift = 0; 648 } else if (src instanceof ShortBuffer) { 649 shift = 1; 650 } else if (src instanceof IntBuffer) { 651 shift = 2; 652 } else { 653 throw new RuntimeException("unsupported Buffer subclass"); 654 } 655 656 long bufferBytes = (long) elements << shift; 657 long bitmapBytes = getByteCount(); 658 659 if (bufferBytes < bitmapBytes) { 660 throw new RuntimeException("Buffer not large enough for pixels"); 661 } 662 663 nativeCopyPixelsFromBuffer(mNativePtr, src); 664 665 // now update the buffer's position 666 int position = src.position(); 667 position += bitmapBytes >> shift; 668 src.position(position); 669 } 670 noteHardwareBitmapSlowCall()671 private void noteHardwareBitmapSlowCall() { 672 if (getConfig() == Config.HARDWARE) { 673 StrictMode.noteSlowCall("Warning: attempt to read pixels from hardware " 674 + "bitmap, which is very slow operation"); 675 } 676 } 677 678 /** 679 * Tries to make a new bitmap based on the dimensions of this bitmap, 680 * setting the new bitmap's config to the one specified, and then copying 681 * this bitmap's pixels into the new bitmap. If the conversion is not 682 * supported, or the allocator fails, then this returns NULL. The returned 683 * bitmap has the same density and color space as the original, except in 684 * the following cases. When copying to {@link Config#ALPHA_8}, the color 685 * space is dropped. When copying to or from {@link Config#RGBA_F16}, 686 * EXTENDED or non-EXTENDED variants may be adjusted as appropriate. 687 * 688 * @param config The desired config for the resulting bitmap 689 * @param isMutable True if the resulting bitmap should be mutable (i.e. 690 * its pixels can be modified) 691 * @return the new bitmap, or null if the copy could not be made. 692 * @throws IllegalArgumentException if config is {@link Config#HARDWARE} and isMutable is true 693 */ copy(@onNull Config config, boolean isMutable)694 public Bitmap copy(@NonNull Config config, boolean isMutable) { 695 checkRecycled("Can't copy a recycled bitmap"); 696 if (config == Config.HARDWARE && isMutable) { 697 throw new IllegalArgumentException("Hardware bitmaps are always immutable"); 698 } 699 noteHardwareBitmapSlowCall(); 700 Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable); 701 if (b != null) { 702 b.setPremultiplied(mRequestPremultiplied); 703 b.mDensity = mDensity; 704 } 705 return b; 706 } 707 708 /** 709 * Creates a new immutable bitmap backed by ashmem which can efficiently 710 * be passed between processes. 711 * 712 * @hide 713 */ 714 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, 715 publicAlternatives = "Use {@link #asShared()} instead") createAshmemBitmap()716 public Bitmap createAshmemBitmap() { 717 checkRecycled("Can't copy a recycled bitmap"); 718 noteHardwareBitmapSlowCall(); 719 Bitmap b = nativeCopyAshmem(mNativePtr); 720 if (b != null) { 721 b.setPremultiplied(mRequestPremultiplied); 722 b.mDensity = mDensity; 723 } 724 return b; 725 } 726 727 /** 728 * Return an immutable bitmap backed by shared memory which can be 729 * efficiently passed between processes via Parcelable. 730 * 731 * <p>If this bitmap already meets these criteria it will return itself. 732 */ 733 @NonNull asShared()734 public Bitmap asShared() { 735 if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)) { 736 return this; 737 } 738 Bitmap shared = createAshmemBitmap(); 739 if (shared == null) { 740 throw new RuntimeException("Failed to create shared Bitmap!"); 741 } 742 return shared; 743 } 744 745 /** 746 * Returns the shared memory handle to the pixel storage if the bitmap is already using 747 * shared memory and null if it is not. The SharedMemory object is then useful to then pass 748 * through HIDL APIs (e.g. WearOS's DisplayOffload service). 749 * 750 * @hide 751 */ getSharedMemory()752 public SharedMemory getSharedMemory() { 753 checkRecycled("Cannot access shared memory of a recycled bitmap"); 754 if (nativeIsBackedByAshmem(mNativePtr)) { 755 try { 756 int fd = nativeGetAshmemFD(mNativePtr); 757 return SharedMemory.fromFileDescriptor(ParcelFileDescriptor.fromFd(fd)); 758 } catch (IOException e) { 759 Log.e(TAG, "Unable to create dup'd file descriptor for shared bitmap memory"); 760 } 761 } 762 return null; 763 } 764 765 /** 766 * Create a hardware bitmap backed by a {@link HardwareBuffer}. 767 * 768 * <p>The passed HardwareBuffer's usage flags must contain 769 * {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE}. 770 * 771 * <p>The bitmap will keep a reference to the buffer so that callers can safely close the 772 * HardwareBuffer without affecting the Bitmap. However the HardwareBuffer must not be 773 * modified while a wrapped Bitmap is accessing it. Doing so will result in undefined behavior. 774 * 775 * @param hardwareBuffer The HardwareBuffer to wrap. 776 * @param colorSpace The color space of the bitmap. Must be a {@link ColorSpace.Rgb} colorspace. 777 * If null, SRGB is assumed. 778 * @return A bitmap wrapping the buffer, or null if there was a problem creating the bitmap. 779 * @throws IllegalArgumentException if the HardwareBuffer has an invalid usage, or an invalid 780 * colorspace is given. 781 */ 782 @Nullable wrapHardwareBuffer(@onNull HardwareBuffer hardwareBuffer, @Nullable ColorSpace colorSpace)783 public static Bitmap wrapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer, 784 @Nullable ColorSpace colorSpace) { 785 if ((hardwareBuffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) { 786 throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE."); 787 } 788 int format = hardwareBuffer.getFormat(); 789 if (colorSpace == null) { 790 colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); 791 } 792 Bitmap bitmap = nativeWrapHardwareBufferBitmap(hardwareBuffer, 793 colorSpace.getNativeInstance()); 794 if (bitmap != null) { 795 bitmap.mHardwareBuffer = new WeakReference<HardwareBuffer>(hardwareBuffer); 796 } 797 return bitmap; 798 } 799 800 /** 801 * Creates a new bitmap, scaled from an existing bitmap, when possible. If the 802 * specified width and height are the same as the current width and height of 803 * the source bitmap, the source bitmap is returned and no new bitmap is 804 * created. 805 * 806 * @param src The source bitmap. 807 * @param dstWidth The new bitmap's desired width. 808 * @param dstHeight The new bitmap's desired height. 809 * @param filter Whether or not bilinear filtering should be used when scaling the 810 * bitmap. If this is true then bilinear filtering will be used when 811 * scaling which has better image quality at the cost of worse performance. 812 * If this is false then nearest-neighbor scaling is used instead which 813 * will have worse image quality but is faster. Recommended default 814 * is to set filter to 'true' as the cost of bilinear filtering is 815 * typically minimal and the improved image quality is significant. 816 * @return The new scaled bitmap or the source bitmap if no scaling is required. 817 * @throws IllegalArgumentException if width is <= 0, or height is <= 0 818 */ 819 @NonNull createScaledBitmap(@onNull Bitmap src, int dstWidth, int dstHeight, boolean filter)820 public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight, 821 boolean filter) { 822 Matrix m = new Matrix(); 823 824 final int width = src.getWidth(); 825 final int height = src.getHeight(); 826 if (width != dstWidth || height != dstHeight) { 827 final float sx = dstWidth / (float) width; 828 final float sy = dstHeight / (float) height; 829 m.setScale(sx, sy); 830 } 831 return Bitmap.createBitmap(src, 0, 0, width, height, m, filter); 832 } 833 834 /** 835 * Returns a bitmap from the source bitmap. The new bitmap may 836 * be the same object as source, or a copy may have been made. It is 837 * initialized with the same density and color space as the original bitmap. 838 */ 839 @NonNull createBitmap(@onNull Bitmap src)840 public static Bitmap createBitmap(@NonNull Bitmap src) { 841 return createBitmap(src, 0, 0, src.getWidth(), src.getHeight()); 842 } 843 844 /** 845 * Returns a bitmap from the specified subset of the source 846 * bitmap. The new bitmap may be the same object as source, or a copy may 847 * have been made. It is initialized with the same density and color space 848 * as the original bitmap. 849 * 850 * @param source The bitmap we are subsetting 851 * @param x The x coordinate of the first pixel in source 852 * @param y The y coordinate of the first pixel in source 853 * @param width The number of pixels in each row 854 * @param height The number of rows 855 * @return A copy of a subset of the source bitmap or the source bitmap itself. 856 * @throws IllegalArgumentException if the x, y, width, height values are 857 * outside of the dimensions of the source bitmap, or width is <= 0, 858 * or height is <= 0 859 */ 860 @NonNull createBitmap(@onNull Bitmap source, int x, int y, int width, int height)861 public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height) { 862 return createBitmap(source, x, y, width, height, null, false); 863 } 864 865 /** 866 * Returns a bitmap from subset of the source bitmap, 867 * transformed by the optional matrix. The new bitmap may be the 868 * same object as source, or a copy may have been made. It is 869 * initialized with the same density and color space as the original 870 * bitmap. 871 * 872 * If the source bitmap is immutable and the requested subset is the 873 * same as the source bitmap itself, then the source bitmap is 874 * returned and no new bitmap is created. 875 * 876 * The returned bitmap will always be mutable except in the following scenarios: 877 * (1) In situations where the source bitmap is returned and the source bitmap is immutable 878 * 879 * (2) The source bitmap is a hardware bitmap. That is {@link #getConfig()} is equivalent to 880 * {@link Config#HARDWARE} 881 * 882 * @param source The bitmap we are subsetting 883 * @param x The x coordinate of the first pixel in source 884 * @param y The y coordinate of the first pixel in source 885 * @param width The number of pixels in each row 886 * @param height The number of rows 887 * @param m Optional matrix to be applied to the pixels 888 * @param filter true if the source should be filtered. 889 * Only applies if the matrix contains more than just 890 * translation. 891 * @return A bitmap that represents the specified subset of source 892 * @throws IllegalArgumentException if the x, y, width, height values are 893 * outside of the dimensions of the source bitmap, or width is <= 0, 894 * or height is <= 0, or if the source bitmap has already been recycled 895 */ 896 @NonNull createBitmap(@onNull Bitmap source, int x, int y, int width, int height, @Nullable Matrix m, boolean filter)897 public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height, 898 @Nullable Matrix m, boolean filter) { 899 900 checkXYSign(x, y); 901 checkWidthHeight(width, height); 902 if (x + width > source.getWidth()) { 903 throw new IllegalArgumentException("x + width must be <= bitmap.width()"); 904 } 905 if (y + height > source.getHeight()) { 906 throw new IllegalArgumentException("y + height must be <= bitmap.height()"); 907 } 908 if (source.isRecycled()) { 909 throw new IllegalArgumentException("cannot use a recycled source in createBitmap"); 910 } 911 912 // check if we can just return our argument unchanged 913 if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() && 914 height == source.getHeight() && (m == null || m.isIdentity())) { 915 return source; 916 } 917 918 boolean isHardware = source.getConfig() == Config.HARDWARE; 919 if (isHardware) { 920 source.noteHardwareBitmapSlowCall(); 921 source = nativeCopyPreserveInternalConfig(source.mNativePtr); 922 } 923 924 int neww = width; 925 int newh = height; 926 Bitmap bitmap; 927 Paint paint; 928 929 Rect srcR = new Rect(x, y, x + width, y + height); 930 RectF dstR = new RectF(0, 0, width, height); 931 RectF deviceR = new RectF(); 932 933 Config newConfig = Config.ARGB_8888; 934 final Config config = source.getConfig(); 935 // GIF files generate null configs, assume ARGB_8888 936 if (config != null) { 937 switch (config) { 938 case RGB_565: 939 newConfig = Config.RGB_565; 940 break; 941 case ALPHA_8: 942 newConfig = Config.ALPHA_8; 943 break; 944 case RGBA_F16: 945 newConfig = Config.RGBA_F16; 946 break; 947 //noinspection deprecation 948 case ARGB_4444: 949 case ARGB_8888: 950 default: 951 newConfig = Config.ARGB_8888; 952 break; 953 } 954 } 955 956 ColorSpace cs = source.getColorSpace(); 957 958 if (m == null || m.isIdentity()) { 959 bitmap = createBitmap(null, neww, newh, newConfig, source.hasAlpha(), cs); 960 paint = null; // not needed 961 } else { 962 final boolean transformed = !m.rectStaysRect(); 963 964 m.mapRect(deviceR, dstR); 965 966 neww = Math.round(deviceR.width()); 967 newh = Math.round(deviceR.height()); 968 969 Config transformedConfig = newConfig; 970 if (transformed) { 971 if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) { 972 transformedConfig = Config.ARGB_8888; 973 if (cs == null) { 974 cs = ColorSpace.get(ColorSpace.Named.SRGB); 975 } 976 } 977 } 978 979 bitmap = createBitmap(null, neww, newh, transformedConfig, 980 transformed || source.hasAlpha(), cs); 981 982 paint = new Paint(); 983 paint.setFilterBitmap(filter); 984 if (transformed) { 985 paint.setAntiAlias(true); 986 } 987 } 988 989 // The new bitmap was created from a known bitmap source so assume that 990 // they use the same density 991 bitmap.mDensity = source.mDensity; 992 bitmap.setHasAlpha(source.hasAlpha()); 993 bitmap.setPremultiplied(source.mRequestPremultiplied); 994 995 Canvas canvas = new Canvas(bitmap); 996 canvas.translate(-deviceR.left, -deviceR.top); 997 canvas.concat(m); 998 canvas.drawBitmap(source, srcR, dstR, paint); 999 canvas.setBitmap(null); 1000 1001 // If the source has a gainmap, apply the same set of transformations to the gainmap 1002 // and set it on the output 1003 if (source.hasGainmap()) { 1004 Bitmap newMapContents = transformGainmap(source, m, neww, newh, paint, srcR, dstR, 1005 deviceR); 1006 if (newMapContents != null) { 1007 bitmap.setGainmap(new Gainmap(source.getGainmap(), newMapContents)); 1008 } 1009 } 1010 1011 if (isHardware) { 1012 return bitmap.copy(Config.HARDWARE, false); 1013 } 1014 return bitmap; 1015 } 1016 transformGainmap(Bitmap source, Matrix m, int neww, int newh, Paint paint, Rect srcR, RectF dstR, RectF deviceR)1017 private static Bitmap transformGainmap(Bitmap source, Matrix m, int neww, int newh, Paint paint, 1018 Rect srcR, RectF dstR, RectF deviceR) { 1019 Canvas canvas; 1020 Bitmap sourceGainmap = source.getGainmap().getGainmapContents(); 1021 // Gainmaps can be scaled relative to the base image (eg, 1/4th res) 1022 // Preserve that relative scaling between the base & gainmap in the output 1023 float scaleX = (sourceGainmap.getWidth() / (float) source.getWidth()); 1024 float scaleY = (sourceGainmap.getHeight() / (float) source.getHeight()); 1025 int mapw = Math.round(neww * scaleX); 1026 int maph = Math.round(newh * scaleY); 1027 1028 if (mapw == 0 || maph == 0) { 1029 // The gainmap has been scaled away entirely, drop it 1030 return null; 1031 } 1032 1033 // Scale the computed `srcR` used for rendering the source bitmap to the destination 1034 // to be in gainmap dimensions 1035 Rect gSrcR = new Rect((int) (srcR.left * scaleX), 1036 (int) (srcR.top * scaleY), (int) (srcR.right * scaleX), 1037 (int) (srcR.bottom * scaleY)); 1038 1039 // Note: createBitmap isn't used as that requires a non-null colorspace, however 1040 // gainmaps don't have a colorspace. So use `nativeCreate` directly to bypass 1041 // that colorspace enforcement requirement (#getColorSpace() allows a null return) 1042 Bitmap newMapContents = nativeCreate(null, 0, mapw, mapw, maph, 1043 sourceGainmap.getConfig().nativeInt, true, 0); 1044 newMapContents.eraseColor(0); 1045 canvas = new Canvas(newMapContents); 1046 // Scale the translate & matrix to be in gainmap-relative dimensions 1047 canvas.scale(scaleX, scaleY); 1048 canvas.translate(-deviceR.left, -deviceR.top); 1049 canvas.concat(m); 1050 canvas.drawBitmap(sourceGainmap, gSrcR, dstR, paint); 1051 canvas.setBitmap(null); 1052 // Create a new gainmap using a copy of the metadata information from the source but 1053 // with the transformed bitmap created above 1054 return newMapContents; 1055 } 1056 1057 /** 1058 * Returns a mutable bitmap with the specified width and height. Its 1059 * initial density is as per {@link #getDensity}. The newly created 1060 * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space. 1061 * 1062 * @param width The width of the bitmap 1063 * @param height The height of the bitmap 1064 * @param config The bitmap config to create. 1065 * @throws IllegalArgumentException if the width or height are <= 0, or if 1066 * Config is Config.HARDWARE, because hardware bitmaps are always immutable 1067 */ 1068 @NonNull createBitmap(int width, int height, @NonNull Config config)1069 public static Bitmap createBitmap(int width, int height, @NonNull Config config) { 1070 return createBitmap(width, height, config, true); 1071 } 1072 1073 /** 1074 * Returns a mutable bitmap with the specified width and height. Its 1075 * initial density is determined from the given {@link DisplayMetrics}. 1076 * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB} 1077 * color space. 1078 * 1079 * @param display Display metrics for the display this bitmap will be 1080 * drawn on. 1081 * @param width The width of the bitmap 1082 * @param height The height of the bitmap 1083 * @param config The bitmap config to create. 1084 * @throws IllegalArgumentException if the width or height are <= 0, or if 1085 * Config is Config.HARDWARE, because hardware bitmaps are always immutable 1086 */ 1087 @NonNull createBitmap(@ullable DisplayMetrics display, int width, int height, @NonNull Config config)1088 public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, 1089 int height, @NonNull Config config) { 1090 return createBitmap(display, width, height, config, true); 1091 } 1092 1093 /** 1094 * Returns a mutable bitmap with the specified width and height. Its 1095 * initial density is as per {@link #getDensity}. The newly created 1096 * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space. 1097 * 1098 * @param width The width of the bitmap 1099 * @param height The height of the bitmap 1100 * @param config The bitmap config to create. 1101 * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be 1102 * used to mark the bitmap as opaque. Doing so will clear the bitmap in black 1103 * instead of transparent. 1104 * 1105 * @throws IllegalArgumentException if the width or height are <= 0, or if 1106 * Config is Config.HARDWARE, because hardware bitmaps are always immutable 1107 */ 1108 @NonNull createBitmap(int width, int height, @NonNull Config config, boolean hasAlpha)1109 public static Bitmap createBitmap(int width, int height, 1110 @NonNull Config config, boolean hasAlpha) { 1111 return createBitmap(null, width, height, config, hasAlpha); 1112 } 1113 1114 /** 1115 * Returns a mutable bitmap with the specified width and height. Its 1116 * initial density is as per {@link #getDensity}. 1117 * 1118 * @param width The width of the bitmap 1119 * @param height The height of the bitmap 1120 * @param config The bitmap config to create. 1121 * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be 1122 * used to mark the bitmap as opaque. Doing so will clear the bitmap in black 1123 * instead of transparent. 1124 * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16} 1125 * and {@link ColorSpace.Named#SRGB sRGB} or 1126 * {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the 1127 * corresponding extended range variant is assumed. 1128 * 1129 * @throws IllegalArgumentException if the width or height are <= 0, if 1130 * Config is Config.HARDWARE (because hardware bitmaps are always 1131 * immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB}, 1132 * if the specified color space's transfer function is not an 1133 * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if 1134 * the color space is null 1135 */ 1136 @NonNull createBitmap(int width, int height, @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace)1137 public static Bitmap createBitmap(int width, int height, @NonNull Config config, 1138 boolean hasAlpha, @NonNull ColorSpace colorSpace) { 1139 return createBitmap(null, width, height, config, hasAlpha, colorSpace); 1140 } 1141 1142 /** 1143 * Returns a mutable bitmap with the specified width and height. Its 1144 * initial density is determined from the given {@link DisplayMetrics}. 1145 * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB} 1146 * color space. 1147 * 1148 * @param display Display metrics for the display this bitmap will be 1149 * drawn on. 1150 * @param width The width of the bitmap 1151 * @param height The height of the bitmap 1152 * @param config The bitmap config to create. 1153 * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be 1154 * used to mark the bitmap as opaque. Doing so will clear the bitmap in black 1155 * instead of transparent. 1156 * 1157 * @throws IllegalArgumentException if the width or height are <= 0, or if 1158 * Config is Config.HARDWARE, because hardware bitmaps are always immutable 1159 */ 1160 @NonNull createBitmap(@ullable DisplayMetrics display, int width, int height, @NonNull Config config, boolean hasAlpha)1161 public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height, 1162 @NonNull Config config, boolean hasAlpha) { 1163 return createBitmap(display, width, height, config, hasAlpha, 1164 ColorSpace.get(ColorSpace.Named.SRGB)); 1165 } 1166 1167 /** 1168 * Returns a mutable bitmap with the specified width and height. Its 1169 * initial density is determined from the given {@link DisplayMetrics}. 1170 * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB} 1171 * color space. 1172 * 1173 * @param display Display metrics for the display this bitmap will be 1174 * drawn on. 1175 * @param width The width of the bitmap 1176 * @param height The height of the bitmap 1177 * @param config The bitmap config to create. 1178 * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be 1179 * used to mark the bitmap as opaque. Doing so will clear the bitmap in black 1180 * instead of transparent. 1181 * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16} 1182 * and {@link ColorSpace.Named#SRGB sRGB} or 1183 * {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the 1184 * corresponding extended range variant is assumed. 1185 * 1186 * @throws IllegalArgumentException if the width or height are <= 0, if 1187 * Config is Config.HARDWARE (because hardware bitmaps are always 1188 * immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB}, 1189 * if the specified color space's transfer function is not an 1190 * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if 1191 * the color space is null 1192 */ 1193 @NonNull createBitmap(@ullable DisplayMetrics display, int width, int height, @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace)1194 public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height, 1195 @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) { 1196 if (width <= 0 || height <= 0) { 1197 throw new IllegalArgumentException("width and height must be > 0"); 1198 } 1199 if (config == Config.HARDWARE) { 1200 throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE"); 1201 } 1202 if (colorSpace == null && config != Config.ALPHA_8) { 1203 throw new IllegalArgumentException("can't create bitmap without a color space"); 1204 } 1205 1206 Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, 1207 colorSpace == null ? 0 : colorSpace.getNativeInstance()); 1208 1209 if (display != null) { 1210 bm.mDensity = display.densityDpi; 1211 } 1212 bm.setHasAlpha(hasAlpha); 1213 if ((config == Config.ARGB_8888 || config == Config.RGBA_F16) && !hasAlpha) { 1214 nativeErase(bm.mNativePtr, 0xff000000); 1215 } 1216 // No need to initialize the bitmap to zeroes with other configs; 1217 // it is backed by a VM byte array which is by definition preinitialized 1218 // to all zeroes. 1219 return bm; 1220 } 1221 1222 /** 1223 * Returns a immutable bitmap with the specified width and height, with each 1224 * pixel value set to the corresponding value in the colors array. Its 1225 * initial density is as per {@link #getDensity}. The newly created 1226 * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space. 1227 * 1228 * @param colors Array of sRGB {@link Color colors} used to initialize the pixels. 1229 * @param offset Number of values to skip before the first color in the 1230 * array of colors. 1231 * @param stride Number of colors in the array between rows (must be >= 1232 * width or <= -width). 1233 * @param width The width of the bitmap 1234 * @param height The height of the bitmap 1235 * @param config The bitmap config to create. If the config does not 1236 * support per-pixel alpha (e.g. RGB_565), then the alpha 1237 * bytes in the colors[] will be ignored (assumed to be FF) 1238 * @throws IllegalArgumentException if the width or height are <= 0, or if 1239 * the color array's length is less than the number of pixels. 1240 */ 1241 @NonNull createBitmap(@onNull @olorInt int[] colors, int offset, int stride, int width, int height, @NonNull Config config)1242 public static Bitmap createBitmap(@NonNull @ColorInt int[] colors, int offset, int stride, 1243 int width, int height, @NonNull Config config) { 1244 return createBitmap(null, colors, offset, stride, width, height, config); 1245 } 1246 1247 /** 1248 * Returns a immutable bitmap with the specified width and height, with each 1249 * pixel value set to the corresponding value in the colors array. Its 1250 * initial density is determined from the given {@link DisplayMetrics}. 1251 * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB} 1252 * color space. 1253 * 1254 * @param display Display metrics for the display this bitmap will be 1255 * drawn on. 1256 * @param colors Array of sRGB {@link Color colors} used to initialize the pixels. 1257 * @param offset Number of values to skip before the first color in the 1258 * array of colors. 1259 * @param stride Number of colors in the array between rows (must be >= 1260 * width or <= -width). 1261 * @param width The width of the bitmap 1262 * @param height The height of the bitmap 1263 * @param config The bitmap config to create. If the config does not 1264 * support per-pixel alpha (e.g. RGB_565), then the alpha 1265 * bytes in the colors[] will be ignored (assumed to be FF) 1266 * @throws IllegalArgumentException if the width or height are <= 0, or if 1267 * the color array's length is less than the number of pixels. 1268 */ 1269 @NonNull createBitmap(@onNull DisplayMetrics display, @NonNull @ColorInt int[] colors, int offset, int stride, int width, int height, @NonNull Config config)1270 public static Bitmap createBitmap(@NonNull DisplayMetrics display, 1271 @NonNull @ColorInt int[] colors, int offset, int stride, 1272 int width, int height, @NonNull Config config) { 1273 1274 checkWidthHeight(width, height); 1275 if (Math.abs(stride) < width) { 1276 throw new IllegalArgumentException("abs(stride) must be >= width"); 1277 } 1278 int lastScanline = offset + (height - 1) * stride; 1279 int length = colors.length; 1280 if (offset < 0 || (offset + width > length) || lastScanline < 0 || 1281 (lastScanline + width > length)) { 1282 throw new ArrayIndexOutOfBoundsException(); 1283 } 1284 if (width <= 0 || height <= 0) { 1285 throw new IllegalArgumentException("width and height must be > 0"); 1286 } 1287 ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB); 1288 Bitmap bm = nativeCreate(colors, offset, stride, width, height, 1289 config.nativeInt, false, sRGB.getNativeInstance()); 1290 if (display != null) { 1291 bm.mDensity = display.densityDpi; 1292 } 1293 return bm; 1294 } 1295 1296 /** 1297 * Returns a immutable bitmap with the specified width and height, with each 1298 * pixel value set to the corresponding value in the colors array. Its 1299 * initial density is as per {@link #getDensity}. The newly created 1300 * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space. 1301 * 1302 * @param colors Array of sRGB {@link Color colors} used to initialize the pixels. 1303 * This array must be at least as large as width * height. 1304 * @param width The width of the bitmap 1305 * @param height The height of the bitmap 1306 * @param config The bitmap config to create. If the config does not 1307 * support per-pixel alpha (e.g. RGB_565), then the alpha 1308 * bytes in the colors[] will be ignored (assumed to be FF) 1309 * @throws IllegalArgumentException if the width or height are <= 0, or if 1310 * the color array's length is less than the number of pixels. 1311 */ 1312 @NonNull createBitmap(@onNull @olorInt int[] colors, int width, int height, Config config)1313 public static Bitmap createBitmap(@NonNull @ColorInt int[] colors, 1314 int width, int height, Config config) { 1315 return createBitmap(null, colors, 0, width, width, height, config); 1316 } 1317 1318 /** 1319 * Returns a immutable bitmap with the specified width and height, with each 1320 * pixel value set to the corresponding value in the colors array. Its 1321 * initial density is determined from the given {@link DisplayMetrics}. 1322 * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB} 1323 * color space. 1324 * 1325 * @param display Display metrics for the display this bitmap will be 1326 * drawn on. 1327 * @param colors Array of sRGB {@link Color colors} used to initialize the pixels. 1328 * This array must be at least as large as width * height. 1329 * @param width The width of the bitmap 1330 * @param height The height of the bitmap 1331 * @param config The bitmap config to create. If the config does not 1332 * support per-pixel alpha (e.g. RGB_565), then the alpha 1333 * bytes in the colors[] will be ignored (assumed to be FF) 1334 * @throws IllegalArgumentException if the width or height are <= 0, or if 1335 * the color array's length is less than the number of pixels. 1336 */ 1337 @NonNull createBitmap(@ullable DisplayMetrics display, @NonNull @ColorInt int colors[], int width, int height, @NonNull Config config)1338 public static Bitmap createBitmap(@Nullable DisplayMetrics display, 1339 @NonNull @ColorInt int colors[], int width, int height, @NonNull Config config) { 1340 return createBitmap(display, colors, 0, width, width, height, config); 1341 } 1342 1343 /** 1344 * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands. 1345 * 1346 * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with 1347 * width and height the same as the Picture's width and height and a Config.HARDWARE 1348 * config. 1349 * 1350 * @param source The recorded {@link Picture} of drawing commands that will be 1351 * drawn into the returned Bitmap. 1352 * @return An immutable bitmap with a HARDWARE config whose contents are created 1353 * from the recorded drawing commands in the Picture source. 1354 */ 1355 @NonNull createBitmap(@onNull Picture source)1356 public static Bitmap createBitmap(@NonNull Picture source) { 1357 return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE); 1358 } 1359 1360 /** 1361 * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands. 1362 * 1363 * The bitmap will be immutable with the given width and height. If the width and height 1364 * are not the same as the Picture's width & height, the Picture will be scaled to 1365 * fit the given width and height. 1366 * 1367 * @param source The recorded {@link Picture} of drawing commands that will be 1368 * drawn into the returned Bitmap. 1369 * @param width The width of the bitmap to create. The picture's width will be 1370 * scaled to match if necessary. 1371 * @param height The height of the bitmap to create. The picture's height will be 1372 * scaled to match if necessary. 1373 * @param config The {@link Config} of the created bitmap. 1374 * 1375 * @return An immutable bitmap with a configuration specified by the config parameter 1376 */ 1377 @NonNull createBitmap(@onNull Picture source, int width, int height, @NonNull Config config)1378 public static Bitmap createBitmap(@NonNull Picture source, int width, int height, 1379 @NonNull Config config) { 1380 if (width <= 0 || height <= 0) { 1381 throw new IllegalArgumentException("width & height must be > 0"); 1382 } 1383 if (config == null) { 1384 throw new IllegalArgumentException("Config must not be null"); 1385 } 1386 source.endRecording(); 1387 if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) { 1388 StrictMode.noteSlowCall("GPU readback"); 1389 } 1390 if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) { 1391 final RenderNode node = RenderNode.create("BitmapTemporary", null); 1392 node.setLeftTopRightBottom(0, 0, width, height); 1393 node.setClipToBounds(false); 1394 node.setForceDarkAllowed(false); 1395 final RecordingCanvas canvas = node.beginRecording(width, height); 1396 if (source.getWidth() != width || source.getHeight() != height) { 1397 canvas.scale(width / (float) source.getWidth(), 1398 height / (float) source.getHeight()); 1399 } 1400 canvas.drawPicture(source); 1401 node.endRecording(); 1402 Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height); 1403 if (config != Config.HARDWARE) { 1404 bitmap = bitmap.copy(config, false); 1405 } 1406 return bitmap; 1407 } else { 1408 Bitmap bitmap = Bitmap.createBitmap(width, height, config); 1409 Canvas canvas = new Canvas(bitmap); 1410 if (source.getWidth() != width || source.getHeight() != height) { 1411 canvas.scale(width / (float) source.getWidth(), 1412 height / (float) source.getHeight()); 1413 } 1414 canvas.drawPicture(source); 1415 canvas.setBitmap(null); 1416 bitmap.setImmutable(); 1417 return bitmap; 1418 } 1419 } 1420 1421 /** 1422 * Returns an optional array of private data, used by the UI system for 1423 * some bitmaps. Not intended to be called by applications. 1424 */ 1425 @Nullable getNinePatchChunk()1426 public byte[] getNinePatchChunk() { 1427 return mNinePatchChunk; 1428 } 1429 1430 /** 1431 * Populates a rectangle with the bitmap's optical insets. 1432 * 1433 * @param outInsets Rect to populate with optical insets 1434 * 1435 * @hide 1436 * Must be public for access from android.graphics.drawable, 1437 * but must not be called from outside the UI module. 1438 */ getOpticalInsets(@onNull Rect outInsets)1439 public void getOpticalInsets(@NonNull Rect outInsets) { 1440 if (mNinePatchInsets == null) { 1441 outInsets.setEmpty(); 1442 } else { 1443 outInsets.set(mNinePatchInsets.opticalRect); 1444 } 1445 } 1446 1447 /** 1448 * @hide 1449 * Must be public for access from android.graphics.drawable, 1450 * but must not be called from outside the UI module. 1451 */ getNinePatchInsets()1452 public NinePatch.InsetStruct getNinePatchInsets() { 1453 return mNinePatchInsets; 1454 } 1455 1456 /** 1457 * Specifies the known formats a bitmap can be compressed into 1458 */ 1459 public enum CompressFormat { 1460 /** 1461 * Compress to the JPEG format. {@code quality} of {@code 0} means 1462 * compress for the smallest size. {@code 100} means compress for max 1463 * visual quality. 1464 */ 1465 JPEG (0), 1466 /** 1467 * Compress to the PNG format. PNG is lossless, so {@code quality} is 1468 * ignored. 1469 */ 1470 PNG (1), 1471 /** 1472 * Compress to the WEBP format. {@code quality} of {@code 0} means 1473 * compress for the smallest size. {@code 100} means compress for max 1474 * visual quality. As of {@link android.os.Build.VERSION_CODES#Q}, a 1475 * value of {@code 100} results in a file in the lossless WEBP format. 1476 * Otherwise the file will be in the lossy WEBP format. 1477 * 1478 * @deprecated in favor of the more explicit 1479 * {@link CompressFormat#WEBP_LOSSY} and 1480 * {@link CompressFormat#WEBP_LOSSLESS}. 1481 */ 1482 @Deprecated 1483 WEBP (2), 1484 /** 1485 * Compress to the WEBP lossy format. {@code quality} of {@code 0} means 1486 * compress for the smallest size. {@code 100} means compress for max 1487 * visual quality. 1488 */ 1489 WEBP_LOSSY (3), 1490 /** 1491 * Compress to the WEBP lossless format. {@code quality} refers to how 1492 * much effort to put into compression. A value of {@code 0} means to 1493 * compress quickly, resulting in a relatively large file size. 1494 * {@code 100} means to spend more time compressing, resulting in a 1495 * smaller file. 1496 */ 1497 WEBP_LOSSLESS (4); 1498 CompressFormat(int nativeInt)1499 CompressFormat(int nativeInt) { 1500 this.nativeInt = nativeInt; 1501 } 1502 final int nativeInt; 1503 } 1504 1505 /** 1506 * Number of bytes of temp storage we use for communicating between the 1507 * native compressor and the java OutputStream. 1508 */ 1509 private final static int WORKING_COMPRESS_STORAGE = 4096; 1510 1511 /** 1512 * Write a compressed version of the bitmap to the specified outputstream. 1513 * If this returns true, the bitmap can be reconstructed by passing a 1514 * corresponding inputstream to BitmapFactory.decodeStream(). Note: not 1515 * all Formats support all bitmap configs directly, so it is possible that 1516 * the returned bitmap from BitmapFactory could be in a different bitdepth, 1517 * and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque 1518 * pixels). 1519 * 1520 * @param format The format of the compressed image 1521 * @param quality Hint to the compressor, 0-100. The value is interpreted 1522 * differently depending on the {@link CompressFormat}. 1523 * @param stream The outputstream to write the compressed data. 1524 * @return true if successfully compressed to the specified stream. 1525 */ 1526 @WorkerThread compress(@onNull CompressFormat format, int quality, @NonNull OutputStream stream)1527 public boolean compress(@NonNull CompressFormat format, int quality, 1528 @NonNull OutputStream stream) { 1529 checkRecycled("Can't compress a recycled bitmap"); 1530 // do explicit check before calling the native method 1531 if (stream == null) { 1532 throw new NullPointerException(); 1533 } 1534 if (quality < 0 || quality > 100) { 1535 throw new IllegalArgumentException("quality must be 0..100"); 1536 } 1537 StrictMode.noteSlowCall("Compression of a bitmap is slow"); 1538 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress"); 1539 boolean result = nativeCompress(mNativePtr, format.nativeInt, 1540 quality, stream, new byte[WORKING_COMPRESS_STORAGE]); 1541 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); 1542 return result; 1543 } 1544 1545 /** 1546 * Returns true if the bitmap is marked as mutable (i.e. can be drawn into) 1547 */ isMutable()1548 public final boolean isMutable() { 1549 return !nativeIsImmutable(mNativePtr); 1550 } 1551 1552 /** 1553 * Marks the Bitmap as immutable. Further modifications to this Bitmap are disallowed. 1554 * After this method is called, this Bitmap cannot be made mutable again and subsequent calls 1555 * to {@link #reconfigure(int, int, Config)}, {@link #setPixel(int, int, int)}, 1556 * {@link #setPixels(int[], int, int, int, int, int, int)} and {@link #eraseColor(int)} will 1557 * fail and throw an IllegalStateException. 1558 */ setImmutable()1559 private void setImmutable() { 1560 if (isMutable()) { 1561 nativeSetImmutable(mNativePtr); 1562 } 1563 } 1564 1565 /** 1566 * <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied. 1567 * When a pixel is pre-multiplied, the RGB components have been multiplied by 1568 * the alpha component. For instance, if the original color is a 50% 1569 * translucent red <code>(128, 255, 0, 0)</code>, the pre-multiplied form is 1570 * <code>(128, 128, 0, 0)</code>.</p> 1571 * 1572 * <p>This method always returns false if {@link #getConfig()} is 1573 * {@link Bitmap.Config#RGB_565}.</p> 1574 * 1575 * <p>The return value is undefined if {@link #getConfig()} is 1576 * {@link Bitmap.Config#ALPHA_8}.</p> 1577 * 1578 * <p>This method only returns true if {@link #hasAlpha()} returns true. 1579 * A bitmap with no alpha channel can be used both as a pre-multiplied and 1580 * as a non pre-multiplied bitmap.</p> 1581 * 1582 * <p>Only pre-multiplied bitmaps may be drawn by the view system or 1583 * {@link Canvas}. If a non-pre-multiplied bitmap with an alpha channel is 1584 * drawn to a Canvas, a RuntimeException will be thrown.</p> 1585 * 1586 * @return true if the underlying pixels have been pre-multiplied, false 1587 * otherwise 1588 * 1589 * @see Bitmap#setPremultiplied(boolean) 1590 * @see BitmapFactory.Options#inPremultiplied 1591 */ isPremultiplied()1592 public final boolean isPremultiplied() { 1593 if (mRecycled) { 1594 Log.w(TAG, "Called isPremultiplied() on a recycle()'d bitmap! This is undefined behavior!"); 1595 } 1596 return nativeIsPremultiplied(mNativePtr); 1597 } 1598 1599 /** 1600 * Sets whether the bitmap should treat its data as pre-multiplied. 1601 * 1602 * <p>Bitmaps are always treated as pre-multiplied by the view system and 1603 * {@link Canvas} for performance reasons. Storing un-pre-multiplied data in 1604 * a Bitmap (through {@link #setPixel}, {@link #setPixels}, or {@link 1605 * BitmapFactory.Options#inPremultiplied BitmapFactory.Options.inPremultiplied}) 1606 * can lead to incorrect blending if drawn by the framework.</p> 1607 * 1608 * <p>This method will not affect the behavior of a bitmap without an alpha 1609 * channel, or if {@link #hasAlpha()} returns false.</p> 1610 * 1611 * <p>Calling {@link #createBitmap} or {@link #createScaledBitmap} with a source 1612 * Bitmap whose colors are not pre-multiplied may result in a RuntimeException, 1613 * since those functions require drawing the source, which is not supported for 1614 * un-pre-multiplied Bitmaps.</p> 1615 * 1616 * @see Bitmap#isPremultiplied() 1617 * @see BitmapFactory.Options#inPremultiplied 1618 */ setPremultiplied(boolean premultiplied)1619 public final void setPremultiplied(boolean premultiplied) { 1620 checkRecycled("setPremultiplied called on a recycled bitmap"); 1621 mRequestPremultiplied = premultiplied; 1622 nativeSetPremultiplied(mNativePtr, premultiplied); 1623 } 1624 1625 /** Returns the bitmap's width */ getWidth()1626 public final int getWidth() { 1627 if (mRecycled) { 1628 Log.w(TAG, "Called getWidth() on a recycle()'d bitmap! This is undefined behavior!"); 1629 } 1630 return mWidth; 1631 } 1632 1633 /** Returns the bitmap's height */ getHeight()1634 public final int getHeight() { 1635 if (mRecycled) { 1636 Log.w(TAG, "Called getHeight() on a recycle()'d bitmap! This is undefined behavior!"); 1637 } 1638 return mHeight; 1639 } 1640 1641 /** 1642 * Convenience for calling {@link #getScaledWidth(int)} with the target 1643 * density of the given {@link Canvas}. 1644 */ getScaledWidth(@onNull Canvas canvas)1645 public int getScaledWidth(@NonNull Canvas canvas) { 1646 return scaleFromDensity(getWidth(), mDensity, canvas.mDensity); 1647 } 1648 1649 /** 1650 * Convenience for calling {@link #getScaledHeight(int)} with the target 1651 * density of the given {@link Canvas}. 1652 */ getScaledHeight(@onNull Canvas canvas)1653 public int getScaledHeight(@NonNull Canvas canvas) { 1654 return scaleFromDensity(getHeight(), mDensity, canvas.mDensity); 1655 } 1656 1657 /** 1658 * Convenience for calling {@link #getScaledWidth(int)} with the target 1659 * density of the given {@link DisplayMetrics}. 1660 */ getScaledWidth(@onNull DisplayMetrics metrics)1661 public int getScaledWidth(@NonNull DisplayMetrics metrics) { 1662 return scaleFromDensity(getWidth(), mDensity, metrics.densityDpi); 1663 } 1664 1665 /** 1666 * Convenience for calling {@link #getScaledHeight(int)} with the target 1667 * density of the given {@link DisplayMetrics}. 1668 */ getScaledHeight(@onNull DisplayMetrics metrics)1669 public int getScaledHeight(@NonNull DisplayMetrics metrics) { 1670 return scaleFromDensity(getHeight(), mDensity, metrics.densityDpi); 1671 } 1672 1673 /** 1674 * Convenience method that returns the width of this bitmap divided 1675 * by the density scale factor. 1676 * 1677 * Returns the bitmap's width multiplied by the ratio of the target density to the bitmap's 1678 * source density 1679 * 1680 * @param targetDensity The density of the target canvas of the bitmap. 1681 * @return The scaled width of this bitmap, according to the density scale factor. 1682 */ getScaledWidth(int targetDensity)1683 public int getScaledWidth(int targetDensity) { 1684 return scaleFromDensity(getWidth(), mDensity, targetDensity); 1685 } 1686 1687 /** 1688 * Convenience method that returns the height of this bitmap divided 1689 * by the density scale factor. 1690 * 1691 * Returns the bitmap's height multiplied by the ratio of the target density to the bitmap's 1692 * source density 1693 * 1694 * @param targetDensity The density of the target canvas of the bitmap. 1695 * @return The scaled height of this bitmap, according to the density scale factor. 1696 */ getScaledHeight(int targetDensity)1697 public int getScaledHeight(int targetDensity) { 1698 return scaleFromDensity(getHeight(), mDensity, targetDensity); 1699 } 1700 1701 /** 1702 * @hide 1703 * Must be public for access from android.graphics.drawable, 1704 * but must not be called from outside the UI module. 1705 */ 1706 @UnsupportedAppUsage scaleFromDensity(int size, int sdensity, int tdensity)1707 static public int scaleFromDensity(int size, int sdensity, int tdensity) { 1708 if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) { 1709 return size; 1710 } 1711 1712 // Scale by tdensity / sdensity, rounding up. 1713 return ((size * tdensity) + (sdensity >> 1)) / sdensity; 1714 } 1715 1716 /** 1717 * Return the number of bytes between rows in the bitmap's pixels. Note that 1718 * this refers to the pixels as stored natively by the bitmap. If you call 1719 * getPixels() or setPixels(), then the pixels are uniformly treated as 1720 * 32bit values, packed according to the Color class. 1721 * 1722 * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, this method 1723 * should not be used to calculate the memory usage of the bitmap. Instead, 1724 * see {@link #getAllocationByteCount()}. 1725 * 1726 * @return number of bytes between rows of the native bitmap pixels. 1727 */ getRowBytes()1728 public final int getRowBytes() { 1729 if (mRecycled) { 1730 Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!"); 1731 } 1732 return nativeRowBytes(mNativePtr); 1733 } 1734 1735 /** 1736 * Returns the minimum number of bytes that can be used to store this bitmap's pixels. 1737 * 1738 * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, the result of this method can 1739 * no longer be used to determine memory usage of a bitmap. See {@link 1740 * #getAllocationByteCount()}.</p> 1741 */ getByteCount()1742 public final int getByteCount() { 1743 if (mRecycled) { 1744 Log.w(TAG, "Called getByteCount() on a recycle()'d bitmap! " 1745 + "This is undefined behavior!"); 1746 return 0; 1747 } 1748 // int result permits bitmaps up to 46,340 x 46,340 1749 return getRowBytes() * getHeight(); 1750 } 1751 1752 /** 1753 * Returns the size of the allocated memory used to store this bitmap's pixels. 1754 * 1755 * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to 1756 * decode other bitmaps of smaller size, or by manual reconfiguration. See {@link 1757 * #reconfigure(int, int, Config)}, {@link #setWidth(int)}, {@link #setHeight(int)}, {@link 1758 * #setConfig(Bitmap.Config)}, and {@link BitmapFactory.Options#inBitmap 1759 * BitmapFactory.Options.inBitmap}. If a bitmap is not modified in this way, this value will be 1760 * the same as that returned by {@link #getByteCount()}.</p> 1761 * 1762 * <p>This value will not change over the lifetime of a Bitmap.</p> 1763 * 1764 * @see #reconfigure(int, int, Config) 1765 */ getAllocationByteCount()1766 public final int getAllocationByteCount() { 1767 if (mRecycled) { 1768 Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! " 1769 + "This is undefined behavior!"); 1770 return 0; 1771 } 1772 return nativeGetAllocationByteCount(mNativePtr); 1773 } 1774 1775 /** 1776 * If the bitmap's internal config is in one of the public formats, return 1777 * that config, otherwise return null. 1778 */ 1779 @NonNull getConfig()1780 public final Config getConfig() { 1781 if (mRecycled) { 1782 Log.w(TAG, "Called getConfig() on a recycle()'d bitmap! This is undefined behavior!"); 1783 } 1784 return Config.nativeToConfig(nativeConfig(mNativePtr)); 1785 } 1786 1787 /** Returns true if the bitmap's config supports per-pixel alpha, and 1788 * if the pixels may contain non-opaque alpha values. For some configs, 1789 * this is always false (e.g. RGB_565), since they do not support per-pixel 1790 * alpha. However, for configs that do, the bitmap may be flagged to be 1791 * known that all of its pixels are opaque. In this case hasAlpha() will 1792 * also return false. If a config such as ARGB_8888 is not so flagged, 1793 * it will return true by default. 1794 */ hasAlpha()1795 public final boolean hasAlpha() { 1796 if (mRecycled) { 1797 Log.w(TAG, "Called hasAlpha() on a recycle()'d bitmap! This is undefined behavior!"); 1798 } 1799 return nativeHasAlpha(mNativePtr); 1800 } 1801 1802 /** 1803 * Tell the bitmap if all of the pixels are known to be opaque (false) 1804 * or if some of the pixels may contain non-opaque alpha values (true). 1805 * Note, for some configs (e.g. RGB_565) this call is ignored, since it 1806 * does not support per-pixel alpha values. 1807 * 1808 * This is meant as a drawing hint, as in some cases a bitmap that is known 1809 * to be opaque can take a faster drawing case than one that may have 1810 * non-opaque per-pixel alpha values. 1811 */ setHasAlpha(boolean hasAlpha)1812 public void setHasAlpha(boolean hasAlpha) { 1813 checkRecycled("setHasAlpha called on a recycled bitmap"); 1814 nativeSetHasAlpha(mNativePtr, hasAlpha, mRequestPremultiplied); 1815 } 1816 1817 /** 1818 * Indicates whether the renderer responsible for drawing this 1819 * bitmap should attempt to use mipmaps when this bitmap is drawn 1820 * scaled down. 1821 * 1822 * If you know that you are going to draw this bitmap at less than 1823 * 50% of its original size, you may be able to obtain a higher 1824 * quality 1825 * 1826 * This property is only a suggestion that can be ignored by the 1827 * renderer. It is not guaranteed to have any effect. 1828 * 1829 * @return true if the renderer should attempt to use mipmaps, 1830 * false otherwise 1831 * 1832 * @see #setHasMipMap(boolean) 1833 */ hasMipMap()1834 public final boolean hasMipMap() { 1835 if (mRecycled) { 1836 Log.w(TAG, "Called hasMipMap() on a recycle()'d bitmap! This is undefined behavior!"); 1837 } 1838 return nativeHasMipMap(mNativePtr); 1839 } 1840 1841 /** 1842 * Set a hint for the renderer responsible for drawing this bitmap 1843 * indicating that it should attempt to use mipmaps when this bitmap 1844 * is drawn scaled down. 1845 * 1846 * If you know that you are going to draw this bitmap at less than 1847 * 50% of its original size, you may be able to obtain a higher 1848 * quality by turning this property on. 1849 * 1850 * Note that if the renderer respects this hint it might have to 1851 * allocate extra memory to hold the mipmap levels for this bitmap. 1852 * 1853 * This property is only a suggestion that can be ignored by the 1854 * renderer. It is not guaranteed to have any effect. 1855 * 1856 * @param hasMipMap indicates whether the renderer should attempt 1857 * to use mipmaps 1858 * 1859 * @see #hasMipMap() 1860 */ setHasMipMap(boolean hasMipMap)1861 public final void setHasMipMap(boolean hasMipMap) { 1862 checkRecycled("setHasMipMap called on a recycled bitmap"); 1863 nativeSetHasMipMap(mNativePtr, hasMipMap); 1864 } 1865 1866 /** 1867 * Returns the color space associated with this bitmap. If the color 1868 * space is unknown, this method returns null. 1869 */ 1870 @Nullable getColorSpace()1871 public final ColorSpace getColorSpace() { 1872 checkRecycled("getColorSpace called on a recycled bitmap"); 1873 if (mColorSpace == null) { 1874 mColorSpace = nativeComputeColorSpace(mNativePtr); 1875 } 1876 return mColorSpace; 1877 } 1878 1879 /** 1880 * <p>Modifies the bitmap to have the specified {@link ColorSpace}, without 1881 * affecting the underlying allocation backing the bitmap.</p> 1882 * 1883 * <p>This affects how the framework will interpret the color at each pixel. A bitmap 1884 * with {@link Config#ALPHA_8} never has a color space, since a color space does not 1885 * affect the alpha channel. Other {@code Config}s must always have a non-null 1886 * {@code ColorSpace}.</p> 1887 * 1888 * @throws IllegalArgumentException If the specified color space is {@code null}, not 1889 * {@link ColorSpace.Model#RGB RGB}, or whose components min/max values reduce 1890 * the numerical range compared to the previously assigned color space. 1891 * Prior to {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1892 * <code>IllegalArgumentException</code> will also be thrown 1893 * if the specified color space has a transfer function that is not an 1894 * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. Starting from 1895 * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the color spaces with non 1896 * ICC parametric curve transfer function are allowed. 1897 * E.g., {@link ColorSpace.Named#BT2020_HLG BT2020_HLG}. 1898 * 1899 * @throws IllegalArgumentException If the {@code Config} (returned by {@link #getConfig()}) 1900 * is {@link Config#ALPHA_8}. 1901 * 1902 * @param colorSpace to assign to the bitmap 1903 */ setColorSpace(@onNull ColorSpace colorSpace)1904 public void setColorSpace(@NonNull ColorSpace colorSpace) { 1905 checkRecycled("setColorSpace called on a recycled bitmap"); 1906 if (colorSpace == null) { 1907 throw new IllegalArgumentException("The colorSpace cannot be set to null"); 1908 } 1909 1910 if (getConfig() == Config.ALPHA_8) { 1911 throw new IllegalArgumentException("Cannot set a ColorSpace on ALPHA_8"); 1912 } 1913 1914 // Keep track of the old ColorSpace for comparison, and so we can reset it in case of an 1915 // Exception. 1916 final ColorSpace oldColorSpace = getColorSpace(); 1917 nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance()); 1918 1919 // This will update mColorSpace. It may not be the same as |colorSpace|, e.g. if we 1920 // corrected it because the Bitmap is F16. 1921 mColorSpace = null; 1922 final ColorSpace newColorSpace = getColorSpace(); 1923 1924 try { 1925 if (oldColorSpace.getComponentCount() != newColorSpace.getComponentCount()) { 1926 throw new IllegalArgumentException("The new ColorSpace must have the same " 1927 + "component count as the current ColorSpace"); 1928 } else { 1929 for (int i = 0; i < oldColorSpace.getComponentCount(); i++) { 1930 if (oldColorSpace.getMinValue(i) < newColorSpace.getMinValue(i)) { 1931 throw new IllegalArgumentException("The new ColorSpace cannot increase the " 1932 + "minimum value for any of the components compared to the current " 1933 + "ColorSpace. To perform this type of conversion create a new " 1934 + "Bitmap in the desired ColorSpace and draw this Bitmap into it."); 1935 } 1936 if (oldColorSpace.getMaxValue(i) > newColorSpace.getMaxValue(i)) { 1937 throw new IllegalArgumentException("The new ColorSpace cannot decrease the " 1938 + "maximum value for any of the components compared to the current " 1939 + "ColorSpace/ To perform this type of conversion create a new " 1940 + "Bitmap in the desired ColorSpace and draw this Bitmap into it."); 1941 } 1942 } 1943 } 1944 } catch (IllegalArgumentException e) { 1945 // Undo the change to the ColorSpace. 1946 mColorSpace = oldColorSpace; 1947 nativeSetColorSpace(mNativePtr, mColorSpace.getNativeInstance()); 1948 throw e; 1949 } 1950 } 1951 1952 /** 1953 * Returns whether or not this Bitmap contains a Gainmap. 1954 */ hasGainmap()1955 public boolean hasGainmap() { 1956 checkRecycled("Bitmap is recycled"); 1957 return nativeHasGainmap(mNativePtr); 1958 } 1959 1960 /** 1961 * Returns the gainmap or null if the bitmap doesn't contain a gainmap 1962 */ getGainmap()1963 public @Nullable Gainmap getGainmap() { 1964 checkRecycled("Bitmap is recycled"); 1965 if (mGainmap == null) { 1966 mGainmap = nativeExtractGainmap(mNativePtr); 1967 } 1968 return mGainmap; 1969 } 1970 1971 /** 1972 * Sets a gainmap on this bitmap, or removes the gainmap if null 1973 */ setGainmap(@ullable Gainmap gainmap)1974 public void setGainmap(@Nullable Gainmap gainmap) { 1975 checkRecycled("Bitmap is recycled"); 1976 mGainmap = null; 1977 nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr); 1978 } 1979 1980 /** 1981 * Fills the bitmap's pixels with the specified {@link Color}. 1982 * 1983 * @throws IllegalStateException if the bitmap is not mutable. 1984 */ eraseColor(@olorInt int c)1985 public void eraseColor(@ColorInt int c) { 1986 checkRecycled("Can't erase a recycled bitmap"); 1987 if (!isMutable()) { 1988 throw new IllegalStateException("cannot erase immutable bitmaps"); 1989 } 1990 nativeErase(mNativePtr, c); 1991 } 1992 1993 /** 1994 * Fills the bitmap's pixels with the specified {@code ColorLong}. 1995 * 1996 * @param color The color to fill as packed by the {@link Color} class. 1997 * @throws IllegalStateException if the bitmap is not mutable. 1998 * @throws IllegalArgumentException if the color space encoded in the 1999 * {@code ColorLong} is invalid or unknown. 2000 * 2001 */ eraseColor(@olorLong long color)2002 public void eraseColor(@ColorLong long color) { 2003 checkRecycled("Can't erase a recycled bitmap"); 2004 if (!isMutable()) { 2005 throw new IllegalStateException("cannot erase immutable bitmaps"); 2006 } 2007 2008 ColorSpace cs = Color.colorSpace(color); 2009 nativeErase(mNativePtr, cs.getNativeInstance(), color); 2010 } 2011 2012 /** 2013 * Returns the {@link Color} at the specified location. Throws an exception 2014 * if x or y are out of bounds (negative or >= to the width or height 2015 * respectively). The returned color is a non-premultiplied ARGB value in 2016 * the {@link ColorSpace.Named#SRGB sRGB} color space. 2017 * 2018 * @param x The x coordinate (0...width-1) of the pixel to return 2019 * @param y The y coordinate (0...height-1) of the pixel to return 2020 * @return The argb {@link Color} at the specified coordinate 2021 * @throws IllegalArgumentException if x, y exceed the bitmap's bounds 2022 * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE} 2023 */ 2024 @ColorInt getPixel(int x, int y)2025 public int getPixel(int x, int y) { 2026 checkRecycled("Can't call getPixel() on a recycled bitmap"); 2027 checkHardware("unable to getPixel(), " 2028 + "pixel access is not supported on Config#HARDWARE bitmaps"); 2029 checkPixelAccess(x, y); 2030 return nativeGetPixel(mNativePtr, x, y); 2031 } 2032 clamp(float value, @NonNull ColorSpace cs, int index)2033 private static float clamp(float value, @NonNull ColorSpace cs, int index) { 2034 return Math.max(Math.min(value, cs.getMaxValue(index)), cs.getMinValue(index)); 2035 } 2036 2037 /** 2038 * Returns the {@link Color} at the specified location. Throws an exception 2039 * if x or y are out of bounds (negative or >= to the width or height 2040 * respectively). 2041 * 2042 * @param x The x coordinate (0...width-1) of the pixel to return 2043 * @param y The y coordinate (0...height-1) of the pixel to return 2044 * @return The {@link Color} at the specified coordinate 2045 * @throws IllegalArgumentException if x, y exceed the bitmap's bounds 2046 * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE} 2047 * 2048 */ 2049 @NonNull getColor(int x, int y)2050 public Color getColor(int x, int y) { 2051 checkRecycled("Can't call getColor() on a recycled bitmap"); 2052 checkHardware("unable to getColor(), " 2053 + "pixel access is not supported on Config#HARDWARE bitmaps"); 2054 checkPixelAccess(x, y); 2055 2056 final ColorSpace cs = getColorSpace(); 2057 if (cs == null || cs.equals(ColorSpace.get(ColorSpace.Named.SRGB))) { 2058 return Color.valueOf(nativeGetPixel(mNativePtr, x, y)); 2059 } 2060 // The returned value is in kRGBA_F16_SkColorType, which is packed as 2061 // four half-floats, r,g,b,a. 2062 long rgba = nativeGetColor(mNativePtr, x, y); 2063 float r = Half.toFloat((short) ((rgba >> 0) & 0xffff)); 2064 float g = Half.toFloat((short) ((rgba >> 16) & 0xffff)); 2065 float b = Half.toFloat((short) ((rgba >> 32) & 0xffff)); 2066 float a = Half.toFloat((short) ((rgba >> 48) & 0xffff)); 2067 2068 // Skia may draw outside of the numerical range of the colorSpace. 2069 // Clamp to get an expected value. 2070 return Color.valueOf(clamp(r, cs, 0), clamp(g, cs, 1), clamp(b, cs, 2), a, cs); 2071 } 2072 2073 /** 2074 * Returns in pixels[] a copy of the data in the bitmap. Each value is 2075 * a packed int representing a {@link Color}. The stride parameter allows 2076 * the caller to allow for gaps in the returned pixels array between 2077 * rows. For normal packed results, just pass width for the stride value. 2078 * The returned colors are non-premultiplied ARGB values in the 2079 * {@link ColorSpace.Named#SRGB sRGB} color space. 2080 * 2081 * @param pixels The array to receive the bitmap's colors 2082 * @param offset The first index to write into pixels[] 2083 * @param stride The number of entries in pixels[] to skip between 2084 * rows (must be >= bitmap's width). Can be negative. 2085 * @param x The x coordinate of the first pixel to read from 2086 * the bitmap 2087 * @param y The y coordinate of the first pixel to read from 2088 * the bitmap 2089 * @param width The number of pixels to read from each row 2090 * @param height The number of rows to read 2091 * 2092 * @throws IllegalArgumentException if x, y, width, height exceed the 2093 * bounds of the bitmap, or if abs(stride) < width. 2094 * @throws ArrayIndexOutOfBoundsException if the pixels array is too small 2095 * to receive the specified number of pixels. 2096 * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE} 2097 */ getPixels(@onNull @olorInt int[] pixels, int offset, int stride, int x, int y, int width, int height)2098 public void getPixels(@NonNull @ColorInt int[] pixels, int offset, int stride, 2099 int x, int y, int width, int height) { 2100 checkRecycled("Can't call getPixels() on a recycled bitmap"); 2101 checkHardware("unable to getPixels(), " 2102 + "pixel access is not supported on Config#HARDWARE bitmaps"); 2103 if (width == 0 || height == 0) { 2104 return; // nothing to do 2105 } 2106 checkPixelsAccess(x, y, width, height, offset, stride, pixels); 2107 nativeGetPixels(mNativePtr, pixels, offset, stride, 2108 x, y, width, height); 2109 } 2110 2111 /** 2112 * Shared code to check for illegal arguments passed to getPixel() 2113 * or setPixel() 2114 * 2115 * @param x x coordinate of the pixel 2116 * @param y y coordinate of the pixel 2117 */ checkPixelAccess(int x, int y)2118 private void checkPixelAccess(int x, int y) { 2119 checkXYSign(x, y); 2120 if (x >= getWidth()) { 2121 throw new IllegalArgumentException("x must be < bitmap.width()"); 2122 } 2123 if (y >= getHeight()) { 2124 throw new IllegalArgumentException("y must be < bitmap.height()"); 2125 } 2126 } 2127 2128 /** 2129 * Shared code to check for illegal arguments passed to getPixels() 2130 * or setPixels() 2131 * 2132 * @param x left edge of the area of pixels to access 2133 * @param y top edge of the area of pixels to access 2134 * @param width width of the area of pixels to access 2135 * @param height height of the area of pixels to access 2136 * @param offset offset into pixels[] array 2137 * @param stride number of elements in pixels[] between each logical row 2138 * @param pixels array to hold the area of pixels being accessed 2139 */ checkPixelsAccess(int x, int y, int width, int height, int offset, int stride, int pixels[])2140 private void checkPixelsAccess(int x, int y, int width, int height, 2141 int offset, int stride, int pixels[]) { 2142 checkXYSign(x, y); 2143 if (width < 0) { 2144 throw new IllegalArgumentException("width must be >= 0"); 2145 } 2146 if (height < 0) { 2147 throw new IllegalArgumentException("height must be >= 0"); 2148 } 2149 if (x + width > getWidth()) { 2150 throw new IllegalArgumentException( 2151 "x + width must be <= bitmap.width()"); 2152 } 2153 if (y + height > getHeight()) { 2154 throw new IllegalArgumentException( 2155 "y + height must be <= bitmap.height()"); 2156 } 2157 if (Math.abs(stride) < width) { 2158 throw new IllegalArgumentException("abs(stride) must be >= width"); 2159 } 2160 int lastScanline = offset + (height - 1) * stride; 2161 int length = pixels.length; 2162 if (offset < 0 || (offset + width > length) 2163 || lastScanline < 0 2164 || (lastScanline + width > length)) { 2165 throw new ArrayIndexOutOfBoundsException(); 2166 } 2167 } 2168 2169 /** 2170 * <p>Write the specified {@link Color} into the bitmap (assuming it is 2171 * mutable) at the x,y coordinate. The color must be a 2172 * non-premultiplied ARGB value in the {@link ColorSpace.Named#SRGB sRGB} 2173 * color space.</p> 2174 * 2175 * @param x The x coordinate of the pixel to replace (0...width-1) 2176 * @param y The y coordinate of the pixel to replace (0...height-1) 2177 * @param color The ARGB color to write into the bitmap 2178 * 2179 * @throws IllegalStateException if the bitmap is not mutable 2180 * @throws IllegalArgumentException if x, y are outside of the bitmap's 2181 * bounds. 2182 */ setPixel(int x, int y, @ColorInt int color)2183 public void setPixel(int x, int y, @ColorInt int color) { 2184 checkRecycled("Can't call setPixel() on a recycled bitmap"); 2185 if (!isMutable()) { 2186 throw new IllegalStateException(); 2187 } 2188 checkPixelAccess(x, y); 2189 nativeSetPixel(mNativePtr, x, y, color); 2190 } 2191 2192 /** 2193 * <p>Replace pixels in the bitmap with the colors in the array. Each element 2194 * in the array is a packed int representing a non-premultiplied ARGB 2195 * {@link Color} in the {@link ColorSpace.Named#SRGB sRGB} color space.</p> 2196 * 2197 * @param pixels The colors to write to the bitmap 2198 * @param offset The index of the first color to read from pixels[] 2199 * @param stride The number of colors in pixels[] to skip between rows. 2200 * Normally this value will be the same as the width of 2201 * the bitmap, but it can be larger (or negative). 2202 * @param x The x coordinate of the first pixel to write to in 2203 * the bitmap. 2204 * @param y The y coordinate of the first pixel to write to in 2205 * the bitmap. 2206 * @param width The number of colors to copy from pixels[] per row 2207 * @param height The number of rows to write to the bitmap 2208 * 2209 * @throws IllegalStateException if the bitmap is not mutable 2210 * @throws IllegalArgumentException if x, y, width, height are outside of 2211 * the bitmap's bounds. 2212 * @throws ArrayIndexOutOfBoundsException if the pixels array is too small 2213 * to receive the specified number of pixels. 2214 */ setPixels(@onNull @olorInt int[] pixels, int offset, int stride, int x, int y, int width, int height)2215 public void setPixels(@NonNull @ColorInt int[] pixels, int offset, int stride, 2216 int x, int y, int width, int height) { 2217 checkRecycled("Can't call setPixels() on a recycled bitmap"); 2218 if (!isMutable()) { 2219 throw new IllegalStateException(); 2220 } 2221 if (width == 0 || height == 0) { 2222 return; // nothing to do 2223 } 2224 checkPixelsAccess(x, y, width, height, offset, stride, pixels); 2225 nativeSetPixels(mNativePtr, pixels, offset, stride, 2226 x, y, width, height); 2227 } 2228 2229 public static final @NonNull Parcelable.Creator<Bitmap> CREATOR 2230 = new Parcelable.Creator<Bitmap>() { 2231 /** 2232 * Rebuilds a bitmap previously stored with writeToParcel(). 2233 * 2234 * @param p Parcel object to read the bitmap from 2235 * @return a new bitmap created from the data in the parcel 2236 */ 2237 public Bitmap createFromParcel(Parcel p) { 2238 Bitmap bm = nativeCreateFromParcel(p); 2239 if (bm == null) { 2240 throw new RuntimeException("Failed to unparcel Bitmap"); 2241 } 2242 if (p.readBoolean()) { 2243 bm.setGainmap(p.readTypedObject(Gainmap.CREATOR)); 2244 } 2245 return bm; 2246 } 2247 public Bitmap[] newArray(int size) { 2248 return new Bitmap[size]; 2249 } 2250 }; 2251 2252 /** 2253 * No special parcel contents. 2254 */ describeContents()2255 public int describeContents() { 2256 return 0; 2257 } 2258 2259 /** 2260 * Write the bitmap and its pixels to the parcel. The bitmap can be 2261 * rebuilt from the parcel by calling CREATOR.createFromParcel(). 2262 * 2263 * If this bitmap is {@link Config#HARDWARE}, it may be unparceled with a different pixel 2264 * format (e.g. 565, 8888), but the content will be preserved to the best quality permitted 2265 * by the final pixel format 2266 * @param p Parcel object to write the bitmap data into 2267 */ writeToParcel(@onNull Parcel p, int flags)2268 public void writeToParcel(@NonNull Parcel p, int flags) { 2269 checkRecycled("Can't parcel a recycled bitmap"); 2270 noteHardwareBitmapSlowCall(); 2271 if (!nativeWriteToParcel(mNativePtr, mDensity, p)) { 2272 throw new RuntimeException("native writeToParcel failed"); 2273 } 2274 if (hasGainmap()) { 2275 p.writeBoolean(true); 2276 p.writeTypedObject(mGainmap, flags); 2277 } else { 2278 p.writeBoolean(false); 2279 } 2280 } 2281 2282 /** 2283 * Returns a new bitmap that captures the alpha values of the original. 2284 * This may be drawn with Canvas.drawBitmap(), where the color(s) will be 2285 * taken from the paint that is passed to the draw call. 2286 * 2287 * @return new bitmap containing the alpha channel of the original bitmap. 2288 */ 2289 @CheckResult 2290 @NonNull extractAlpha()2291 public Bitmap extractAlpha() { 2292 return extractAlpha(null, null); 2293 } 2294 2295 /** 2296 * Returns a new bitmap that captures the alpha values of the original. 2297 * These values may be affected by the optional Paint parameter, which 2298 * can contain its own alpha, and may also contain a MaskFilter which 2299 * could change the actual dimensions of the resulting bitmap (e.g. 2300 * a blur maskfilter might enlarge the resulting bitmap). If offsetXY 2301 * is not null, it returns the amount to offset the returned bitmap so 2302 * that it will logically align with the original. For example, if the 2303 * paint contains a blur of radius 2, then offsetXY[] would contains 2304 * -2, -2, so that drawing the alpha bitmap offset by (-2, -2) and then 2305 * drawing the original would result in the blur visually aligning with 2306 * the original. 2307 * 2308 * <p>The initial density of the returned bitmap is the same as the original's. 2309 * 2310 * @param paint Optional paint used to modify the alpha values in the 2311 * resulting bitmap. Pass null for default behavior. 2312 * @param offsetXY Optional array that returns the X (index 0) and Y 2313 * (index 1) offset needed to position the returned bitmap 2314 * so that it visually lines up with the original. 2315 * @return new bitmap containing the (optionally modified by paint) alpha 2316 * channel of the original bitmap. This may be drawn with 2317 * Canvas.drawBitmap(), where the color(s) will be taken from the 2318 * paint that is passed to the draw call. 2319 */ 2320 @CheckResult 2321 @NonNull extractAlpha(@ullable Paint paint, int[] offsetXY)2322 public Bitmap extractAlpha(@Nullable Paint paint, int[] offsetXY) { 2323 checkRecycled("Can't extractAlpha on a recycled bitmap"); 2324 long nativePaint = paint != null ? paint.getNativeInstance() : 0; 2325 noteHardwareBitmapSlowCall(); 2326 Bitmap bm = nativeExtractAlpha(mNativePtr, nativePaint, offsetXY); 2327 if (bm == null) { 2328 throw new RuntimeException("Failed to extractAlpha on Bitmap"); 2329 } 2330 bm.mDensity = mDensity; 2331 return bm; 2332 } 2333 2334 /** 2335 * Given another bitmap, return true if it has the same dimensions, config, 2336 * and pixel data as this bitmap. If any of those differ, return false. 2337 * If other is null, return false. 2338 */ 2339 @WorkerThread sameAs(@ullable Bitmap other)2340 public boolean sameAs(@Nullable Bitmap other) { 2341 StrictMode.noteSlowCall("sameAs compares pixel data, not expected to be fast"); 2342 checkRecycled("Can't call sameAs on a recycled bitmap!"); 2343 if (this == other) return true; 2344 if (other == null) return false; 2345 if (other.isRecycled()) { 2346 throw new IllegalArgumentException("Can't compare to a recycled bitmap!"); 2347 } 2348 return nativeSameAs(mNativePtr, other.mNativePtr); 2349 } 2350 2351 /** 2352 * Builds caches associated with the bitmap that are used for drawing it. 2353 * 2354 * <p>Starting in {@link android.os.Build.VERSION_CODES#N}, this call initiates an asynchronous 2355 * upload to the GPU on RenderThread, if the Bitmap is not already uploaded. With Hardware 2356 * Acceleration, Bitmaps must be uploaded to the GPU in order to be rendered. This is done by 2357 * default the first time a Bitmap is drawn, but the process can take several milliseconds, 2358 * depending on the size of the Bitmap. Each time a Bitmap is modified and drawn again, it must 2359 * be re-uploaded.</p> 2360 * 2361 * <p>Calling this method in advance can save time in the first frame it's used. For example, it 2362 * is recommended to call this on an image decoding worker thread when a decoded Bitmap is about 2363 * to be displayed. It is recommended to make any pre-draw modifications to the Bitmap before 2364 * calling this method, so the cached, uploaded copy may be reused without re-uploading.</p> 2365 * 2366 * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, for purgeable bitmaps, this call 2367 * would attempt to ensure that the pixels have been decoded. 2368 */ prepareToDraw()2369 public void prepareToDraw() { 2370 checkRecycled("Can't prepareToDraw on a recycled bitmap!"); 2371 // Kick off an update/upload of the bitmap outside of the normal 2372 // draw path. 2373 nativePrepareToDraw(mNativePtr); 2374 } 2375 2376 /** 2377 * @return {@link HardwareBuffer} which is internally used by hardware bitmap 2378 * 2379 * Note: the HardwareBuffer does *not* have an associated {@link ColorSpace}. 2380 * To render this object the same as its rendered with this Bitmap, you 2381 * should also call {@link #getColorSpace()}.</p> 2382 * 2383 * Must not be modified while a wrapped Bitmap is accessing it. Doing so will 2384 * result in undefined behavior.</p> 2385 * 2386 * @throws IllegalStateException if the bitmap's config is not {@link Config#HARDWARE} 2387 * or if the bitmap has been recycled. 2388 */ 2389 @NonNull getHardwareBuffer()2390 public HardwareBuffer getHardwareBuffer() { 2391 checkRecycled("Can't getHardwareBuffer from a recycled bitmap"); 2392 HardwareBuffer hardwareBuffer = mHardwareBuffer == null ? null : mHardwareBuffer.get(); 2393 if (hardwareBuffer == null || hardwareBuffer.isClosed()) { 2394 hardwareBuffer = nativeGetHardwareBuffer(mNativePtr); 2395 mHardwareBuffer = new WeakReference<HardwareBuffer>(hardwareBuffer); 2396 } 2397 return hardwareBuffer; 2398 } 2399 2400 //////////// native methods 2401 nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean mutable, long nativeColorSpace)2402 private static native Bitmap nativeCreate(int[] colors, int offset, 2403 int stride, int width, int height, 2404 int nativeConfig, boolean mutable, 2405 long nativeColorSpace); nativeCopy(long nativeSrcBitmap, int nativeConfig, boolean isMutable)2406 private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig, 2407 boolean isMutable); nativeCopyAshmem(long nativeSrcBitmap)2408 private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap); nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig)2409 private static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig); nativeGetAshmemFD(long nativeBitmap)2410 private static native int nativeGetAshmemFD(long nativeBitmap); nativeGetNativeFinalizer()2411 private static native long nativeGetNativeFinalizer(); nativeRecycle(long nativeBitmap)2412 private static native void nativeRecycle(long nativeBitmap); 2413 @UnsupportedAppUsage nativeReconfigure(long nativeBitmap, int width, int height, int config, boolean isPremultiplied)2414 private static native void nativeReconfigure(long nativeBitmap, int width, int height, 2415 int config, boolean isPremultiplied); 2416 nativeCompress(long nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage)2417 private static native boolean nativeCompress(long nativeBitmap, int format, 2418 int quality, OutputStream stream, 2419 byte[] tempStorage); nativeErase(long nativeBitmap, int color)2420 private static native void nativeErase(long nativeBitmap, int color); nativeErase(long nativeBitmap, long colorSpacePtr, long color)2421 private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color); nativeRowBytes(long nativeBitmap)2422 private static native int nativeRowBytes(long nativeBitmap); nativeConfig(long nativeBitmap)2423 private static native int nativeConfig(long nativeBitmap); 2424 nativeGetPixel(long nativeBitmap, int x, int y)2425 private static native int nativeGetPixel(long nativeBitmap, int x, int y); nativeGetColor(long nativeBitmap, int x, int y)2426 private static native long nativeGetColor(long nativeBitmap, int x, int y); nativeGetPixels(long nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height)2427 private static native void nativeGetPixels(long nativeBitmap, int[] pixels, 2428 int offset, int stride, int x, int y, 2429 int width, int height); 2430 nativeSetPixel(long nativeBitmap, int x, int y, int color)2431 private static native void nativeSetPixel(long nativeBitmap, int x, int y, int color); nativeSetPixels(long nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height)2432 private static native void nativeSetPixels(long nativeBitmap, int[] colors, 2433 int offset, int stride, int x, int y, 2434 int width, int height); nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst)2435 private static native void nativeCopyPixelsToBuffer(long nativeBitmap, 2436 Buffer dst); nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src)2437 private static native void nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src); nativeGenerationId(long nativeBitmap)2438 private static native int nativeGenerationId(long nativeBitmap); 2439 nativeCreateFromParcel(Parcel p)2440 private static native Bitmap nativeCreateFromParcel(Parcel p); 2441 // returns true on success nativeWriteToParcel(long nativeBitmap, int density, Parcel p)2442 private static native boolean nativeWriteToParcel(long nativeBitmap, 2443 int density, 2444 Parcel p); 2445 // returns a new bitmap built from the native bitmap's alpha, and the paint nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY)2446 private static native Bitmap nativeExtractAlpha(long nativeBitmap, 2447 long nativePaint, 2448 int[] offsetXY); 2449 nativeHasAlpha(long nativeBitmap)2450 private static native boolean nativeHasAlpha(long nativeBitmap); nativeIsPremultiplied(long nativeBitmap)2451 private static native boolean nativeIsPremultiplied(long nativeBitmap); nativeSetPremultiplied(long nativeBitmap, boolean isPremul)2452 private static native void nativeSetPremultiplied(long nativeBitmap, 2453 boolean isPremul); nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, boolean requestPremul)2454 private static native void nativeSetHasAlpha(long nativeBitmap, 2455 boolean hasAlpha, 2456 boolean requestPremul); nativeHasMipMap(long nativeBitmap)2457 private static native boolean nativeHasMipMap(long nativeBitmap); nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap)2458 private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap); nativeSameAs(long nativeBitmap0, long nativeBitmap1)2459 private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1); nativePrepareToDraw(long nativeBitmap)2460 private static native void nativePrepareToDraw(long nativeBitmap); nativeGetAllocationByteCount(long nativeBitmap)2461 private static native int nativeGetAllocationByteCount(long nativeBitmap); nativeCopyPreserveInternalConfig(long nativeBitmap)2462 private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap); nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace)2463 private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, 2464 long nativeColorSpace); nativeGetHardwareBuffer(long nativeBitmap)2465 private static native HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap); nativeComputeColorSpace(long nativePtr)2466 private static native ColorSpace nativeComputeColorSpace(long nativePtr); nativeSetColorSpace(long nativePtr, long nativeColorSpace)2467 private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace); nativeIsSRGB(long nativePtr)2468 private static native boolean nativeIsSRGB(long nativePtr); nativeIsSRGBLinear(long nativePtr)2469 private static native boolean nativeIsSRGBLinear(long nativePtr); 2470 nativeSetImmutable(long nativePtr)2471 private static native void nativeSetImmutable(long nativePtr); 2472 nativeExtractGainmap(long nativePtr)2473 private static native Gainmap nativeExtractGainmap(long nativePtr); nativeSetGainmap(long bitmapPtr, long gainmapPtr)2474 private static native void nativeSetGainmap(long bitmapPtr, long gainmapPtr); 2475 2476 // ---------------- @CriticalNative ------------------- 2477 2478 @CriticalNative nativeIsImmutable(long nativePtr)2479 private static native boolean nativeIsImmutable(long nativePtr); 2480 2481 @CriticalNative nativeIsBackedByAshmem(long nativePtr)2482 private static native boolean nativeIsBackedByAshmem(long nativePtr); 2483 2484 @CriticalNative nativeHasGainmap(long nativePtr)2485 private static native boolean nativeHasGainmap(long nativePtr); 2486 } 2487