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