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