Home | History | Annotate | Download | only in graphics
      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 
     28 typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
     29                               int x, int y);
     30 
     31 static 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 
     40 static 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 
     52 static 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
     65 static 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 
     79 bool 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 
    112 typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
    113                             SkColorTable*);
    114 
    115 static 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 
    124 static 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 
    135 static 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 
    144 static 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 
    155 static 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 
    166 static 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 
    177 static 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
    191 static 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 
    213 static 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 
    241 static 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 
    253 static 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 
    263 static 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
    274 enum JavaEncodeFormat {
    275     kJPEG_JavaEncodeFormat = 0,
    276     kPNG_JavaEncodeFormat = 1,
    277     kWEBP_JavaEncodeFormat = 2
    278 };
    279 
    280 static 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 
    312 static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
    313     bitmap->eraseColor(color);
    314 }
    315 
    316 static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) {
    317     return bitmap->width();
    318 }
    319 
    320 static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) {
    321     return bitmap->height();
    322 }
    323 
    324 static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
    325     return bitmap->rowBytes();
    326 }
    327 
    328 static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
    329     return bitmap->config();
    330 }
    331 
    332 static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {
    333     return bitmap->getGenerationID();
    334 }
    335 
    336 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
    337     return !bitmap->isOpaque();
    338 }
    339 
    340 static void Bitmap_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap,
    341                                jboolean hasAlpha) {
    342     bitmap->setIsOpaque(!hasAlpha);
    343 }
    344 
    345 ///////////////////////////////////////////////////////////////////////////////
    346 
    347 static 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 
    412 static 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 
    465 static 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 
    492 static 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 
    510 static 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 
    537 static 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 
    553 static 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 
    560 static 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 
    573 static 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         bitmap->notifyPixelsChanged();
    583     }
    584 }
    585 
    586 static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0,
    587                              const SkBitmap* bm1) {
    588     if (bm0->width() != bm1->width() ||
    589         bm0->height() != bm1->height() ||
    590         bm0->config() != bm1->config()) {
    591         return false;
    592     }
    593 
    594     SkAutoLockPixels alp0(*bm0);
    595     SkAutoLockPixels alp1(*bm1);
    596 
    597     // if we can't load the pixels, return false
    598     if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
    599         return false;
    600     }
    601 
    602     if (bm0->config() == SkBitmap::kIndex8_Config) {
    603         SkColorTable* ct0 = bm0->getColorTable();
    604         SkColorTable* ct1 = bm1->getColorTable();
    605         if (NULL == ct0 || NULL == ct1) {
    606             return false;
    607         }
    608         if (ct0->count() != ct1->count()) {
    609             return false;
    610         }
    611 
    612         SkAutoLockColors alc0(ct0);
    613         SkAutoLockColors alc1(ct1);
    614         const size_t size = ct0->count() * sizeof(SkPMColor);
    615         if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
    616             return false;
    617         }
    618     }
    619 
    620     // now compare each scanline. We can't do the entire buffer at once,
    621     // since we don't care about the pixel values that might extend beyond
    622     // the width (since the scanline might be larger than the logical width)
    623     const int h = bm0->height();
    624     const size_t size = bm0->width() * bm0->bytesPerPixel();
    625     for (int y = 0; y < h; y++) {
    626         if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
    627             return false;
    628         }
    629     }
    630     return true;
    631 }
    632 
    633 static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
    634     bitmap->lockPixels();
    635     bitmap->unlockPixels();
    636 }
    637 
    638 ///////////////////////////////////////////////////////////////////////////////
    639 
    640 #include <android_runtime/AndroidRuntime.h>
    641 
    642 static JNINativeMethod gBitmapMethods[] = {
    643     {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
    644         (void*)Bitmap_creator },
    645     {   "nativeCopy",               "(IIZ)Landroid/graphics/Bitmap;",
    646         (void*)Bitmap_copy },
    647     {   "nativeDestructor",         "(I)V", (void*)Bitmap_destructor },
    648     {   "nativeRecycle",            "(I)V", (void*)Bitmap_recycle },
    649     {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",
    650         (void*)Bitmap_compress },
    651     {   "nativeErase",              "(II)V", (void*)Bitmap_erase },
    652     {   "nativeWidth",              "(I)I", (void*)Bitmap_width },
    653     {   "nativeHeight",             "(I)I", (void*)Bitmap_height },
    654     {   "nativeRowBytes",           "(I)I", (void*)Bitmap_rowBytes },
    655     {   "nativeConfig",             "(I)I", (void*)Bitmap_config },
    656     {   "nativeHasAlpha",           "(I)Z", (void*)Bitmap_hasAlpha },
    657     {   "nativeSetHasAlpha",        "(IZ)V", (void*)Bitmap_setHasAlpha },
    658     {   "nativeCreateFromParcel",
    659         "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
    660         (void*)Bitmap_createFromParcel },
    661     {   "nativeWriteToParcel",      "(IZILandroid/os/Parcel;)Z",
    662         (void*)Bitmap_writeToParcel },
    663     {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",
    664         (void*)Bitmap_extractAlpha },
    665     {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },
    666     {   "nativeGetPixel",           "(III)I", (void*)Bitmap_getPixel },
    667     {   "nativeGetPixels",          "(I[IIIIIII)V", (void*)Bitmap_getPixels },
    668     {   "nativeSetPixel",           "(IIII)V", (void*)Bitmap_setPixel },
    669     {   "nativeSetPixels",          "(I[IIIIIII)V", (void*)Bitmap_setPixels },
    670     {   "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
    671                                             (void*)Bitmap_copyPixelsToBuffer },
    672     {   "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
    673                                             (void*)Bitmap_copyPixelsFromBuffer },
    674     {   "nativeSameAs",             "(II)Z", (void*)Bitmap_sameAs },
    675     {   "nativePrepareToDraw",      "(I)V", (void*)Bitmap_prepareToDraw },
    676 };
    677 
    678 #define kClassPathName  "android/graphics/Bitmap"
    679 
    680 int register_android_graphics_Bitmap(JNIEnv* env)
    681 {
    682     return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
    683                                 gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
    684 }
    685