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