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     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    587     public final boolean isMutable() {
    588         return mIsMutable;
    589     }
    590 
    591     /** Returns the bitmap's width */
    592     public final int getWidth() {
    593         return mWidth == -1 ? mWidth = nativeWidth(mNativeBitmap) : mWidth;
    594     }
    595 
    596     /** Returns the bitmap's height */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    651     public int getScaledHeight(int targetDensity) {
    652         return scaleFromDensity(getHeight(), mDensity, targetDensity);
    653     }
    654 
    655     /**
    656      * @hide
    657      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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     */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      *  Given another bitmap, return true if it has the same dimensions, config,
    976      *  and pixel data as this bitmap. If any of those differ, return false.
    977      *  If other is null, return false.
    978      *
    979      * @hide (just needed internally right now)
    980      */
    981     public boolean sameAs(Bitmap other) {
    982         return this == other ||
    983               (other != null &&
    984                nativeSameAs(mNativeBitmap, other.mNativeBitmap));
    985     }
    986 
    987     /**
    988      * Rebuilds any caches associated with the bitmap that are used for
    989      * drawing it. In the case of purgeable bitmaps, this call will attempt to
    990      * ensure that the pixels have been decoded.
    991      * If this is called on more than one bitmap in sequence, the priority is
    992      * given in LRU order (i.e. the last bitmap called will be given highest
    993      * priority).
    994      *
    995      * For bitmaps with no associated caches, this call is effectively a no-op,
    996      * and therefore is harmless.
    997      */
    998     public void prepareToDraw() {
    999         nativePrepareToDraw(mNativeBitmap);
   1000     }
   1001 
   1002     @Override
   1003     protected void finalize() throws Throwable {
   1004         try {
   1005             nativeDestructor(mNativeBitmap);
   1006         } finally {
   1007             super.finalize();
   1008         }
   1009     }
   1010 
   1011     //////////// native methods
   1012 
   1013     private static native Bitmap nativeCreate(int[] colors, int offset,
   1014                                               int stride, int width, int height,
   1015                                             int nativeConfig, boolean mutable);
   1016     private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig,
   1017                                             boolean isMutable);
   1018     private static native void nativeDestructor(int nativeBitmap);
   1019     private static native void nativeRecycle(int nativeBitmap);
   1020 
   1021     private static native boolean nativeCompress(int nativeBitmap, int format,
   1022                                             int quality, OutputStream stream,
   1023                                             byte[] tempStorage);
   1024     private static native void nativeErase(int nativeBitmap, int color);
   1025     private static native int nativeWidth(int nativeBitmap);
   1026     private static native int nativeHeight(int nativeBitmap);
   1027     private static native int nativeRowBytes(int nativeBitmap);
   1028     private static native int nativeConfig(int nativeBitmap);
   1029     private static native boolean nativeHasAlpha(int nativeBitmap);
   1030 
   1031     private static native int nativeGetPixel(int nativeBitmap, int x, int y);
   1032     private static native void nativeGetPixels(int nativeBitmap, int[] pixels,
   1033                                                int offset, int stride, int x,
   1034                                                int y, int width, int height);
   1035 
   1036     private static native void nativeSetPixel(int nativeBitmap, int x, int y,
   1037                                               int color);
   1038     private static native void nativeSetPixels(int nativeBitmap, int[] colors,
   1039                                                int offset, int stride, int x,
   1040                                                int y, int width, int height);
   1041     private static native void nativeCopyPixelsToBuffer(int nativeBitmap,
   1042                                                         Buffer dst);
   1043     private static native void nativeCopyPixelsFromBuffer(int nb, Buffer src);
   1044 
   1045     private static native Bitmap nativeCreateFromParcel(Parcel p);
   1046     // returns true on success
   1047     private static native boolean nativeWriteToParcel(int nativeBitmap,
   1048                                                       boolean isMutable,
   1049                                                       int density,
   1050                                                       Parcel p);
   1051     // returns a new bitmap built from the native bitmap's alpha, and the paint
   1052     private static native Bitmap nativeExtractAlpha(int nativeBitmap,
   1053                                                     int nativePaint,
   1054                                                     int[] offsetXY);
   1055 
   1056     private static native void nativePrepareToDraw(int nativeBitmap);
   1057     private static native void nativeSetHasAlpha(int nBitmap, boolean hasAlpha);
   1058     private static native boolean nativeSameAs(int nb0, int nb1);
   1059 
   1060     /* package */ final int ni() {
   1061         return mNativeBitmap;
   1062     }
   1063 }
   1064