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