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