Bitmap.cpp revision 56c58f66b97d22fe7e7de1f7d9548bcbe1973029
1#include "SkBitmap.h"
2#include "SkPixelRef.h"
3#include "SkImageEncoder.h"
4#include "SkColorPriv.h"
5#include "GraphicsJNI.h"
6#include "SkDither.h"
7#include "SkUnPreMultiply.h"
8
9#include <binder/Parcel.h>
10#include "android_util_Binder.h"
11#include "android_nio_utils.h"
12#include "CreateJavaOutputStreamAdaptor.h"
13
14#include <jni.h>
15
16#include <Caches.h>
17
18#if 0
19    #define TRACE_BITMAP(code)  code
20#else
21    #define TRACE_BITMAP(code)
22#endif
23
24///////////////////////////////////////////////////////////////////////////////
25// Conversions to/from SkColor, for get/setPixels, and the create method, which
26// is basically like setPixels
27
28typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
29                              int x, int y);
30
31static void FromColor_D32(void* dst, const SkColor src[], int width,
32                          int, int) {
33    SkPMColor* d = (SkPMColor*)dst;
34
35    for (int i = 0; i < width; i++) {
36        *d++ = SkPreMultiplyColor(*src++);
37    }
38}
39
40static void FromColor_D565(void* dst, const SkColor src[], int width,
41                           int x, int y) {
42    uint16_t* d = (uint16_t*)dst;
43
44    DITHER_565_SCAN(y);
45    for (int stop = x + width; x < stop; x++) {
46        SkColor c = *src++;
47        *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
48                                DITHER_VALUE(x));
49    }
50}
51
52static void FromColor_D4444(void* dst, const SkColor src[], int width,
53                            int x, int y) {
54    SkPMColor16* d = (SkPMColor16*)dst;
55
56    DITHER_4444_SCAN(y);
57    for (int stop = x + width; x < stop; x++) {
58        SkPMColor c = SkPreMultiplyColor(*src++);
59        *d++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
60//        *d++ = SkPixel32ToPixel4444(c);
61    }
62}
63
64// can return NULL
65static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {
66    switch (config) {
67        case SkBitmap::kARGB_8888_Config:
68            return FromColor_D32;
69        case SkBitmap::kARGB_4444_Config:
70            return FromColor_D4444;
71        case SkBitmap::kRGB_565_Config:
72            return FromColor_D565;
73        default:
74            break;
75    }
76    return NULL;
77}
78
79bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors,
80                            int srcOffset, int srcStride,
81                            int x, int y, int width, int height,
82                            const SkBitmap& dstBitmap) {
83    SkAutoLockPixels alp(dstBitmap);
84    void* dst = dstBitmap.getPixels();
85    FromColorProc proc = ChooseFromColorProc(dstBitmap.config());
86
87    if (NULL == dst || NULL == proc) {
88        return false;
89    }
90
91    const jint* array = env->GetIntArrayElements(srcColors, NULL);
92    const SkColor* src = (const SkColor*)array + srcOffset;
93
94    // reset to to actual choice from caller
95    dst = dstBitmap.getAddr(x, y);
96    // now copy/convert each scanline
97    for (int y = 0; y < height; y++) {
98        proc(dst, src, width, x, y);
99        src += srcStride;
100        dst = (char*)dst + dstBitmap.rowBytes();
101    }
102
103    dstBitmap.notifyPixelsChanged();
104
105    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
106                                 JNI_ABORT);
107    return true;
108}
109
110//////////////////// ToColor procs
111
112typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
113                            SkColorTable*);
114
115static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
116                              SkColorTable*) {
117    SkASSERT(width > 0);
118    const SkPMColor* s = (const SkPMColor*)src;
119    do {
120        *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
121    } while (--width != 0);
122}
123
124static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
125                               SkColorTable*) {
126    SkASSERT(width > 0);
127    const SkPMColor* s = (const SkPMColor*)src;
128    do {
129        SkPMColor c = *s++;
130        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
131                               SkGetPackedB32(c));
132    } while (--width != 0);
133}
134
135static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
136                                SkColorTable*) {
137    SkASSERT(width > 0);
138    const SkPMColor16* s = (const SkPMColor16*)src;
139    do {
140        *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
141    } while (--width != 0);
142}
143
144static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
145                                 SkColorTable*) {
146    SkASSERT(width > 0);
147    const SkPMColor* s = (const SkPMColor*)src;
148    do {
149        SkPMColor c = SkPixel4444ToPixel32(*s++);
150        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
151                               SkGetPackedB32(c));
152    } while (--width != 0);
153}
154
155static void ToColor_S565(SkColor dst[], const void* src, int width,
156                         SkColorTable*) {
157    SkASSERT(width > 0);
158    const uint16_t* s = (const uint16_t*)src;
159    do {
160        uint16_t c = *s++;
161        *dst++ =  SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
162                                SkPacked16ToB32(c));
163    } while (--width != 0);
164}
165
166static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
167                              SkColorTable* ctable) {
168    SkASSERT(width > 0);
169    const uint8_t* s = (const uint8_t*)src;
170    const SkPMColor* colors = ctable->lockColors();
171    do {
172        *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
173    } while (--width != 0);
174    ctable->unlockColors(false);
175}
176
177static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
178                               SkColorTable* ctable) {
179    SkASSERT(width > 0);
180    const uint8_t* s = (const uint8_t*)src;
181    const SkPMColor* colors = ctable->lockColors();
182    do {
183        SkPMColor c = colors[*s++];
184        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
185                               SkGetPackedB32(c));
186    } while (--width != 0);
187    ctable->unlockColors(false);
188}
189
190// can return NULL
191static ToColorProc ChooseToColorProc(const SkBitmap& src) {
192    switch (src.config()) {
193        case SkBitmap::kARGB_8888_Config:
194            return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha;
195        case SkBitmap::kARGB_4444_Config:
196            return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha;
197        case SkBitmap::kRGB_565_Config:
198            return ToColor_S565;
199        case SkBitmap::kIndex8_Config:
200            if (src.getColorTable() == NULL) {
201                return NULL;
202            }
203            return src.isOpaque() ? ToColor_SI8_Opaque : ToColor_SI8_Alpha;
204        default:
205            break;
206    }
207    return NULL;
208}
209
210///////////////////////////////////////////////////////////////////////////////
211///////////////////////////////////////////////////////////////////////////////
212
213static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
214                              int offset, int stride, int width, int height,
215                              SkBitmap::Config config, jboolean isMutable) {
216    if (NULL != jColors) {
217        size_t n = env->GetArrayLength(jColors);
218        if (n < SkAbs32(stride) * (size_t)height) {
219            doThrowAIOOBE(env);
220            return NULL;
221        }
222    }
223
224    SkBitmap bitmap;
225
226    bitmap.setConfig(config, width, height);
227
228    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
229    if (NULL == buff) {
230        return NULL;
231    }
232
233    if (jColors != NULL) {
234        GraphicsJNI::SetPixels(env, jColors, offset, stride,
235                               0, 0, width, height, bitmap);
236    }
237
238    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL);
239}
240
241static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
242                           SkBitmap::Config dstConfig, jboolean isMutable) {
243    SkBitmap            result;
244    JavaPixelAllocator  allocator(env);
245
246    if (!src->copyTo(&result, dstConfig, &allocator)) {
247        return NULL;
248    }
249
250    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL);
251}
252
253static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
254#ifdef USE_OPENGL_RENDERER
255    if (android::uirenderer::Caches::hasInstance()) {
256        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
257        return;
258    }
259#endif // USE_OPENGL_RENDERER
260    delete bitmap;
261}
262
263static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {
264#ifdef USE_OPENGL_RENDERER
265    if (android::uirenderer::Caches::hasInstance()) {
266        android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
267        return;
268    }
269#endif // USE_OPENGL_RENDERER
270    bitmap->setPixels(NULL, NULL);
271}
272
273// These must match the int values in Bitmap.java
274enum JavaEncodeFormat {
275    kJPEG_JavaEncodeFormat = 0,
276    kPNG_JavaEncodeFormat = 1,
277    kWEBP_JavaEncodeFormat = 2
278};
279
280static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap,
281                            int format, int quality,
282                            jobject jstream, jbyteArray jstorage) {
283    SkImageEncoder::Type fm;
284
285    switch (format) {
286    case kJPEG_JavaEncodeFormat:
287        fm = SkImageEncoder::kJPEG_Type;
288        break;
289    case kPNG_JavaEncodeFormat:
290        fm = SkImageEncoder::kPNG_Type;
291        break;
292    case kWEBP_JavaEncodeFormat:
293        fm = SkImageEncoder::kWEBP_Type;
294        break;
295    default:
296        return false;
297    }
298
299    bool success = false;
300    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
301    if (NULL != strm) {
302        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
303        if (NULL != encoder) {
304            success = encoder->encodeStream(strm, *bitmap, quality);
305            delete encoder;
306        }
307        delete strm;
308    }
309    return success;
310}
311
312static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
313    bitmap->eraseColor(color);
314}
315
316static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) {
317    return bitmap->width();
318}
319
320static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) {
321    return bitmap->height();
322}
323
324static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
325    return bitmap->rowBytes();
326}
327
328static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
329    return bitmap->config();
330}
331
332static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {
333    return bitmap->getGenerationID();
334}
335
336static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
337    return !bitmap->isOpaque();
338}
339
340static void Bitmap_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap,
341                               jboolean hasAlpha) {
342    bitmap->setIsOpaque(!hasAlpha);
343}
344
345///////////////////////////////////////////////////////////////////////////////
346
347static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
348    if (parcel == NULL) {
349        SkDebugf("-------- unparcel parcel is NULL\n");
350        return NULL;
351    }
352
353    android::Parcel* p = android::parcelForJavaObject(env, parcel);
354
355    const bool              isMutable = p->readInt32() != 0;
356    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
357    const int               width = p->readInt32();
358    const int               height = p->readInt32();
359    const int               rowBytes = p->readInt32();
360    const int               density = p->readInt32();
361
362    if (SkBitmap::kARGB_8888_Config != config &&
363            SkBitmap::kRGB_565_Config != config &&
364            SkBitmap::kARGB_4444_Config != config &&
365            SkBitmap::kIndex8_Config != config &&
366            SkBitmap::kA8_Config != config) {
367        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
368        return NULL;
369    }
370
371    SkBitmap* bitmap = new SkBitmap;
372
373    bitmap->setConfig(config, width, height, rowBytes);
374
375    SkColorTable* ctable = NULL;
376    if (config == SkBitmap::kIndex8_Config) {
377        int count = p->readInt32();
378        if (count > 0) {
379            size_t size = count * sizeof(SkPMColor);
380            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
381            ctable = new SkColorTable(src, count);
382        }
383    }
384
385    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
386    if (NULL == buffer) {
387        SkSafeUnref(ctable);
388        delete bitmap;
389        return NULL;
390    }
391
392    SkSafeUnref(ctable);
393
394    size_t size = bitmap->getSize();
395
396    android::Parcel::ReadableBlob blob;
397    android::status_t status = p->readBlob(size, &blob);
398    if (status) {
399        doThrowRE(env, "Could not read bitmap from parcel blob.");
400        delete bitmap;
401        return NULL;
402    }
403
404    bitmap->lockPixels();
405    memcpy(bitmap->getPixels(), blob.data(), size);
406    bitmap->unlockPixels();
407
408    blob.release();
409    return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density);
410}
411
412static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
413                                     const SkBitmap* bitmap,
414                                     jboolean isMutable, jint density,
415                                     jobject parcel) {
416    if (parcel == NULL) {
417        SkDebugf("------- writeToParcel null parcel\n");
418        return false;
419    }
420
421    android::Parcel* p = android::parcelForJavaObject(env, parcel);
422
423    p->writeInt32(isMutable);
424    p->writeInt32(bitmap->config());
425    p->writeInt32(bitmap->width());
426    p->writeInt32(bitmap->height());
427    p->writeInt32(bitmap->rowBytes());
428    p->writeInt32(density);
429
430    if (bitmap->getConfig() == SkBitmap::kIndex8_Config) {
431        SkColorTable* ctable = bitmap->getColorTable();
432        if (ctable != NULL) {
433            int count = ctable->count();
434            p->writeInt32(count);
435            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
436                   ctable->lockColors(), count * sizeof(SkPMColor));
437            ctable->unlockColors(false);
438        } else {
439            p->writeInt32(0);   // indicate no ctable
440        }
441    }
442
443    size_t size = bitmap->getSize();
444
445    android::Parcel::WritableBlob blob;
446    android::status_t status = p->writeBlob(size, &blob);
447    if (status) {
448        doThrowRE(env, "Could not write bitmap to parcel blob.");
449        return false;
450    }
451
452    bitmap->lockPixels();
453    const void* pSrc =  bitmap->getPixels();
454    if (pSrc == NULL) {
455        memset(blob.data(), 0, size);
456    } else {
457        memcpy(blob.data(), pSrc, size);
458    }
459    bitmap->unlockPixels();
460
461    blob.release();
462    return true;
463}
464
465static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
466                                   const SkBitmap* src, const SkPaint* paint,
467                                   jintArray offsetXY) {
468    SkIPoint  offset;
469    SkBitmap* dst = new SkBitmap;
470    JavaPixelAllocator allocator(env);
471
472    src->extractAlpha(dst, paint, &allocator, &offset);
473    // If Skia can't allocate pixels for destination bitmap, it resets
474    // it, that is set its pixels buffer to NULL, and zero width and height.
475    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
476        delete dst;
477        doThrowOOME(env, "failed to allocate pixels for alpha");
478        return NULL;
479    }
480    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
481        int* array = env->GetIntArrayElements(offsetXY, NULL);
482        array[0] = offset.fX;
483        array[1] = offset.fY;
484        env->ReleaseIntArrayElements(offsetXY, array, 0);
485    }
486
487    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL);
488}
489
490///////////////////////////////////////////////////////////////////////////////
491
492static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
493                           int x, int y) {
494    SkAutoLockPixels alp(*bitmap);
495
496    ToColorProc proc = ChooseToColorProc(*bitmap);
497    if (NULL == proc) {
498        return 0;
499    }
500    const void* src = bitmap->getAddr(x, y);
501    if (NULL == src) {
502        return 0;
503    }
504
505    SkColor dst[1];
506    proc(dst, src, 1, bitmap->getColorTable());
507    return dst[0];
508}
509
510static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
511                             jintArray pixelArray, int offset, int stride,
512                             int x, int y, int width, int height) {
513    SkAutoLockPixels alp(*bitmap);
514
515    ToColorProc proc = ChooseToColorProc(*bitmap);
516    if (NULL == proc) {
517        return;
518    }
519    const void* src = bitmap->getAddr(x, y);
520    if (NULL == src) {
521        return;
522    }
523
524    SkColorTable* ctable = bitmap->getColorTable();
525    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
526    SkColor* d = (SkColor*)dst + offset;
527    while (--height >= 0) {
528        proc(d, src, width, ctable);
529        d += stride;
530        src = (void*)((const char*)src + bitmap->rowBytes());
531    }
532    env->ReleaseIntArrayElements(pixelArray, dst, 0);
533}
534
535///////////////////////////////////////////////////////////////////////////////
536
537static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
538                            int x, int y, SkColor color) {
539    SkAutoLockPixels alp(*bitmap);
540    if (NULL == bitmap->getPixels()) {
541        return;
542    }
543
544    FromColorProc proc = ChooseFromColorProc(bitmap->config());
545    if (NULL == proc) {
546        return;
547    }
548
549    proc(bitmap->getAddr(x, y), &color, 1, x, y);
550    bitmap->notifyPixelsChanged();
551}
552
553static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
554                             jintArray pixelArray, int offset, int stride,
555                             int x, int y, int width, int height) {
556    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
557                           x, y, width, height, *bitmap);
558}
559
560static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
561                                      const SkBitmap* bitmap, jobject jbuffer) {
562    SkAutoLockPixels alp(*bitmap);
563    const void* src = bitmap->getPixels();
564
565    if (NULL != src) {
566        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
567
568        // the java side has already checked that buffer is large enough
569        memcpy(abp.pointer(), src, bitmap->getSize());
570    }
571}
572
573static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
574                                    const SkBitmap* bitmap, jobject jbuffer) {
575    SkAutoLockPixels alp(*bitmap);
576    void* dst = bitmap->getPixels();
577
578    if (NULL != dst) {
579        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
580        // the java side has already checked that buffer is large enough
581        memcpy(dst, abp.pointer(), bitmap->getSize());
582    }
583}
584
585static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0,
586                             const SkBitmap* bm1) {
587    if (bm0->width() != bm1->width() ||
588        bm0->height() != bm1->height() ||
589        bm0->config() != bm1->config()) {
590        return false;
591    }
592
593    SkAutoLockPixels alp0(*bm0);
594    SkAutoLockPixels alp1(*bm1);
595
596    // if we can't load the pixels, return false
597    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
598        return false;
599    }
600
601    if (bm0->config() == SkBitmap::kIndex8_Config) {
602        SkColorTable* ct0 = bm0->getColorTable();
603        SkColorTable* ct1 = bm1->getColorTable();
604        if (NULL == ct0 || NULL == ct1) {
605            return false;
606        }
607        if (ct0->count() != ct1->count()) {
608            return false;
609        }
610
611        SkAutoLockColors alc0(ct0);
612        SkAutoLockColors alc1(ct1);
613        const size_t size = ct0->count() * sizeof(SkPMColor);
614        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
615            return false;
616        }
617    }
618
619    // now compare each scanline. We can't do the entire buffer at once,
620    // since we don't care about the pixel values that might extend beyond
621    // the width (since the scanline might be larger than the logical width)
622    const int h = bm0->height();
623    const size_t size = bm0->width() * bm0->bytesPerPixel();
624    for (int y = 0; y < h; y++) {
625        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
626            return false;
627        }
628    }
629    return true;
630}
631
632static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
633    bitmap->lockPixels();
634    bitmap->unlockPixels();
635}
636
637///////////////////////////////////////////////////////////////////////////////
638
639#include <android_runtime/AndroidRuntime.h>
640
641static JNINativeMethod gBitmapMethods[] = {
642    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
643        (void*)Bitmap_creator },
644    {   "nativeCopy",               "(IIZ)Landroid/graphics/Bitmap;",
645        (void*)Bitmap_copy },
646    {   "nativeDestructor",         "(I)V", (void*)Bitmap_destructor },
647    {   "nativeRecycle",            "(I)V", (void*)Bitmap_recycle },
648    {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",
649        (void*)Bitmap_compress },
650    {   "nativeErase",              "(II)V", (void*)Bitmap_erase },
651    {   "nativeWidth",              "(I)I", (void*)Bitmap_width },
652    {   "nativeHeight",             "(I)I", (void*)Bitmap_height },
653    {   "nativeRowBytes",           "(I)I", (void*)Bitmap_rowBytes },
654    {   "nativeConfig",             "(I)I", (void*)Bitmap_config },
655    {   "nativeHasAlpha",           "(I)Z", (void*)Bitmap_hasAlpha },
656    {   "nativeSetHasAlpha",        "(IZ)V", (void*)Bitmap_setHasAlpha },
657    {   "nativeCreateFromParcel",
658        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
659        (void*)Bitmap_createFromParcel },
660    {   "nativeWriteToParcel",      "(IZILandroid/os/Parcel;)Z",
661        (void*)Bitmap_writeToParcel },
662    {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",
663        (void*)Bitmap_extractAlpha },
664    {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },
665    {   "nativeGetPixel",           "(III)I", (void*)Bitmap_getPixel },
666    {   "nativeGetPixels",          "(I[IIIIIII)V", (void*)Bitmap_getPixels },
667    {   "nativeSetPixel",           "(IIII)V", (void*)Bitmap_setPixel },
668    {   "nativeSetPixels",          "(I[IIIIIII)V", (void*)Bitmap_setPixels },
669    {   "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
670                                            (void*)Bitmap_copyPixelsToBuffer },
671    {   "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
672                                            (void*)Bitmap_copyPixelsFromBuffer },
673    {   "nativeSameAs",             "(II)Z", (void*)Bitmap_sameAs },
674    {   "nativePrepareToDraw",      "(I)V", (void*)Bitmap_prepareToDraw },
675};
676
677#define kClassPathName  "android/graphics/Bitmap"
678
679int register_android_graphics_Bitmap(JNIEnv* env)
680{
681    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
682                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
683}
684