Home | History | Annotate | Download | only in graphics
      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      */
     60     public static void setDefaultDensity(int density) {
     61         sDefaultDensity = density;
     62     }
     63 
     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     */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    221         Config(int ni) {
    222             this.nativeInt = ni;
    223         }
    224         final int nativeInt;
    225 
    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      */
    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      */
    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      */
    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 
    322     /**
    323      * Creates a new bitmap, scaled from an existing bitmap.
    324      *
    325      * @param src       The source bitmap.
    326      * @param dstWidth  The new bitmap's desired width.
    327      * @param dstHeight The new bitmap's desired height.
    328      * @param filter    true if the source should be filtered.
    329      * @return the new scaled bitmap.
    330      */
    331     public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
    332             int dstHeight, boolean filter) {
    333         Matrix m;
    334         synchronized (Bitmap.class) {
    335             // small pool of just 1 matrix
    336             m = sScaleMatrix;
    337             sScaleMatrix = null;
    338         }
    339 
    340         if (m == null) {
    341             m = new Matrix();
    342         }
    343 
    344         final int width = src.getWidth();
    345         final int height = src.getHeight();
    346         final float sx = dstWidth  / (float)width;
    347         final float sy = dstHeight / (float)height;
    348         m.setScale(sx, sy);
    349         Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
    350 
    351         synchronized (Bitmap.class) {
    352             // do we need to check for null? why not just assign everytime?
    353             if (sScaleMatrix == null) {
    354                 sScaleMatrix = m;
    355             }
    356         }
    357 
    358         return b;
    359     }
    360 
    361     /**
    362      * Returns an immutable bitmap from the source bitmap. The new bitmap may
    363      * be the same object as source, or a copy may have been made.  It is
    364      * initialized with the same density as the original bitmap.
    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.  It is
    374      * initialized with the same density as the original bitmap.
    375      *
    376      * @param source   The bitmap we are subsetting
    377      * @param x        The x coordinate of the first pixel in source
    378      * @param y        The y coordinate of the first pixel in source
    379      * @param width    The number of pixels in each row
    380      * @param height   The number of rows
    381      */
    382     public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {
    383         return createBitmap(source, x, y, width, height, null, false);
    384     }
    385 
    386     /**
    387      * Returns an immutable bitmap from subset of the source bitmap,
    388      * transformed by the optional matrix.  It is
    389      * initialized with the same density as the original bitmap.
    390      *
    391      * @param source   The bitmap we are subsetting
    392      * @param x        The x coordinate of the first pixel in source
    393      * @param y        The y coordinate of the first pixel in source
    394      * @param width    The number of pixels in each row
    395      * @param height   The number of rows
    396      * @param m        Optional matrix to be applied to the pixels
    397      * @param filter   true if the source should be filtered.
    398      *                   Only applies if the matrix contains more than just
    399      *                   translation.
    400      * @return A bitmap that represents the specified subset of source
    401      * @throws IllegalArgumentException if the x, y, width, height values are
    402      *         outside of the dimensions of the source bitmap.
    403      */
    404     public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
    405             Matrix m, boolean filter) {
    406 
    407         checkXYSign(x, y);
    408         checkWidthHeight(width, height);
    409         if (x + width > source.getWidth()) {
    410             throw new IllegalArgumentException("x + width must be <= bitmap.width()");
    411         }
    412         if (y + height > source.getHeight()) {
    413             throw new IllegalArgumentException("y + height must be <= bitmap.height()");
    414         }
    415 
    416         // check if we can just return our argument unchanged
    417         if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() &&
    418                 height == source.getHeight() && (m == null || m.isIdentity())) {
    419             return source;
    420         }
    421 
    422         int neww = width;
    423         int newh = height;
    424         Canvas canvas = new Canvas();
    425         Bitmap bitmap;
    426         Paint paint;
    427 
    428         Rect srcR = new Rect(x, y, x + width, y + height);
    429         RectF dstR = new RectF(0, 0, width, height);
    430 
    431         if (m == null || m.isIdentity()) {
    432             bitmap = createBitmap(neww, newh,
    433                     source.hasAlpha() ? Config.ARGB_8888 : Config.RGB_565);
    434             paint = null;   // not needed
    435         } else {
    436             /*  the dst should have alpha if the src does, or if our matrix
    437                 doesn't preserve rectness
    438             */
    439             boolean hasAlpha = source.hasAlpha() || !m.rectStaysRect();
    440             RectF deviceR = new RectF();
    441             m.mapRect(deviceR, dstR);
    442             neww = Math.round(deviceR.width());
    443             newh = Math.round(deviceR.height());
    444             bitmap = createBitmap(neww, newh, hasAlpha ? Config.ARGB_8888 : Config.RGB_565);
    445             if (hasAlpha) {
    446                 bitmap.eraseColor(0);
    447             }
    448             canvas.translate(-deviceR.left, -deviceR.top);
    449             canvas.concat(m);
    450             paint = new Paint();
    451             paint.setFilterBitmap(filter);
    452             if (!m.rectStaysRect()) {
    453                 paint.setAntiAlias(true);
    454             }
    455         }
    456 
    457         // The new bitmap was created from a known bitmap source so assume that
    458         // they use the same density
    459         bitmap.mDensity = source.mDensity;
    460 
    461         canvas.setBitmap(bitmap);
    462         canvas.drawBitmap(source, srcR, dstR, paint);
    463 
    464         return bitmap;
    465     }
    466 
    467     /**
    468      * Returns a mutable bitmap with the specified width and height.  Its
    469      * initial density is as per {@link #getDensity}.
    470      *
    471      * @param width    The width of the bitmap
    472      * @param height   The height of the bitmap
    473      * @param config   The bitmap config to create.
    474      * @throws IllegalArgumentException if the width or height are <= 0
    475      */
    476     public static Bitmap createBitmap(int width, int height, Config config) {
    477         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
    478         bm.eraseColor(0);    // start with black/transparent pixels
    479         return bm;
    480     }
    481 
    482     /**
    483      * Returns a immutable bitmap with the specified width and height, with each
    484      * pixel value set to the corresponding value in the colors array.  Its
    485      * initial density is as per {@link #getDensity}.
    486      *
    487      * @param colors   Array of {@link Color} used to initialize the pixels.
    488      * @param offset   Number of values to skip before the first color in the
    489      *                 array of colors.
    490      * @param stride   Number of colors in the array between rows (must be >=
    491      *                 width or <= -width).
    492      * @param width    The width of the bitmap
    493      * @param height   The height of the bitmap
    494      * @param config   The bitmap config to create. If the config does not
    495      *                 support per-pixel alpha (e.g. RGB_565), then the alpha
    496      *                 bytes in the colors[] will be ignored (assumed to be FF)
    497      * @throws IllegalArgumentException if the width or height are <= 0, or if
    498      *         the color array's length is less than the number of pixels.
    499      */
    500     public static Bitmap createBitmap(int colors[], int offset, int stride,
    501             int width, int height, Config config) {
    502 
    503         checkWidthHeight(width, height);
    504         if (Math.abs(stride) < width) {
    505             throw new IllegalArgumentException("abs(stride) must be >= width");
    506         }
    507         int lastScanline = offset + (height - 1) * stride;
    508         int length = colors.length;
    509         if (offset < 0 || (offset + width > length) || lastScanline < 0 ||
    510                 (lastScanline + width > length)) {
    511             throw new ArrayIndexOutOfBoundsException();
    512         }
    513         return nativeCreate(colors, offset, stride, width, height,
    514                             config.nativeInt, false);
    515     }
    516 
    517     /**
    518      * Returns a immutable bitmap with the specified width and height, with each
    519      * pixel value set to the corresponding value in the colors array.  Its
    520      * initial density is as per {@link #getDensity}.
    521      *
    522      * @param colors   Array of {@link Color} used to initialize the pixels.
    523      *                 This array must be at least as large as width * height.
    524      * @param width    The width of the bitmap
    525      * @param height   The height of the bitmap
    526      * @param config   The bitmap config to create. If the config does not
    527      *                 support per-pixel alpha (e.g. RGB_565), then the alpha
    528      *                 bytes in the colors[] will be ignored (assumed to be FF)
    529      * @throws IllegalArgumentException if the width or height are <= 0, or if
    530      *         the color array's length is less than the number of pixels.
    531      */
    532     public static Bitmap createBitmap(int colors[], int width, int height, Config config) {
    533         return createBitmap(colors, 0, width, width, height, config);
    534     }
    535 
    536     /**
    537      * Returns an optional array of private data, used by the UI system for
    538      * some bitmaps. Not intended to be called by applications.
    539      */
    540     public byte[] getNinePatchChunk() {
    541         return mNinePatchChunk;
    542     }
    543 
    544     /**
    545      * Specifies the known formats a bitmap can be compressed into
    546      */
    547     public enum CompressFormat {
    548         JPEG    (0),
    549         PNG     (1);
    550 
    551         CompressFormat(int nativeInt) {
    552             this.nativeInt = nativeInt;
    553         }
    554         final int nativeInt;
    555     }
    556 
    557     /**
    558      * Number of bytes of temp storage we use for communicating between the
    559      * native compressor and the java OutputStream.
    560      */
    561     private final static int WORKING_COMPRESS_STORAGE = 4096;
    562 
    563     /**
    564      * Write a compressed version of the bitmap to the specified outputstream.
    565      * If this returns true, the bitmap can be reconstructed by passing a
    566      * corresponding inputstream to BitmapFactory.decodeStream(). Note: not
    567      * all Formats support all bitmap configs directly, so it is possible that
    568      * the returned bitmap from BitmapFactory could be in a different bitdepth,
    569      * and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque
    570      * pixels).
    571      *
    572      * @param format   The format of the compressed image
    573      * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
    574      *                 small size, 100 meaning compress for max quality. Some
    575      *                 formats, like PNG which is lossless, will ignore the
    576      *                 quality setting
    577      * @param stream   The outputstream to write the compressed data.
    578      * @return true if successfully compressed to the specified stream.
    579      */
    580     public boolean compress(CompressFormat format, int quality, OutputStream stream) {
    581         checkRecycled("Can't compress a recycled bitmap");
    582         // do explicit check before calling the native method
    583         if (stream == null) {
    584             throw new NullPointerException();
    585         }
    586         if (quality < 0 || quality > 100) {
    587             throw new IllegalArgumentException("quality must be 0..100");
    588         }
    589         return nativeCompress(mNativeBitmap, format.nativeInt, quality,
    590                               stream, new byte[WORKING_COMPRESS_STORAGE]);
    591     }
    592 
    593     /**
    594      * Returns true if the bitmap is marked as mutable (i.e. can be drawn into)
    595      */
    596     public final boolean isMutable() {
    597         return mIsMutable;
    598     }
    599 
    600     /** Returns the bitmap's width */
    601     public final int getWidth() {
    602         return mWidth == -1 ? mWidth = nativeWidth(mNativeBitmap) : mWidth;
    603     }
    604 
    605     /** Returns the bitmap's height */
    606     public final int getHeight() {
    607         return mHeight == -1 ? mHeight = nativeHeight(mNativeBitmap) : mHeight;
    608     }
    609 
    610     /**
    611      * Convenience for calling {@link #getScaledWidth(int)} with the target
    612      * density of the given {@link Canvas}.
    613      */
    614     public int getScaledWidth(Canvas canvas) {
    615         return scaleFromDensity(getWidth(), mDensity, canvas.mDensity);
    616     }
    617 
    618     /**
    619      * Convenience for calling {@link #getScaledHeight(int)} with the target
    620      * density of the given {@link Canvas}.
    621      */
    622     public int getScaledHeight(Canvas canvas) {
    623         return scaleFromDensity(getHeight(), mDensity, canvas.mDensity);
    624     }
    625 
    626     /**
    627      * Convenience for calling {@link #getScaledWidth(int)} with the target
    628      * density of the given {@link DisplayMetrics}.
    629      */
    630     public int getScaledWidth(DisplayMetrics metrics) {
    631         return scaleFromDensity(getWidth(), mDensity, metrics.densityDpi);
    632     }
    633 
    634     /**
    635      * Convenience for calling {@link #getScaledHeight(int)} with the target
    636      * density of the given {@link DisplayMetrics}.
    637      */
    638     public int getScaledHeight(DisplayMetrics metrics) {
    639         return scaleFromDensity(getHeight(), mDensity, metrics.densityDpi);
    640     }
    641 
    642     /**
    643      * Convenience method that returns the width of this bitmap divided
    644      * by the density scale factor.
    645      *
    646      * @param targetDensity The density of the target canvas of the bitmap.
    647      * @return The scaled width of this bitmap, according to the density scale factor.
    648      */
    649     public int getScaledWidth(int targetDensity) {
    650         return scaleFromDensity(getWidth(), mDensity, targetDensity);
    651     }
    652 
    653     /**
    654      * Convenience method that returns the height of this bitmap divided
    655      * by the density scale factor.
    656      *
    657      * @param targetDensity The density of the target canvas of the bitmap.
    658      * @return The scaled height of this bitmap, according to the density scale factor.
    659      */
    660     public int getScaledHeight(int targetDensity) {
    661         return scaleFromDensity(getHeight(), mDensity, targetDensity);
    662     }
    663 
    664     /**
    665      * @hide
    666      */
    667     static public int scaleFromDensity(int size, int sdensity, int tdensity) {
    668         if (sdensity == DENSITY_NONE || sdensity == tdensity) {
    669             return size;
    670         }
    671 
    672         // Scale by tdensity / sdensity, rounding up.
    673         return ( (size * tdensity) + (sdensity >> 1) ) / sdensity;
    674     }
    675 
    676     /**
    677      * Return the number of bytes between rows in the bitmap's pixels. Note that
    678      * this refers to the pixels as stored natively by the bitmap. If you call
    679      * getPixels() or setPixels(), then the pixels are uniformly treated as
    680      * 32bit values, packed according to the Color class.
    681      *
    682      * @return number of bytes between rows of the native bitmap pixels.
    683      */
    684     public final int getRowBytes() {
    685         return nativeRowBytes(mNativeBitmap);
    686     }
    687 
    688     /**
    689      * If the bitmap's internal config is in one of the public formats, return
    690      * that config, otherwise return null.
    691      */
    692     public final Config getConfig() {
    693         return Config.nativeToConfig(nativeConfig(mNativeBitmap));
    694     }
    695 
    696     /** Returns true if the bitmap's config supports per-pixel alpha, and
    697      * if the pixels may contain non-opaque alpha values. For some configs,
    698      * this is always false (e.g. RGB_565), since they do not support per-pixel
    699      * alpha. However, for configs that do, the bitmap may be flagged to be
    700      * known that all of its pixels are opaque. In this case hasAlpha() will
    701      * also return false. If a config such as ARGB_8888 is not so flagged,
    702      * it will return true by default.
    703      */
    704     public final boolean hasAlpha() {
    705         return nativeHasAlpha(mNativeBitmap);
    706     }
    707 
    708     /**
    709      * Tell the bitmap if all of the pixels are known to be opaque (false)
    710      * or if some of the pixels may contain non-opaque alpha values (true).
    711      * Note, for some configs (e.g. RGB_565) this call is ignore, since it does
    712      * not support per-pixel alpha values.
    713      *
    714      * This is meant as a drawing hint, as in some cases a bitmap that is known
    715      * to be opaque can take a faster drawing case than one that may have
    716      * non-opaque per-pixel alpha values.
    717      *
    718      * @hide
    719      */
    720     public void setHasAlpha(boolean hasAlpha) {
    721         nativeSetHasAlpha(mNativeBitmap, hasAlpha);
    722     }
    723 
    724     /**
    725      * Fills the bitmap's pixels with the specified {@link Color}.
    726      *
    727      * @throws IllegalStateException if the bitmap is not mutable.
    728      */
    729     public void eraseColor(int c) {
    730         checkRecycled("Can't erase a recycled bitmap");
    731         if (!isMutable()) {
    732             throw new IllegalStateException("cannot erase immutable bitmaps");
    733         }
    734         nativeErase(mNativeBitmap, c);
    735     }
    736 
    737     /**
    738      * Returns the {@link Color} at the specified location. Throws an exception
    739      * if x or y are out of bounds (negative or >= to the width or height
    740      * respectively).
    741      *
    742      * @param x    The x coordinate (0...width-1) of the pixel to return
    743      * @param y    The y coordinate (0...height-1) of the pixel to return
    744      * @return     The argb {@link Color} at the specified coordinate
    745      * @throws IllegalArgumentException if x, y exceed the bitmap's bounds
    746      */
    747     public int getPixel(int x, int y) {
    748         checkRecycled("Can't call getPixel() on a recycled bitmap");
    749         checkPixelAccess(x, y);
    750         return nativeGetPixel(mNativeBitmap, x, y);
    751     }
    752 
    753     /**
    754      * Returns in pixels[] a copy of the data in the bitmap. Each value is
    755      * a packed int representing a {@link Color}. The stride parameter allows
    756      * the caller to allow for gaps in the returned pixels array between
    757      * rows. For normal packed results, just pass width for the stride value.
    758      *
    759      * @param pixels   The array to receive the bitmap's colors
    760      * @param offset   The first index to write into pixels[]
    761      * @param stride   The number of entries in pixels[] to skip between
    762      *                 rows (must be >= bitmap's width). Can be negative.
    763      * @param x        The x coordinate of the first pixel to read from
    764      *                 the bitmap
    765      * @param y        The y coordinate of the first pixel to read from
    766      *                 the bitmap
    767      * @param width    The number of pixels to read from each row
    768      * @param height   The number of rows to read
    769      * @throws IllegalArgumentException if x, y, width, height exceed the
    770      *         bounds of the bitmap, or if abs(stride) < width.
    771      * @throws ArrayIndexOutOfBoundsException if the pixels array is too small
    772      *         to receive the specified number of pixels.
    773      */
    774     public void getPixels(int[] pixels, int offset, int stride,
    775                           int x, int y, int width, int height) {
    776         checkRecycled("Can't call getPixels() on a recycled bitmap");
    777         if (width == 0 || height == 0) {
    778             return; // nothing to do
    779         }
    780         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
    781         nativeGetPixels(mNativeBitmap, pixels, offset, stride,
    782                         x, y, width, height);
    783     }
    784 
    785     /**
    786      * Shared code to check for illegal arguments passed to getPixel()
    787      * or setPixel()
    788      * @param x x coordinate of the pixel
    789      * @param y y coordinate of the pixel
    790      */
    791     private void checkPixelAccess(int x, int y) {
    792         checkXYSign(x, y);
    793         if (x >= getWidth()) {
    794             throw new IllegalArgumentException("x must be < bitmap.width()");
    795         }
    796         if (y >= getHeight()) {
    797             throw new IllegalArgumentException("y must be < bitmap.height()");
    798         }
    799     }
    800 
    801     /**
    802      * Shared code to check for illegal arguments passed to getPixels()
    803      * or setPixels()
    804      *
    805      * @param x left edge of the area of pixels to access
    806      * @param y top edge of the area of pixels to access
    807      * @param width width of the area of pixels to access
    808      * @param height height of the area of pixels to access
    809      * @param offset offset into pixels[] array
    810      * @param stride number of elements in pixels[] between each logical row
    811      * @param pixels array to hold the area of pixels being accessed
    812     */
    813     private void checkPixelsAccess(int x, int y, int width, int height,
    814                                    int offset, int stride, int pixels[]) {
    815         checkXYSign(x, y);
    816         if (width < 0) {
    817             throw new IllegalArgumentException("width must be >= 0");
    818         }
    819         if (height < 0) {
    820             throw new IllegalArgumentException("height must be >= 0");
    821         }
    822         if (x + width > getWidth()) {
    823             throw new IllegalArgumentException(
    824                     "x + width must be <= bitmap.width()");
    825         }
    826         if (y + height > getHeight()) {
    827             throw new IllegalArgumentException(
    828                     "y + height must be <= bitmap.height()");
    829         }
    830         if (Math.abs(stride) < width) {
    831             throw new IllegalArgumentException("abs(stride) must be >= width");
    832         }
    833         int lastScanline = offset + (height - 1) * stride;
    834         int length = pixels.length;
    835         if (offset < 0 || (offset + width > length)
    836                 || lastScanline < 0
    837                 || (lastScanline + width > length)) {
    838             throw new ArrayIndexOutOfBoundsException();
    839         }
    840     }
    841 
    842     /**
    843      * Write the specified {@link Color} into the bitmap (assuming it is
    844      * mutable) at the x,y coordinate.
    845      *
    846      * @param x     The x coordinate of the pixel to replace (0...width-1)
    847      * @param y     The y coordinate of the pixel to replace (0...height-1)
    848      * @param color The {@link Color} to write into the bitmap
    849      * @throws IllegalStateException if the bitmap is not mutable
    850      * @throws IllegalArgumentException if x, y are outside of the bitmap's
    851      *         bounds.
    852      */
    853     public void setPixel(int x, int y, int color) {
    854         checkRecycled("Can't call setPixel() on a recycled bitmap");
    855         if (!isMutable()) {
    856             throw new IllegalStateException();
    857         }
    858         checkPixelAccess(x, y);
    859         nativeSetPixel(mNativeBitmap, x, y, color);
    860     }
    861 
    862     /**
    863      * Replace pixels in the bitmap with the colors in the array. Each element
    864      * in the array is a packed int prepresenting a {@link Color}
    865      *
    866      * @param pixels   The colors to write to the bitmap
    867      * @param offset   The index of the first color to read from pixels[]
    868      * @param stride   The number of colors in pixels[] to skip between rows.
    869      *                 Normally this value will be the same as the width of
    870      *                 the bitmap, but it can be larger (or negative).
    871      * @param x        The x coordinate of the first pixel to write to in
    872      *                 the bitmap.
    873      * @param y        The y coordinate of the first pixel to write to in
    874      *                 the bitmap.
    875      * @param width    The number of colors to copy from pixels[] per row
    876      * @param height   The number of rows to write to the bitmap
    877      * @throws IllegalStateException if the bitmap is not mutable
    878      * @throws IllegalArgumentException if x, y, width, height are outside of
    879      *         the bitmap's bounds.
    880      * @throws ArrayIndexOutOfBoundsException if the pixels array is too small
    881      *         to receive the specified number of pixels.
    882      */
    883     public void setPixels(int[] pixels, int offset, int stride,
    884                           int x, int y, int width, int height) {
    885         checkRecycled("Can't call setPixels() on a recycled bitmap");
    886         if (!isMutable()) {
    887             throw new IllegalStateException();
    888         }
    889         if (width == 0 || height == 0) {
    890             return; // nothing to do
    891         }
    892         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
    893         nativeSetPixels(mNativeBitmap, pixels, offset, stride,
    894                         x, y, width, height);
    895     }
    896 
    897     public static final Parcelable.Creator<Bitmap> CREATOR
    898             = new Parcelable.Creator<Bitmap>() {
    899         /**
    900          * Rebuilds a bitmap previously stored with writeToParcel().
    901          *
    902          * @param p    Parcel object to read the bitmap from
    903          * @return a new bitmap created from the data in the parcel
    904          */
    905         public Bitmap createFromParcel(Parcel p) {
    906             Bitmap bm = nativeCreateFromParcel(p);
    907             if (bm == null) {
    908                 throw new RuntimeException("Failed to unparcel Bitmap");
    909             }
    910             return bm;
    911         }
    912         public Bitmap[] newArray(int size) {
    913             return new Bitmap[size];
    914         }
    915     };
    916 
    917     /**
    918      * No special parcel contents.
    919      */
    920     public int describeContents() {
    921         return 0;
    922     }
    923 
    924     /**
    925      * Write the bitmap and its pixels to the parcel. The bitmap can be
    926      * rebuilt from the parcel by calling CREATOR.createFromParcel().
    927      * @param p    Parcel object to write the bitmap data into
    928      */
    929     public void writeToParcel(Parcel p, int flags) {
    930         checkRecycled("Can't parcel a recycled bitmap");
    931         if (!nativeWriteToParcel(mNativeBitmap, mIsMutable, mDensity, p)) {
    932             throw new RuntimeException("native writeToParcel failed");
    933         }
    934     }
    935 
    936     /**
    937      * Returns a new bitmap that captures the alpha values of the original.
    938      * This may be drawn with Canvas.drawBitmap(), where the color(s) will be
    939      * taken from the paint that is passed to the draw call.
    940      *
    941      * @return new bitmap containing the alpha channel of the original bitmap.
    942      */
    943     public Bitmap extractAlpha() {
    944         return extractAlpha(null, null);
    945     }
    946 
    947     /**
    948      * Returns a new bitmap that captures the alpha values of the original.
    949      * These values may be affected by the optional Paint parameter, which
    950      * can contain its own alpha, and may also contain a MaskFilter which
    951      * could change the actual dimensions of the resulting bitmap (e.g.
    952      * a blur maskfilter might enlarge the resulting bitmap). If offsetXY
    953      * is not null, it returns the amount to offset the returned bitmap so
    954      * that it will logically align with the original. For example, if the
    955      * paint contains a blur of radius 2, then offsetXY[] would contains
    956      * -2, -2, so that drawing the alpha bitmap offset by (-2, -2) and then
    957      * drawing the original would result in the blur visually aligning with
    958      * the original.
    959      *
    960      * <p>The initial density of the returned bitmap is the same as the original's.
    961      *
    962      * @param paint Optional paint used to modify the alpha values in the
    963      *              resulting bitmap. Pass null for default behavior.
    964      * @param offsetXY Optional array that returns the X (index 0) and Y
    965      *                 (index 1) offset needed to position the returned bitmap
    966      *                 so that it visually lines up with the original.
    967      * @return new bitmap containing the (optionally modified by paint) alpha
    968      *         channel of the original bitmap. This may be drawn with
    969      *         Canvas.drawBitmap(), where the color(s) will be taken from the
    970      *         paint that is passed to the draw call.
    971      */
    972     public Bitmap extractAlpha(Paint paint, int[] offsetXY) {
    973         checkRecycled("Can't extractAlpha on a recycled bitmap");
    974         int nativePaint = paint != null ? paint.mNativePaint : 0;
    975         Bitmap bm = nativeExtractAlpha(mNativeBitmap, nativePaint, offsetXY);
    976         if (bm == null) {
    977             throw new RuntimeException("Failed to extractAlpha on Bitmap");
    978         }
    979         bm.mDensity = mDensity;
    980         return bm;
    981     }
    982 
    983     /**
    984      *  Given another bitmap, return true if it has the same dimensions, config,
    985      *  and pixel data as this bitmap. If any of those differ, return false.
    986      *  If other is null, return false.
    987      *
    988      * @hide (just needed internally right now)
    989      */
    990     public boolean sameAs(Bitmap other) {
    991         return this == other ||
    992               (other != null &&
    993                nativeSameAs(mNativeBitmap, other.mNativeBitmap));
    994     }
    995 
    996     /**
    997      * Rebuilds any caches associated with the bitmap that are used for
    998      * drawing it. In the case of purgeable bitmaps, this call will attempt to
    999      * ensure that the pixels have been decoded.
   1000      * If this is called on more than one bitmap in sequence, the priority is
   1001      * given in LRU order (i.e. the last bitmap called will be given highest
   1002      * priority).
   1003      *
   1004      * For bitmaps with no associated caches, this call is effectively a no-op,
   1005      * and therefore is harmless.
   1006      */
   1007     public void prepareToDraw() {
   1008         nativePrepareToDraw(mNativeBitmap);
   1009     }
   1010 
   1011     @Override
   1012     protected void finalize() throws Throwable {
   1013         try {
   1014             nativeDestructor(mNativeBitmap);
   1015         } finally {
   1016             super.finalize();
   1017         }
   1018     }
   1019 
   1020     //////////// native methods
   1021 
   1022     private static native Bitmap nativeCreate(int[] colors, int offset,
   1023                                               int stride, int width, int height,
   1024                                             int nativeConfig, boolean mutable);
   1025     private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig,
   1026                                             boolean isMutable);
   1027     private static native void nativeDestructor(int nativeBitmap);
   1028     private static native void nativeRecycle(int nativeBitmap);
   1029 
   1030     private static native boolean nativeCompress(int nativeBitmap, int format,
   1031                                             int quality, OutputStream stream,
   1032                                             byte[] tempStorage);
   1033     private static native void nativeErase(int nativeBitmap, int color);
   1034     private static native int nativeWidth(int nativeBitmap);
   1035     private static native int nativeHeight(int nativeBitmap);
   1036     private static native int nativeRowBytes(int nativeBitmap);
   1037     private static native int nativeConfig(int nativeBitmap);
   1038     private static native boolean nativeHasAlpha(int nativeBitmap);
   1039 
   1040     private static native int nativeGetPixel(int nativeBitmap, int x, int y);
   1041     private static native void nativeGetPixels(int nativeBitmap, int[] pixels,
   1042                                                int offset, int stride, int x,
   1043                                                int y, int width, int height);
   1044 
   1045     private static native void nativeSetPixel(int nativeBitmap, int x, int y,
   1046                                               int color);
   1047     private static native void nativeSetPixels(int nativeBitmap, int[] colors,
   1048                                                int offset, int stride, int x,
   1049                                                int y, int width, int height);
   1050     private static native void nativeCopyPixelsToBuffer(int nativeBitmap,
   1051                                                         Buffer dst);
   1052     private static native void nativeCopyPixelsFromBuffer(int nb, Buffer src);
   1053 
   1054     private static native Bitmap nativeCreateFromParcel(Parcel p);
   1055     // returns true on success
   1056     private static native boolean nativeWriteToParcel(int nativeBitmap,
   1057                                                       boolean isMutable,
   1058                                                       int density,
   1059                                                       Parcel p);
   1060     // returns a new bitmap built from the native bitmap's alpha, and the paint
   1061     private static native Bitmap nativeExtractAlpha(int nativeBitmap,
   1062                                                     int nativePaint,
   1063                                                     int[] offsetXY);
   1064 
   1065     private static native void nativePrepareToDraw(int nativeBitmap);
   1066     private static native void nativeSetHasAlpha(int nBitmap, boolean hasAlpha);
   1067     private static native boolean nativeSameAs(int nb0, int nb1);
   1068 
   1069     /* package */ final int ni() {
   1070         return mNativeBitmap;
   1071     }
   1072 }
   1073