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