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