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.os.Parcel; 20 import android.os.Parcelable; 21 import android.util.DisplayMetrics; 22 23 import java.io.OutputStream; 24 import java.nio.Buffer; 25 import java.nio.ByteBuffer; 26 import java.nio.IntBuffer; 27 import java.nio.ShortBuffer; 28 29 public final class Bitmap implements Parcelable { 30 31 /** 32 * Indicates that the bitmap was created for an unknown pixel density. 33 * 34 * @see Bitmap#getDensity() 35 * @see Bitmap#setDensity(int) 36 */ 37 public static final int DENSITY_NONE = 0; 38 39 /** 40 * Note: mNativeBitmap is used by FaceDetector_jni.cpp 41 * Don't change/rename without updating FaceDetector_jni.cpp 42 * 43 * @hide 44 */ 45 public final int mNativeBitmap; 46 47 /** 48 * Backing buffer for the Bitmap. 49 * Made public for quick access from drawing methods -- do NOT modify 50 * from outside this class. 51 * 52 * @hide 53 */ 54 public byte[] mBuffer; 55 56 @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources 57 private final BitmapFinalizer mFinalizer; 58 59 private final boolean mIsMutable; 60 private byte[] mNinePatchChunk; // may be null 61 private int[] mLayoutBounds; // may be null 62 private int mWidth = -1; 63 private int mHeight = -1; 64 private boolean mRecycled; 65 66 // Package-scoped for fast access. 67 /*package*/ int mDensity = sDefaultDensity = getDefaultDensity(); 68 69 private static volatile Matrix sScaleMatrix; 70 71 private static volatile int sDefaultDensity = -1; 72 73 /** 74 * For backwards compatibility, allows the app layer to change the default 75 * density when running old apps. 76 * @hide 77 */ setDefaultDensity(int density)78 public static void setDefaultDensity(int density) { 79 sDefaultDensity = density; 80 } 81 getDefaultDensity()82 /*package*/ static int getDefaultDensity() { 83 if (sDefaultDensity >= 0) { 84 return sDefaultDensity; 85 } 86 sDefaultDensity = DisplayMetrics.DENSITY_DEVICE; 87 return sDefaultDensity; 88 } 89 90 /** 91 * @noinspection UnusedDeclaration 92 */ 93 /* Private constructor that must received an already allocated native 94 bitmap int (pointer). 95 96 This can be called from JNI code. 97 */ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk, int density)98 /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk, 99 int density) { 100 this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density); 101 } 102 103 /** 104 * @noinspection UnusedDeclaration 105 */ 106 /* Private constructor that must received an already allocated native 107 bitmap int (pointer). 108 109 This can be called from JNI code. 110 */ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk, int[] layoutBounds, int density)111 /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk, 112 int[] layoutBounds, int density) { 113 if (nativeBitmap == 0) { 114 throw new RuntimeException("internal error: native bitmap is 0"); 115 } 116 117 mBuffer = buffer; 118 // we delete this in our finalizer 119 mNativeBitmap = nativeBitmap; 120 mFinalizer = new BitmapFinalizer(nativeBitmap); 121 122 mIsMutable = isMutable; 123 mNinePatchChunk = ninePatchChunk; 124 mLayoutBounds = layoutBounds; 125 if (density >= 0) { 126 mDensity = density; 127 } 128 } 129 130 /** 131 * <p>Returns the density for this bitmap.</p> 132 * 133 * <p>The default density is the same density as the current display, 134 * unless the current application does not support different screen 135 * densities in which case it is 136 * {@link android.util.DisplayMetrics#DENSITY_DEFAULT}. Note that 137 * compatibility mode is determined by the application that was initially 138 * loaded into a process -- applications that share the same process should 139 * all have the same compatibility, or ensure they explicitly set the 140 * density of their bitmaps appropriately.</p> 141 * 142 * @return A scaling factor of the default density or {@link #DENSITY_NONE} 143 * if the scaling factor is unknown. 144 * 145 * @see #setDensity(int) 146 * @see android.util.DisplayMetrics#DENSITY_DEFAULT 147 * @see android.util.DisplayMetrics#densityDpi 148 * @see #DENSITY_NONE 149 */ getDensity()150 public int getDensity() { 151 return mDensity; 152 } 153 154 /** 155 * <p>Specifies the density for this bitmap. When the bitmap is 156 * drawn to a Canvas that also has a density, it will be scaled 157 * appropriately.</p> 158 * 159 * @param density The density scaling factor to use with this bitmap or 160 * {@link #DENSITY_NONE} if the density is unknown. 161 * 162 * @see #getDensity() 163 * @see android.util.DisplayMetrics#DENSITY_DEFAULT 164 * @see android.util.DisplayMetrics#densityDpi 165 * @see #DENSITY_NONE 166 */ setDensity(int density)167 public void setDensity(int density) { 168 mDensity = density; 169 } 170 171 /** 172 * Sets the nine patch chunk. 173 * 174 * @param chunk The definition of the nine patch 175 * 176 * @hide 177 */ setNinePatchChunk(byte[] chunk)178 public void setNinePatchChunk(byte[] chunk) { 179 mNinePatchChunk = chunk; 180 } 181 182 /** 183 * Sets the layout bounds as an array of left, top, right, bottom integers 184 * @param bounds the array containing the padding values 185 * 186 * @hide 187 */ setLayoutBounds(int[] bounds)188 public void setLayoutBounds(int[] bounds) { 189 mLayoutBounds = bounds; 190 } 191 192 /** 193 * Free the native object associated with this bitmap, and clear the 194 * reference to the pixel data. This will not free the pixel data synchronously; 195 * it simply allows it to be garbage collected if there are no other references. 196 * The bitmap is marked as "dead", meaning it will throw an exception if 197 * getPixels() or setPixels() is called, and will draw nothing. This operation 198 * cannot be reversed, so it should only be called if you are sure there are no 199 * further uses for the bitmap. This is an advanced call, and normally need 200 * not be called, since the normal GC process will free up this memory when 201 * there are no more references to this bitmap. 202 */ recycle()203 public void recycle() { 204 if (!mRecycled) { 205 mBuffer = null; 206 nativeRecycle(mNativeBitmap); 207 mNinePatchChunk = null; 208 mRecycled = true; 209 } 210 } 211 212 /** 213 * Returns true if this bitmap has been recycled. If so, then it is an error 214 * to try to access its pixels, and the bitmap will not draw. 215 * 216 * @return true if the bitmap has been recycled 217 */ isRecycled()218 public final boolean isRecycled() { 219 return mRecycled; 220 } 221 222 /** 223 * Returns the generation ID of this bitmap. The generation ID changes 224 * whenever the bitmap is modified. This can be used as an efficient way to 225 * check if a bitmap has changed. 226 * 227 * @return The current generation ID for this bitmap. 228 */ getGenerationId()229 public int getGenerationId() { 230 return nativeGenerationId(mNativeBitmap); 231 } 232 233 /** 234 * This is called by methods that want to throw an exception if the bitmap 235 * has already been recycled. 236 */ checkRecycled(String errorMessage)237 private void checkRecycled(String errorMessage) { 238 if (mRecycled) { 239 throw new IllegalStateException(errorMessage); 240 } 241 } 242 243 /** 244 * Common code for checking that x and y are >= 0 245 * 246 * @param x x coordinate to ensure is >= 0 247 * @param y y coordinate to ensure is >= 0 248 */ checkXYSign(int x, int y)249 private static void checkXYSign(int x, int y) { 250 if (x < 0) { 251 throw new IllegalArgumentException("x must be >= 0"); 252 } 253 if (y < 0) { 254 throw new IllegalArgumentException("y must be >= 0"); 255 } 256 } 257 258 /** 259 * Common code for checking that width and height are > 0 260 * 261 * @param width width to ensure is > 0 262 * @param height height to ensure is > 0 263 */ checkWidthHeight(int width, int height)264 private static void checkWidthHeight(int width, int height) { 265 if (width <= 0) { 266 throw new IllegalArgumentException("width must be > 0"); 267 } 268 if (height <= 0) { 269 throw new IllegalArgumentException("height must be > 0"); 270 } 271 } 272 273 /** 274 * Possible bitmap configurations. A bitmap configuration describes 275 * how pixels are stored. This affects the quality (color depth) as 276 * well as the ability to display transparent/translucent colors. 277 */ 278 public enum Config { 279 // these native values must match up with the enum in SkBitmap.h 280 281 /** 282 * Each pixel is stored as a single translucency (alpha) channel. 283 * This is very useful to efficiently store masks for instance. 284 * No color information is stored. 285 * With this configuration, each pixel requires 1 byte of memory. 286 */ 287 ALPHA_8 (2), 288 289 /** 290 * Each pixel is stored on 2 bytes and only the RGB channels are 291 * encoded: red is stored with 5 bits of precision (32 possible 292 * values), green is stored with 6 bits of precision (64 possible 293 * values) and blue is stored with 5 bits of precision. 294 * 295 * This configuration can produce slight visual artifacts depending 296 * on the configuration of the source. For instance, without 297 * dithering, the result might show a greenish tint. To get better 298 * results dithering should be applied. 299 * 300 * This configuration may be useful when using opaque bitmaps 301 * that do not require high color fidelity. 302 */ 303 RGB_565 (4), 304 305 /** 306 * Each pixel is stored on 2 bytes. The three RGB color channels 307 * and the alpha channel (translucency) are stored with a 4 bits 308 * precision (16 possible values.) 309 * 310 * This configuration is mostly useful if the application needs 311 * to store translucency information but also needs to save 312 * memory. 313 * 314 * It is recommended to use {@link #ARGB_8888} instead of this 315 * configuration. 316 * 317 * @deprecated Because of the poor quality of this configuration, 318 * it is advised to use {@link #ARGB_8888} instead. 319 */ 320 @Deprecated 321 ARGB_4444 (5), 322 323 /** 324 * Each pixel is stored on 4 bytes. Each channel (RGB and alpha 325 * for translucency) is stored with 8 bits of precision (256 326 * possible values.) 327 * 328 * This configuration is very flexible and offers the best 329 * quality. It should be used whenever possible. 330 */ 331 ARGB_8888 (6); 332 333 final int nativeInt; 334 335 @SuppressWarnings({"deprecation"}) 336 private static Config sConfigs[] = { 337 null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 338 }; 339 Config(int ni)340 Config(int ni) { 341 this.nativeInt = ni; 342 } 343 nativeToConfig(int ni)344 static Config nativeToConfig(int ni) { 345 return sConfigs[ni]; 346 } 347 } 348 349 /** 350 * Copy the bitmap's pixels into the specified buffer (allocated by the 351 * caller). An exception is thrown if the buffer is not large enough to 352 * hold all of the pixels (taking into account the number of bytes per 353 * pixel) or if the Buffer subclass is not one of the support types 354 * (ByteBuffer, ShortBuffer, IntBuffer). 355 */ copyPixelsToBuffer(Buffer dst)356 public void copyPixelsToBuffer(Buffer dst) { 357 int elements = dst.remaining(); 358 int shift; 359 if (dst instanceof ByteBuffer) { 360 shift = 0; 361 } else if (dst instanceof ShortBuffer) { 362 shift = 1; 363 } else if (dst instanceof IntBuffer) { 364 shift = 2; 365 } else { 366 throw new RuntimeException("unsupported Buffer subclass"); 367 } 368 369 long bufferSize = (long)elements << shift; 370 long pixelSize = getByteCount(); 371 372 if (bufferSize < pixelSize) { 373 throw new RuntimeException("Buffer not large enough for pixels"); 374 } 375 376 nativeCopyPixelsToBuffer(mNativeBitmap, dst); 377 378 // now update the buffer's position 379 int position = dst.position(); 380 position += pixelSize >> shift; 381 dst.position(position); 382 } 383 384 /** 385 * Copy the pixels from the buffer, beginning at the current position, 386 * overwriting the bitmap's pixels. The data in the buffer is not changed 387 * in any way (unlike setPixels(), which converts from unpremultipled 32bit 388 * to whatever the bitmap's native format is. 389 */ copyPixelsFromBuffer(Buffer src)390 public void copyPixelsFromBuffer(Buffer src) { 391 checkRecycled("copyPixelsFromBuffer called on recycled bitmap"); 392 393 int elements = src.remaining(); 394 int shift; 395 if (src instanceof ByteBuffer) { 396 shift = 0; 397 } else if (src instanceof ShortBuffer) { 398 shift = 1; 399 } else if (src instanceof IntBuffer) { 400 shift = 2; 401 } else { 402 throw new RuntimeException("unsupported Buffer subclass"); 403 } 404 405 long bufferBytes = (long)elements << shift; 406 long bitmapBytes = getByteCount(); 407 408 if (bufferBytes < bitmapBytes) { 409 throw new RuntimeException("Buffer not large enough for pixels"); 410 } 411 412 nativeCopyPixelsFromBuffer(mNativeBitmap, src); 413 } 414 415 /** 416 * Tries to make a new bitmap based on the dimensions of this bitmap, 417 * setting the new bitmap's config to the one specified, and then copying 418 * this bitmap's pixels into the new bitmap. If the conversion is not 419 * supported, or the allocator fails, then this returns NULL. The returned 420 * bitmap initially has the same density as the original. 421 * 422 * @param config The desired config for the resulting bitmap 423 * @param isMutable True if the resulting bitmap should be mutable (i.e. 424 * its pixels can be modified) 425 * @return the new bitmap, or null if the copy could not be made. 426 */ copy(Config config, boolean isMutable)427 public Bitmap copy(Config config, boolean isMutable) { 428 checkRecycled("Can't copy a recycled bitmap"); 429 Bitmap b = nativeCopy(mNativeBitmap, config.nativeInt, isMutable); 430 if (b != null) { 431 b.mDensity = mDensity; 432 } 433 return b; 434 } 435 436 /** 437 * Creates a new bitmap, scaled from an existing bitmap, when possible. If the 438 * specified width and height are the same as the current width and height of 439 * the source btimap, the source bitmap is returned and now new bitmap is 440 * created. 441 * 442 * @param src The source bitmap. 443 * @param dstWidth The new bitmap's desired width. 444 * @param dstHeight The new bitmap's desired height. 445 * @param filter true if the source should be filtered. 446 * @return The new scaled bitmap or the source bitmap if no scaling is required. 447 */ createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)448 public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, 449 boolean filter) { 450 Matrix m; 451 synchronized (Bitmap.class) { 452 // small pool of just 1 matrix 453 m = sScaleMatrix; 454 sScaleMatrix = null; 455 } 456 457 if (m == null) { 458 m = new Matrix(); 459 } 460 461 final int width = src.getWidth(); 462 final int height = src.getHeight(); 463 final float sx = dstWidth / (float)width; 464 final float sy = dstHeight / (float)height; 465 m.setScale(sx, sy); 466 Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter); 467 468 synchronized (Bitmap.class) { 469 // do we need to check for null? why not just assign everytime? 470 if (sScaleMatrix == null) { 471 sScaleMatrix = m; 472 } 473 } 474 475 return b; 476 } 477 478 /** 479 * Returns an immutable bitmap from the source bitmap. The new bitmap may 480 * be the same object as source, or a copy may have been made. It is 481 * initialized with the same density as the original bitmap. 482 */ createBitmap(Bitmap src)483 public static Bitmap createBitmap(Bitmap src) { 484 return createBitmap(src, 0, 0, src.getWidth(), src.getHeight()); 485 } 486 487 /** 488 * Returns an immutable bitmap from the specified subset of the source 489 * bitmap. The new bitmap may be the same object as source, or a copy may 490 * have been made. It is initialized with the same density as the original 491 * bitmap. 492 * 493 * @param source The bitmap we are subsetting 494 * @param x The x coordinate of the first pixel in source 495 * @param y The y coordinate of the first pixel in source 496 * @param width The number of pixels in each row 497 * @param height The number of rows 498 * @return A copy of a subset of the source bitmap or the source bitmap itself. 499 */ createBitmap(Bitmap source, int x, int y, int width, int height)500 public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) { 501 return createBitmap(source, x, y, width, height, null, false); 502 } 503 504 /** 505 * Returns an immutable bitmap from subset of the source bitmap, 506 * transformed by the optional matrix. The new bitmap may be the 507 * same object as source, or a copy may have been made. It is 508 * initialized with the same density as the original bitmap. 509 * 510 * If the source bitmap is immutable and the requested subset is the 511 * same as the source bitmap itself, then the source bitmap is 512 * returned and no new bitmap is created. 513 * 514 * @param source The bitmap we are subsetting 515 * @param x The x coordinate of the first pixel in source 516 * @param y The y coordinate of the first pixel in source 517 * @param width The number of pixels in each row 518 * @param height The number of rows 519 * @param m Optional matrix to be applied to the pixels 520 * @param filter true if the source should be filtered. 521 * Only applies if the matrix contains more than just 522 * translation. 523 * @return A bitmap that represents the specified subset of source 524 * @throws IllegalArgumentException if the x, y, width, height values are 525 * outside of the dimensions of the source bitmap. 526 */ createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)527 public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height, 528 Matrix m, boolean filter) { 529 530 checkXYSign(x, y); 531 checkWidthHeight(width, height); 532 if (x + width > source.getWidth()) { 533 throw new IllegalArgumentException("x + width must be <= bitmap.width()"); 534 } 535 if (y + height > source.getHeight()) { 536 throw new IllegalArgumentException("y + height must be <= bitmap.height()"); 537 } 538 539 // check if we can just return our argument unchanged 540 if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() && 541 height == source.getHeight() && (m == null || m.isIdentity())) { 542 return source; 543 } 544 545 int neww = width; 546 int newh = height; 547 Canvas canvas = new Canvas(); 548 Bitmap bitmap; 549 Paint paint; 550 551 Rect srcR = new Rect(x, y, x + width, y + height); 552 RectF dstR = new RectF(0, 0, width, height); 553 554 Config newConfig = Config.ARGB_8888; 555 final Config config = source.getConfig(); 556 // GIF files generate null configs, assume ARGB_8888 557 if (config != null) { 558 switch (config) { 559 case RGB_565: 560 newConfig = Config.RGB_565; 561 break; 562 case ALPHA_8: 563 newConfig = Config.ALPHA_8; 564 break; 565 //noinspection deprecation 566 case ARGB_4444: 567 case ARGB_8888: 568 default: 569 newConfig = Config.ARGB_8888; 570 break; 571 } 572 } 573 574 if (m == null || m.isIdentity()) { 575 bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha()); 576 paint = null; // not needed 577 } else { 578 final boolean transformed = !m.rectStaysRect(); 579 580 RectF deviceR = new RectF(); 581 m.mapRect(deviceR, dstR); 582 583 neww = Math.round(deviceR.width()); 584 newh = Math.round(deviceR.height()); 585 586 bitmap = createBitmap(neww, newh, transformed ? Config.ARGB_8888 : newConfig, 587 transformed || source.hasAlpha()); 588 589 canvas.translate(-deviceR.left, -deviceR.top); 590 canvas.concat(m); 591 592 paint = new Paint(); 593 paint.setFilterBitmap(filter); 594 if (transformed) { 595 paint.setAntiAlias(true); 596 } 597 } 598 599 // The new bitmap was created from a known bitmap source so assume that 600 // they use the same density 601 bitmap.mDensity = source.mDensity; 602 603 canvas.setBitmap(bitmap); 604 canvas.drawBitmap(source, srcR, dstR, paint); 605 canvas.setBitmap(null); 606 607 return bitmap; 608 } 609 610 /** 611 * Returns a mutable bitmap with the specified width and height. Its 612 * initial density is as per {@link #getDensity}. 613 * 614 * @param width The width of the bitmap 615 * @param height The height of the bitmap 616 * @param config The bitmap config to create. 617 * @throws IllegalArgumentException if the width or height are <= 0 618 */ createBitmap(int width, int height, Config config)619 public static Bitmap createBitmap(int width, int height, Config config) { 620 return createBitmap(width, height, config, true); 621 } 622 623 /** 624 * Returns a mutable bitmap with the specified width and height. Its 625 * initial density is as per {@link #getDensity}. 626 * 627 * @param width The width of the bitmap 628 * @param height The height of the bitmap 629 * @param config The bitmap config to create. 630 * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the 631 * bitmap as opaque. Doing so will clear the bitmap in black 632 * instead of transparent. 633 * 634 * @throws IllegalArgumentException if the width or height are <= 0 635 */ createBitmap(int width, int height, Config config, boolean hasAlpha)636 private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) { 637 if (width <= 0 || height <= 0) { 638 throw new IllegalArgumentException("width and height must be > 0"); 639 } 640 Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true); 641 if (config == Config.ARGB_8888 && !hasAlpha) { 642 nativeErase(bm.mNativeBitmap, 0xff000000); 643 nativeSetHasAlpha(bm.mNativeBitmap, hasAlpha); 644 } else { 645 // No need to initialize it to zeroes; it is backed by a VM byte array 646 // which is by definition preinitialized to all zeroes. 647 // 648 //nativeErase(bm.mNativeBitmap, 0); 649 } 650 return bm; 651 } 652 653 /** 654 * Returns a immutable bitmap with the specified width and height, with each 655 * pixel value set to the corresponding value in the colors array. Its 656 * initial density is as per {@link #getDensity}. 657 * 658 * @param colors Array of {@link Color} used to initialize the pixels. 659 * @param offset Number of values to skip before the first color in the 660 * array of colors. 661 * @param stride Number of colors in the array between rows (must be >= 662 * width or <= -width). 663 * @param width The width of the bitmap 664 * @param height The height of the bitmap 665 * @param config The bitmap config to create. If the config does not 666 * support per-pixel alpha (e.g. RGB_565), then the alpha 667 * bytes in the colors[] will be ignored (assumed to be FF) 668 * @throws IllegalArgumentException if the width or height are <= 0, or if 669 * the color array's length is less than the number of pixels. 670 */ createBitmap(int colors[], int offset, int stride, int width, int height, Config config)671 public static Bitmap createBitmap(int colors[], int offset, int stride, 672 int width, int height, Config config) { 673 674 checkWidthHeight(width, height); 675 if (Math.abs(stride) < width) { 676 throw new IllegalArgumentException("abs(stride) must be >= width"); 677 } 678 int lastScanline = offset + (height - 1) * stride; 679 int length = colors.length; 680 if (offset < 0 || (offset + width > length) || lastScanline < 0 || 681 (lastScanline + width > length)) { 682 throw new ArrayIndexOutOfBoundsException(); 683 } 684 if (width <= 0 || height <= 0) { 685 throw new IllegalArgumentException("width and height must be > 0"); 686 } 687 return nativeCreate(colors, offset, stride, width, height, 688 config.nativeInt, false); 689 } 690 691 /** 692 * Returns a immutable bitmap with the specified width and height, with each 693 * pixel value set to the corresponding value in the colors array. Its 694 * initial density is as per {@link #getDensity}. 695 * 696 * @param colors Array of {@link Color} used to initialize the pixels. 697 * This array must be at least as large as width * height. 698 * @param width The width of the bitmap 699 * @param height The height of the bitmap 700 * @param config The bitmap config to create. If the config does not 701 * support per-pixel alpha (e.g. RGB_565), then the alpha 702 * bytes in the colors[] will be ignored (assumed to be FF) 703 * @throws IllegalArgumentException if the width or height are <= 0, or if 704 * the color array's length is less than the number of pixels. 705 */ createBitmap(int colors[], int width, int height, Config config)706 public static Bitmap createBitmap(int colors[], int width, int height, Config config) { 707 return createBitmap(colors, 0, width, width, height, config); 708 } 709 710 /** 711 * Returns an optional array of private data, used by the UI system for 712 * some bitmaps. Not intended to be called by applications. 713 */ getNinePatchChunk()714 public byte[] getNinePatchChunk() { 715 return mNinePatchChunk; 716 } 717 718 /** 719 * @hide 720 * @return the layout padding [left, right, top, bottom] 721 */ getLayoutBounds()722 public int[] getLayoutBounds() { 723 return mLayoutBounds; 724 } 725 726 /** 727 * Specifies the known formats a bitmap can be compressed into 728 */ 729 public enum CompressFormat { 730 JPEG (0), 731 PNG (1), 732 WEBP (2); 733 CompressFormat(int nativeInt)734 CompressFormat(int nativeInt) { 735 this.nativeInt = nativeInt; 736 } 737 final int nativeInt; 738 } 739 740 /** 741 * Number of bytes of temp storage we use for communicating between the 742 * native compressor and the java OutputStream. 743 */ 744 private final static int WORKING_COMPRESS_STORAGE = 4096; 745 746 /** 747 * Write a compressed version of the bitmap to the specified outputstream. 748 * If this returns true, the bitmap can be reconstructed by passing a 749 * corresponding inputstream to BitmapFactory.decodeStream(). Note: not 750 * all Formats support all bitmap configs directly, so it is possible that 751 * the returned bitmap from BitmapFactory could be in a different bitdepth, 752 * and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque 753 * pixels). 754 * 755 * @param format The format of the compressed image 756 * @param quality Hint to the compressor, 0-100. 0 meaning compress for 757 * small size, 100 meaning compress for max quality. Some 758 * formats, like PNG which is lossless, will ignore the 759 * quality setting 760 * @param stream The outputstream to write the compressed data. 761 * @return true if successfully compressed to the specified stream. 762 */ compress(CompressFormat format, int quality, OutputStream stream)763 public boolean compress(CompressFormat format, int quality, OutputStream stream) { 764 checkRecycled("Can't compress a recycled bitmap"); 765 // do explicit check before calling the native method 766 if (stream == null) { 767 throw new NullPointerException(); 768 } 769 if (quality < 0 || quality > 100) { 770 throw new IllegalArgumentException("quality must be 0..100"); 771 } 772 return nativeCompress(mNativeBitmap, format.nativeInt, quality, 773 stream, new byte[WORKING_COMPRESS_STORAGE]); 774 } 775 776 /** 777 * Returns true if the bitmap is marked as mutable (i.e. can be drawn into) 778 */ isMutable()779 public final boolean isMutable() { 780 return mIsMutable; 781 } 782 783 /** Returns the bitmap's width */ getWidth()784 public final int getWidth() { 785 return mWidth == -1 ? mWidth = nativeWidth(mNativeBitmap) : mWidth; 786 } 787 788 /** Returns the bitmap's height */ getHeight()789 public final int getHeight() { 790 return mHeight == -1 ? mHeight = nativeHeight(mNativeBitmap) : mHeight; 791 } 792 793 /** 794 * Convenience for calling {@link #getScaledWidth(int)} with the target 795 * density of the given {@link Canvas}. 796 */ getScaledWidth(Canvas canvas)797 public int getScaledWidth(Canvas canvas) { 798 return scaleFromDensity(getWidth(), mDensity, canvas.mDensity); 799 } 800 801 /** 802 * Convenience for calling {@link #getScaledHeight(int)} with the target 803 * density of the given {@link Canvas}. 804 */ getScaledHeight(Canvas canvas)805 public int getScaledHeight(Canvas canvas) { 806 return scaleFromDensity(getHeight(), mDensity, canvas.mDensity); 807 } 808 809 /** 810 * Convenience for calling {@link #getScaledWidth(int)} with the target 811 * density of the given {@link DisplayMetrics}. 812 */ getScaledWidth(DisplayMetrics metrics)813 public int getScaledWidth(DisplayMetrics metrics) { 814 return scaleFromDensity(getWidth(), mDensity, metrics.densityDpi); 815 } 816 817 /** 818 * Convenience for calling {@link #getScaledHeight(int)} with the target 819 * density of the given {@link DisplayMetrics}. 820 */ getScaledHeight(DisplayMetrics metrics)821 public int getScaledHeight(DisplayMetrics metrics) { 822 return scaleFromDensity(getHeight(), mDensity, metrics.densityDpi); 823 } 824 825 /** 826 * Convenience method that returns the width of this bitmap divided 827 * by the density scale factor. 828 * 829 * @param targetDensity The density of the target canvas of the bitmap. 830 * @return The scaled width of this bitmap, according to the density scale factor. 831 */ getScaledWidth(int targetDensity)832 public int getScaledWidth(int targetDensity) { 833 return scaleFromDensity(getWidth(), mDensity, targetDensity); 834 } 835 836 /** 837 * Convenience method that returns the height of this bitmap divided 838 * by the density scale factor. 839 * 840 * @param targetDensity The density of the target canvas of the bitmap. 841 * @return The scaled height of this bitmap, according to the density scale factor. 842 */ getScaledHeight(int targetDensity)843 public int getScaledHeight(int targetDensity) { 844 return scaleFromDensity(getHeight(), mDensity, targetDensity); 845 } 846 847 /** 848 * @hide 849 */ scaleFromDensity(int size, int sdensity, int tdensity)850 static public int scaleFromDensity(int size, int sdensity, int tdensity) { 851 if (sdensity == DENSITY_NONE || sdensity == tdensity) { 852 return size; 853 } 854 855 // Scale by tdensity / sdensity, rounding up. 856 return ((size * tdensity) + (sdensity >> 1)) / sdensity; 857 } 858 859 /** 860 * Return the number of bytes between rows in the bitmap's pixels. Note that 861 * this refers to the pixels as stored natively by the bitmap. If you call 862 * getPixels() or setPixels(), then the pixels are uniformly treated as 863 * 32bit values, packed according to the Color class. 864 * 865 * @return number of bytes between rows of the native bitmap pixels. 866 */ getRowBytes()867 public final int getRowBytes() { 868 return nativeRowBytes(mNativeBitmap); 869 } 870 871 /** 872 * Returns the number of bytes used to store this bitmap's pixels. 873 */ getByteCount()874 public final int getByteCount() { 875 // int result permits bitmaps up to 46,340 x 46,340 876 return getRowBytes() * getHeight(); 877 } 878 879 /** 880 * If the bitmap's internal config is in one of the public formats, return 881 * that config, otherwise return null. 882 */ getConfig()883 public final Config getConfig() { 884 return Config.nativeToConfig(nativeConfig(mNativeBitmap)); 885 } 886 887 /** Returns true if the bitmap's config supports per-pixel alpha, and 888 * if the pixels may contain non-opaque alpha values. For some configs, 889 * this is always false (e.g. RGB_565), since they do not support per-pixel 890 * alpha. However, for configs that do, the bitmap may be flagged to be 891 * known that all of its pixels are opaque. In this case hasAlpha() will 892 * also return false. If a config such as ARGB_8888 is not so flagged, 893 * it will return true by default. 894 */ hasAlpha()895 public final boolean hasAlpha() { 896 return nativeHasAlpha(mNativeBitmap); 897 } 898 899 /** 900 * Tell the bitmap if all of the pixels are known to be opaque (false) 901 * or if some of the pixels may contain non-opaque alpha values (true). 902 * Note, for some configs (e.g. RGB_565) this call is ignored, since it 903 * does not support per-pixel alpha values. 904 * 905 * This is meant as a drawing hint, as in some cases a bitmap that is known 906 * to be opaque can take a faster drawing case than one that may have 907 * non-opaque per-pixel alpha values. 908 */ setHasAlpha(boolean hasAlpha)909 public void setHasAlpha(boolean hasAlpha) { 910 nativeSetHasAlpha(mNativeBitmap, hasAlpha); 911 } 912 913 /** 914 * Fills the bitmap's pixels with the specified {@link Color}. 915 * 916 * @throws IllegalStateException if the bitmap is not mutable. 917 */ eraseColor(int c)918 public void eraseColor(int c) { 919 checkRecycled("Can't erase a recycled bitmap"); 920 if (!isMutable()) { 921 throw new IllegalStateException("cannot erase immutable bitmaps"); 922 } 923 nativeErase(mNativeBitmap, c); 924 } 925 926 /** 927 * Returns the {@link Color} at the specified location. Throws an exception 928 * if x or y are out of bounds (negative or >= to the width or height 929 * respectively). 930 * 931 * @param x The x coordinate (0...width-1) of the pixel to return 932 * @param y The y coordinate (0...height-1) of the pixel to return 933 * @return The argb {@link Color} at the specified coordinate 934 * @throws IllegalArgumentException if x, y exceed the bitmap's bounds 935 */ getPixel(int x, int y)936 public int getPixel(int x, int y) { 937 checkRecycled("Can't call getPixel() on a recycled bitmap"); 938 checkPixelAccess(x, y); 939 return nativeGetPixel(mNativeBitmap, x, y); 940 } 941 942 /** 943 * Returns in pixels[] a copy of the data in the bitmap. Each value is 944 * a packed int representing a {@link Color}. The stride parameter allows 945 * the caller to allow for gaps in the returned pixels array between 946 * rows. For normal packed results, just pass width for the stride value. 947 * 948 * @param pixels The array to receive the bitmap's colors 949 * @param offset The first index to write into pixels[] 950 * @param stride The number of entries in pixels[] to skip between 951 * rows (must be >= bitmap's width). Can be negative. 952 * @param x The x coordinate of the first pixel to read from 953 * the bitmap 954 * @param y The y coordinate of the first pixel to read from 955 * the bitmap 956 * @param width The number of pixels to read from each row 957 * @param height The number of rows to read 958 * @throws IllegalArgumentException if x, y, width, height exceed the 959 * bounds of the bitmap, or if abs(stride) < width. 960 * @throws ArrayIndexOutOfBoundsException if the pixels array is too small 961 * to receive the specified number of pixels. 962 */ getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)963 public void getPixels(int[] pixels, int offset, int stride, 964 int x, int y, int width, int height) { 965 checkRecycled("Can't call getPixels() on a recycled bitmap"); 966 if (width == 0 || height == 0) { 967 return; // nothing to do 968 } 969 checkPixelsAccess(x, y, width, height, offset, stride, pixels); 970 nativeGetPixels(mNativeBitmap, pixels, offset, stride, 971 x, y, width, height); 972 } 973 974 /** 975 * Shared code to check for illegal arguments passed to getPixel() 976 * or setPixel() 977 * @param x x coordinate of the pixel 978 * @param y y coordinate of the pixel 979 */ checkPixelAccess(int x, int y)980 private void checkPixelAccess(int x, int y) { 981 checkXYSign(x, y); 982 if (x >= getWidth()) { 983 throw new IllegalArgumentException("x must be < bitmap.width()"); 984 } 985 if (y >= getHeight()) { 986 throw new IllegalArgumentException("y must be < bitmap.height()"); 987 } 988 } 989 990 /** 991 * Shared code to check for illegal arguments passed to getPixels() 992 * or setPixels() 993 * 994 * @param x left edge of the area of pixels to access 995 * @param y top edge of the area of pixels to access 996 * @param width width of the area of pixels to access 997 * @param height height of the area of pixels to access 998 * @param offset offset into pixels[] array 999 * @param stride number of elements in pixels[] between each logical row 1000 * @param pixels array to hold the area of pixels being accessed 1001 */ checkPixelsAccess(int x, int y, int width, int height, int offset, int stride, int pixels[])1002 private void checkPixelsAccess(int x, int y, int width, int height, 1003 int offset, int stride, int pixels[]) { 1004 checkXYSign(x, y); 1005 if (width < 0) { 1006 throw new IllegalArgumentException("width must be >= 0"); 1007 } 1008 if (height < 0) { 1009 throw new IllegalArgumentException("height must be >= 0"); 1010 } 1011 if (x + width > getWidth()) { 1012 throw new IllegalArgumentException( 1013 "x + width must be <= bitmap.width()"); 1014 } 1015 if (y + height > getHeight()) { 1016 throw new IllegalArgumentException( 1017 "y + height must be <= bitmap.height()"); 1018 } 1019 if (Math.abs(stride) < width) { 1020 throw new IllegalArgumentException("abs(stride) must be >= width"); 1021 } 1022 int lastScanline = offset + (height - 1) * stride; 1023 int length = pixels.length; 1024 if (offset < 0 || (offset + width > length) 1025 || lastScanline < 0 1026 || (lastScanline + width > length)) { 1027 throw new ArrayIndexOutOfBoundsException(); 1028 } 1029 } 1030 1031 /** 1032 * Write the specified {@link Color} into the bitmap (assuming it is 1033 * mutable) at the x,y coordinate. 1034 * 1035 * @param x The x coordinate of the pixel to replace (0...width-1) 1036 * @param y The y coordinate of the pixel to replace (0...height-1) 1037 * @param color The {@link Color} to write into the bitmap 1038 * @throws IllegalStateException if the bitmap is not mutable 1039 * @throws IllegalArgumentException if x, y are outside of the bitmap's 1040 * bounds. 1041 */ setPixel(int x, int y, int color)1042 public void setPixel(int x, int y, int color) { 1043 checkRecycled("Can't call setPixel() on a recycled bitmap"); 1044 if (!isMutable()) { 1045 throw new IllegalStateException(); 1046 } 1047 checkPixelAccess(x, y); 1048 nativeSetPixel(mNativeBitmap, x, y, color); 1049 } 1050 1051 /** 1052 * Replace pixels in the bitmap with the colors in the array. Each element 1053 * in the array is a packed int prepresenting a {@link Color} 1054 * 1055 * @param pixels The colors to write to the bitmap 1056 * @param offset The index of the first color to read from pixels[] 1057 * @param stride The number of colors in pixels[] to skip between rows. 1058 * Normally this value will be the same as the width of 1059 * the bitmap, but it can be larger (or negative). 1060 * @param x The x coordinate of the first pixel to write to in 1061 * the bitmap. 1062 * @param y The y coordinate of the first pixel to write to in 1063 * the bitmap. 1064 * @param width The number of colors to copy from pixels[] per row 1065 * @param height The number of rows to write to the bitmap 1066 * @throws IllegalStateException if the bitmap is not mutable 1067 * @throws IllegalArgumentException if x, y, width, height are outside of 1068 * the bitmap's bounds. 1069 * @throws ArrayIndexOutOfBoundsException if the pixels array is too small 1070 * to receive the specified number of pixels. 1071 */ setPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)1072 public void setPixels(int[] pixels, int offset, int stride, 1073 int x, int y, int width, int height) { 1074 checkRecycled("Can't call setPixels() on a recycled bitmap"); 1075 if (!isMutable()) { 1076 throw new IllegalStateException(); 1077 } 1078 if (width == 0 || height == 0) { 1079 return; // nothing to do 1080 } 1081 checkPixelsAccess(x, y, width, height, offset, stride, pixels); 1082 nativeSetPixels(mNativeBitmap, pixels, offset, stride, 1083 x, y, width, height); 1084 } 1085 1086 public static final Parcelable.Creator<Bitmap> CREATOR 1087 = new Parcelable.Creator<Bitmap>() { 1088 /** 1089 * Rebuilds a bitmap previously stored with writeToParcel(). 1090 * 1091 * @param p Parcel object to read the bitmap from 1092 * @return a new bitmap created from the data in the parcel 1093 */ 1094 public Bitmap createFromParcel(Parcel p) { 1095 Bitmap bm = nativeCreateFromParcel(p); 1096 if (bm == null) { 1097 throw new RuntimeException("Failed to unparcel Bitmap"); 1098 } 1099 return bm; 1100 } 1101 public Bitmap[] newArray(int size) { 1102 return new Bitmap[size]; 1103 } 1104 }; 1105 1106 /** 1107 * No special parcel contents. 1108 */ describeContents()1109 public int describeContents() { 1110 return 0; 1111 } 1112 1113 /** 1114 * Write the bitmap and its pixels to the parcel. The bitmap can be 1115 * rebuilt from the parcel by calling CREATOR.createFromParcel(). 1116 * @param p Parcel object to write the bitmap data into 1117 */ writeToParcel(Parcel p, int flags)1118 public void writeToParcel(Parcel p, int flags) { 1119 checkRecycled("Can't parcel a recycled bitmap"); 1120 if (!nativeWriteToParcel(mNativeBitmap, mIsMutable, mDensity, p)) { 1121 throw new RuntimeException("native writeToParcel failed"); 1122 } 1123 } 1124 1125 /** 1126 * Returns a new bitmap that captures the alpha values of the original. 1127 * This may be drawn with Canvas.drawBitmap(), where the color(s) will be 1128 * taken from the paint that is passed to the draw call. 1129 * 1130 * @return new bitmap containing the alpha channel of the original bitmap. 1131 */ extractAlpha()1132 public Bitmap extractAlpha() { 1133 return extractAlpha(null, null); 1134 } 1135 1136 /** 1137 * Returns a new bitmap that captures the alpha values of the original. 1138 * These values may be affected by the optional Paint parameter, which 1139 * can contain its own alpha, and may also contain a MaskFilter which 1140 * could change the actual dimensions of the resulting bitmap (e.g. 1141 * a blur maskfilter might enlarge the resulting bitmap). If offsetXY 1142 * is not null, it returns the amount to offset the returned bitmap so 1143 * that it will logically align with the original. For example, if the 1144 * paint contains a blur of radius 2, then offsetXY[] would contains 1145 * -2, -2, so that drawing the alpha bitmap offset by (-2, -2) and then 1146 * drawing the original would result in the blur visually aligning with 1147 * the original. 1148 * 1149 * <p>The initial density of the returned bitmap is the same as the original's. 1150 * 1151 * @param paint Optional paint used to modify the alpha values in the 1152 * resulting bitmap. Pass null for default behavior. 1153 * @param offsetXY Optional array that returns the X (index 0) and Y 1154 * (index 1) offset needed to position the returned bitmap 1155 * so that it visually lines up with the original. 1156 * @return new bitmap containing the (optionally modified by paint) alpha 1157 * channel of the original bitmap. This may be drawn with 1158 * Canvas.drawBitmap(), where the color(s) will be taken from the 1159 * paint that is passed to the draw call. 1160 */ extractAlpha(Paint paint, int[] offsetXY)1161 public Bitmap extractAlpha(Paint paint, int[] offsetXY) { 1162 checkRecycled("Can't extractAlpha on a recycled bitmap"); 1163 int nativePaint = paint != null ? paint.mNativePaint : 0; 1164 Bitmap bm = nativeExtractAlpha(mNativeBitmap, nativePaint, offsetXY); 1165 if (bm == null) { 1166 throw new RuntimeException("Failed to extractAlpha on Bitmap"); 1167 } 1168 bm.mDensity = mDensity; 1169 return bm; 1170 } 1171 1172 /** 1173 * Given another bitmap, return true if it has the same dimensions, config, 1174 * and pixel data as this bitmap. If any of those differ, return false. 1175 * If other is null, return false. 1176 */ sameAs(Bitmap other)1177 public boolean sameAs(Bitmap other) { 1178 return this == other || (other != null && nativeSameAs(mNativeBitmap, other.mNativeBitmap)); 1179 } 1180 1181 /** 1182 * Rebuilds any caches associated with the bitmap that are used for 1183 * drawing it. In the case of purgeable bitmaps, this call will attempt to 1184 * ensure that the pixels have been decoded. 1185 * If this is called on more than one bitmap in sequence, the priority is 1186 * given in LRU order (i.e. the last bitmap called will be given highest 1187 * priority). 1188 * 1189 * For bitmaps with no associated caches, this call is effectively a no-op, 1190 * and therefore is harmless. 1191 */ prepareToDraw()1192 public void prepareToDraw() { 1193 nativePrepareToDraw(mNativeBitmap); 1194 } 1195 1196 private static class BitmapFinalizer { 1197 private final int mNativeBitmap; 1198 BitmapFinalizer(int nativeBitmap)1199 BitmapFinalizer(int nativeBitmap) { 1200 mNativeBitmap = nativeBitmap; 1201 } 1202 1203 @Override finalize()1204 public void finalize() { 1205 try { 1206 super.finalize(); 1207 } catch (Throwable t) { 1208 // Ignore 1209 } finally { 1210 nativeDestructor(mNativeBitmap); 1211 } 1212 } 1213 } 1214 1215 //////////// native methods 1216 nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean mutable)1217 private static native Bitmap nativeCreate(int[] colors, int offset, 1218 int stride, int width, int height, 1219 int nativeConfig, boolean mutable); nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable)1220 private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig, 1221 boolean isMutable); nativeDestructor(int nativeBitmap)1222 private static native void nativeDestructor(int nativeBitmap); nativeRecycle(int nativeBitmap)1223 private static native void nativeRecycle(int nativeBitmap); 1224 nativeCompress(int nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage)1225 private static native boolean nativeCompress(int nativeBitmap, int format, 1226 int quality, OutputStream stream, 1227 byte[] tempStorage); nativeErase(int nativeBitmap, int color)1228 private static native void nativeErase(int nativeBitmap, int color); nativeWidth(int nativeBitmap)1229 private static native int nativeWidth(int nativeBitmap); nativeHeight(int nativeBitmap)1230 private static native int nativeHeight(int nativeBitmap); nativeRowBytes(int nativeBitmap)1231 private static native int nativeRowBytes(int nativeBitmap); nativeConfig(int nativeBitmap)1232 private static native int nativeConfig(int nativeBitmap); nativeHasAlpha(int nativeBitmap)1233 private static native boolean nativeHasAlpha(int nativeBitmap); 1234 nativeGetPixel(int nativeBitmap, int x, int y)1235 private static native int nativeGetPixel(int nativeBitmap, int x, int y); nativeGetPixels(int nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height)1236 private static native void nativeGetPixels(int nativeBitmap, int[] pixels, 1237 int offset, int stride, int x, 1238 int y, int width, int height); 1239 nativeSetPixel(int nativeBitmap, int x, int y, int color)1240 private static native void nativeSetPixel(int nativeBitmap, int x, int y, 1241 int color); nativeSetPixels(int nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height)1242 private static native void nativeSetPixels(int nativeBitmap, int[] colors, 1243 int offset, int stride, int x, 1244 int y, int width, int height); nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst)1245 private static native void nativeCopyPixelsToBuffer(int nativeBitmap, 1246 Buffer dst); nativeCopyPixelsFromBuffer(int nb, Buffer src)1247 private static native void nativeCopyPixelsFromBuffer(int nb, Buffer src); nativeGenerationId(int nativeBitmap)1248 private static native int nativeGenerationId(int nativeBitmap); 1249 nativeCreateFromParcel(Parcel p)1250 private static native Bitmap nativeCreateFromParcel(Parcel p); 1251 // returns true on success nativeWriteToParcel(int nativeBitmap, boolean isMutable, int density, Parcel p)1252 private static native boolean nativeWriteToParcel(int nativeBitmap, 1253 boolean isMutable, 1254 int density, 1255 Parcel p); 1256 // returns a new bitmap built from the native bitmap's alpha, and the paint nativeExtractAlpha(int nativeBitmap, int nativePaint, int[] offsetXY)1257 private static native Bitmap nativeExtractAlpha(int nativeBitmap, 1258 int nativePaint, 1259 int[] offsetXY); 1260 nativePrepareToDraw(int nativeBitmap)1261 private static native void nativePrepareToDraw(int nativeBitmap); nativeSetHasAlpha(int nBitmap, boolean hasAlpha)1262 private static native void nativeSetHasAlpha(int nBitmap, boolean hasAlpha); nativeSameAs(int nb0, int nb1)1263 private static native boolean nativeSameAs(int nb0, int nb1); 1264 ni()1265 /* package */ final int ni() { 1266 return mNativeBitmap; 1267 } 1268 } 1269