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