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