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