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