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