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