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