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