Bitmap.cpp revision 3ed72781a0b155a88e52c8d295f50daab8cfb9ae
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
29typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
30                              int x, int y);
31
32static 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
41static 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
53static 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
66static 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
80bool 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
113typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
114                            SkColorTable*);
115
116static 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
125static 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
136static 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
145static 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
156static 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
167static 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
178static 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
192static 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
214static 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    // ARGB_4444 is a deprecated format, convert automatically to 8888
226    if (config == SkBitmap::kARGB_4444_Config) {
227        config = SkBitmap::kARGB_8888_Config;
228    }
229
230    SkBitmap bitmap;
231    bitmap.setConfig(config, width, height);
232
233    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
234    if (NULL == buff) {
235        return NULL;
236    }
237
238    if (jColors != NULL) {
239        GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap);
240    }
241
242    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL, NULL);
243}
244
245static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
246                           SkBitmap::Config dstConfig, jboolean isMutable) {
247    SkBitmap            result;
248    JavaPixelAllocator  allocator(env);
249
250    if (!src->copyTo(&result, dstConfig, &allocator)) {
251        return NULL;
252    }
253
254    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL, NULL);
255}
256
257static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
258#ifdef USE_OPENGL_RENDERER
259    if (android::uirenderer::Caches::hasInstance()) {
260        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
261        return;
262    }
263#endif // USE_OPENGL_RENDERER
264    delete bitmap;
265}
266
267static jboolean Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {
268#ifdef USE_OPENGL_RENDERER
269    if (android::uirenderer::Caches::hasInstance()) {
270        return android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
271    }
272#endif // USE_OPENGL_RENDERER
273    bitmap->setPixels(NULL, NULL);
274    return true;
275}
276
277static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jint bitmapInt,
278        int width, int height, SkBitmap::Config config, int allocSize) {
279    if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {
280        // done in native as there's no way to get BytesPerPixel in Java
281        doThrowIAE(env, "Bitmap not large enough to support new configuration");
282        return;
283    }
284    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapInt);
285    SkPixelRef* ref = bitmap->pixelRef();
286    SkSafeRef(ref);
287    bitmap->setConfig(config, width, height);
288    bitmap->setPixelRef(ref);
289
290    // notifyPixelsChanged will increment the generation ID even though the actual pixel data
291    // hasn't been touched. This signals the renderer that the bitmap (including width, height,
292    // and config) has changed.
293    ref->notifyPixelsChanged();
294    SkSafeUnref(ref);
295}
296
297// These must match the int values in Bitmap.java
298enum JavaEncodeFormat {
299    kJPEG_JavaEncodeFormat = 0,
300    kPNG_JavaEncodeFormat = 1,
301    kWEBP_JavaEncodeFormat = 2
302};
303
304static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap,
305                            int format, int quality,
306                            jobject jstream, jbyteArray jstorage) {
307    SkImageEncoder::Type fm;
308
309    switch (format) {
310    case kJPEG_JavaEncodeFormat:
311        fm = SkImageEncoder::kJPEG_Type;
312        break;
313    case kPNG_JavaEncodeFormat:
314        fm = SkImageEncoder::kPNG_Type;
315        break;
316    case kWEBP_JavaEncodeFormat:
317        fm = SkImageEncoder::kWEBP_Type;
318        break;
319    default:
320        return false;
321    }
322
323    bool success = false;
324    if (NULL != bitmap) {
325        SkAutoLockPixels alp(*bitmap);
326
327        if (NULL == bitmap->getPixels()) {
328            return false;
329        }
330
331        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
332        if (NULL == strm) {
333            return false;
334        }
335
336        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
337        if (NULL != encoder) {
338            success = encoder->encodeStream(strm, *bitmap, quality);
339            delete encoder;
340        }
341        delete strm;
342    }
343    return success;
344}
345
346static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
347    bitmap->eraseColor(color);
348}
349
350static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) {
351    return bitmap->width();
352}
353
354static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) {
355    return bitmap->height();
356}
357
358static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
359    return bitmap->rowBytes();
360}
361
362static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
363    return bitmap->config();
364}
365
366static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {
367    return bitmap->getGenerationID();
368}
369
370static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
371    return !bitmap->isOpaque();
372}
373
374static void Bitmap_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap,
375                               jboolean hasAlpha) {
376    bitmap->setIsOpaque(!hasAlpha);
377}
378
379static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap) {
380    return bitmap->hasHardwareMipMap();
381}
382
383static void Bitmap_setHasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap,
384                                jboolean hasMipMap) {
385    bitmap->setHasHardwareMipMap(hasMipMap);
386}
387
388///////////////////////////////////////////////////////////////////////////////
389
390static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
391    if (parcel == NULL) {
392        SkDebugf("-------- unparcel parcel is NULL\n");
393        return NULL;
394    }
395
396    android::Parcel* p = android::parcelForJavaObject(env, parcel);
397
398    const bool              isMutable = p->readInt32() != 0;
399    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
400    const int               width = p->readInt32();
401    const int               height = p->readInt32();
402    const int               rowBytes = p->readInt32();
403    const int               density = p->readInt32();
404
405    if (SkBitmap::kARGB_8888_Config != config &&
406            SkBitmap::kRGB_565_Config != config &&
407            SkBitmap::kARGB_4444_Config != config &&
408            SkBitmap::kIndex8_Config != config &&
409            SkBitmap::kA8_Config != config) {
410        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
411        return NULL;
412    }
413
414    SkBitmap* bitmap = new SkBitmap;
415
416    bitmap->setConfig(config, width, height, rowBytes);
417
418    SkColorTable* ctable = NULL;
419    if (config == SkBitmap::kIndex8_Config) {
420        int count = p->readInt32();
421        if (count > 0) {
422            size_t size = count * sizeof(SkPMColor);
423            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
424            ctable = new SkColorTable(src, count);
425        }
426    }
427
428    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
429    if (NULL == buffer) {
430        SkSafeUnref(ctable);
431        delete bitmap;
432        return NULL;
433    }
434
435    SkSafeUnref(ctable);
436
437    size_t size = bitmap->getSize();
438
439    android::Parcel::ReadableBlob blob;
440    android::status_t status = p->readBlob(size, &blob);
441    if (status) {
442        doThrowRE(env, "Could not read bitmap from parcel blob.");
443        delete bitmap;
444        return NULL;
445    }
446
447    bitmap->lockPixels();
448    memcpy(bitmap->getPixels(), blob.data(), size);
449    bitmap->unlockPixels();
450
451    blob.release();
452    return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, NULL, density);
453}
454
455static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
456                                     const SkBitmap* bitmap,
457                                     jboolean isMutable, jint density,
458                                     jobject parcel) {
459    if (parcel == NULL) {
460        SkDebugf("------- writeToParcel null parcel\n");
461        return false;
462    }
463
464    android::Parcel* p = android::parcelForJavaObject(env, parcel);
465
466    p->writeInt32(isMutable);
467    p->writeInt32(bitmap->config());
468    p->writeInt32(bitmap->width());
469    p->writeInt32(bitmap->height());
470    p->writeInt32(bitmap->rowBytes());
471    p->writeInt32(density);
472
473    if (bitmap->getConfig() == SkBitmap::kIndex8_Config) {
474        SkColorTable* ctable = bitmap->getColorTable();
475        if (ctable != NULL) {
476            int count = ctable->count();
477            p->writeInt32(count);
478            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
479                   ctable->lockColors(), count * sizeof(SkPMColor));
480            ctable->unlockColors(false);
481        } else {
482            p->writeInt32(0);   // indicate no ctable
483        }
484    }
485
486    size_t size = bitmap->getSize();
487
488    android::Parcel::WritableBlob blob;
489    android::status_t status = p->writeBlob(size, &blob);
490    if (status) {
491        doThrowRE(env, "Could not write bitmap to parcel blob.");
492        return false;
493    }
494
495    bitmap->lockPixels();
496    const void* pSrc =  bitmap->getPixels();
497    if (pSrc == NULL) {
498        memset(blob.data(), 0, size);
499    } else {
500        memcpy(blob.data(), pSrc, size);
501    }
502    bitmap->unlockPixels();
503
504    blob.release();
505    return true;
506}
507
508static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
509                                   const SkBitmap* src, const SkPaint* paint,
510                                   jintArray offsetXY) {
511    SkIPoint  offset;
512    SkBitmap* dst = new SkBitmap;
513    JavaPixelAllocator allocator(env);
514
515    src->extractAlpha(dst, paint, &allocator, &offset);
516    // If Skia can't allocate pixels for destination bitmap, it resets
517    // it, that is set its pixels buffer to NULL, and zero width and height.
518    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
519        delete dst;
520        doThrowOOME(env, "failed to allocate pixels for alpha");
521        return NULL;
522    }
523    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
524        int* array = env->GetIntArrayElements(offsetXY, NULL);
525        array[0] = offset.fX;
526        array[1] = offset.fY;
527        env->ReleaseIntArrayElements(offsetXY, array, 0);
528    }
529
530    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL, NULL);
531}
532
533///////////////////////////////////////////////////////////////////////////////
534
535static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
536                           int x, int y) {
537    SkAutoLockPixels alp(*bitmap);
538
539    ToColorProc proc = ChooseToColorProc(*bitmap);
540    if (NULL == proc) {
541        return 0;
542    }
543    const void* src = bitmap->getAddr(x, y);
544    if (NULL == src) {
545        return 0;
546    }
547
548    SkColor dst[1];
549    proc(dst, src, 1, bitmap->getColorTable());
550    return dst[0];
551}
552
553static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
554                             jintArray pixelArray, int offset, int stride,
555                             int x, int y, int width, int height) {
556    SkAutoLockPixels alp(*bitmap);
557
558    ToColorProc proc = ChooseToColorProc(*bitmap);
559    if (NULL == proc) {
560        return;
561    }
562    const void* src = bitmap->getAddr(x, y);
563    if (NULL == src) {
564        return;
565    }
566
567    SkColorTable* ctable = bitmap->getColorTable();
568    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
569    SkColor* d = (SkColor*)dst + offset;
570    while (--height >= 0) {
571        proc(d, src, width, ctable);
572        d += stride;
573        src = (void*)((const char*)src + bitmap->rowBytes());
574    }
575    env->ReleaseIntArrayElements(pixelArray, dst, 0);
576}
577
578///////////////////////////////////////////////////////////////////////////////
579
580static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
581                            int x, int y, SkColor color) {
582    SkAutoLockPixels alp(*bitmap);
583    if (NULL == bitmap->getPixels()) {
584        return;
585    }
586
587    FromColorProc proc = ChooseFromColorProc(bitmap->config());
588    if (NULL == proc) {
589        return;
590    }
591
592    proc(bitmap->getAddr(x, y), &color, 1, x, y);
593    bitmap->notifyPixelsChanged();
594}
595
596static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
597                             jintArray pixelArray, int offset, int stride,
598                             int x, int y, int width, int height) {
599    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
600                           x, y, width, height, *bitmap);
601}
602
603static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
604                                      const SkBitmap* bitmap, jobject jbuffer) {
605    SkAutoLockPixels alp(*bitmap);
606    const void* src = bitmap->getPixels();
607
608    if (NULL != src) {
609        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
610
611        // the java side has already checked that buffer is large enough
612        memcpy(abp.pointer(), src, bitmap->getSize());
613    }
614}
615
616static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
617                                    const SkBitmap* bitmap, jobject jbuffer) {
618    SkAutoLockPixels alp(*bitmap);
619    void* dst = bitmap->getPixels();
620
621    if (NULL != dst) {
622        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
623        // the java side has already checked that buffer is large enough
624        memcpy(dst, abp.pointer(), bitmap->getSize());
625        bitmap->notifyPixelsChanged();
626    }
627}
628
629static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0,
630                             const SkBitmap* bm1) {
631    if (bm0->width() != bm1->width() ||
632        bm0->height() != bm1->height() ||
633        bm0->config() != bm1->config()) {
634        return false;
635    }
636
637    SkAutoLockPixels alp0(*bm0);
638    SkAutoLockPixels alp1(*bm1);
639
640    // if we can't load the pixels, return false
641    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
642        return false;
643    }
644
645    if (bm0->config() == SkBitmap::kIndex8_Config) {
646        SkColorTable* ct0 = bm0->getColorTable();
647        SkColorTable* ct1 = bm1->getColorTable();
648        if (NULL == ct0 || NULL == ct1) {
649            return false;
650        }
651        if (ct0->count() != ct1->count()) {
652            return false;
653        }
654
655        SkAutoLockColors alc0(ct0);
656        SkAutoLockColors alc1(ct1);
657        const size_t size = ct0->count() * sizeof(SkPMColor);
658        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
659            return false;
660        }
661    }
662
663    // now compare each scanline. We can't do the entire buffer at once,
664    // since we don't care about the pixel values that might extend beyond
665    // the width (since the scanline might be larger than the logical width)
666    const int h = bm0->height();
667    const size_t size = bm0->width() * bm0->bytesPerPixel();
668    for (int y = 0; y < h; y++) {
669        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
670            return false;
671        }
672    }
673    return true;
674}
675
676static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
677    bitmap->lockPixels();
678    bitmap->unlockPixels();
679}
680
681///////////////////////////////////////////////////////////////////////////////
682
683#include <android_runtime/AndroidRuntime.h>
684
685static JNINativeMethod gBitmapMethods[] = {
686    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
687        (void*)Bitmap_creator },
688    {   "nativeCopy",               "(IIZ)Landroid/graphics/Bitmap;",
689        (void*)Bitmap_copy },
690    {   "nativeDestructor",         "(I)V", (void*)Bitmap_destructor },
691    {   "nativeRecycle",            "(I)Z", (void*)Bitmap_recycle },
692    {   "nativeReconfigure",        "(IIIII)V", (void*)Bitmap_reconfigure },
693    {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",
694        (void*)Bitmap_compress },
695    {   "nativeErase",              "(II)V", (void*)Bitmap_erase },
696    {   "nativeWidth",              "(I)I", (void*)Bitmap_width },
697    {   "nativeHeight",             "(I)I", (void*)Bitmap_height },
698    {   "nativeRowBytes",           "(I)I", (void*)Bitmap_rowBytes },
699    {   "nativeConfig",             "(I)I", (void*)Bitmap_config },
700    {   "nativeHasAlpha",           "(I)Z", (void*)Bitmap_hasAlpha },
701    {   "nativeSetHasAlpha",        "(IZ)V", (void*)Bitmap_setHasAlpha },
702    {   "nativeHasMipMap",          "(I)Z", (void*)Bitmap_hasMipMap },
703    {   "nativeSetHasMipMap",       "(IZ)V", (void*)Bitmap_setHasMipMap },
704    {   "nativeCreateFromParcel",
705        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
706        (void*)Bitmap_createFromParcel },
707    {   "nativeWriteToParcel",      "(IZILandroid/os/Parcel;)Z",
708        (void*)Bitmap_writeToParcel },
709    {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",
710        (void*)Bitmap_extractAlpha },
711    {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },
712    {   "nativeGetPixel",           "(III)I", (void*)Bitmap_getPixel },
713    {   "nativeGetPixels",          "(I[IIIIIII)V", (void*)Bitmap_getPixels },
714    {   "nativeSetPixel",           "(IIII)V", (void*)Bitmap_setPixel },
715    {   "nativeSetPixels",          "(I[IIIIIII)V", (void*)Bitmap_setPixels },
716    {   "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
717                                            (void*)Bitmap_copyPixelsToBuffer },
718    {   "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
719                                            (void*)Bitmap_copyPixelsFromBuffer },
720    {   "nativeSameAs",             "(II)Z", (void*)Bitmap_sameAs },
721    {   "nativePrepareToDraw",      "(I)V", (void*)Bitmap_prepareToDraw },
722};
723
724#define kClassPathName  "android/graphics/Bitmap"
725
726int register_android_graphics_Bitmap(JNIEnv* env)
727{
728    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
729                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
730}
731