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