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