Bitmap.cpp revision 18b4cbeedef21c1fa666a110a157bab66edff976
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
30typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
31                              int x, int y);
32
33static 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
42static 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
59static 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
71static 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
83static 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
100static 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
114bool 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
146typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
147                            SkColorTable*);
148
149static 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
158static 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
169static 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
180static 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
189static 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
200static 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
211static 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
222static 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
233static 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
246static 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
260static 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
285static int getPremulBitmapCreateFlags(bool isMutable) {
286    int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
287    if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
288    return flags;
289}
290
291static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
292                              jint offset, jint stride, jint width, jint height,
293                              jint configHandle, jboolean isMutable) {
294    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
295    if (NULL != jColors) {
296        size_t n = env->GetArrayLength(jColors);
297        if (n < SkAbs32(stride) * (size_t)height) {
298            doThrowAIOOBE(env);
299            return NULL;
300        }
301    }
302
303    // ARGB_4444 is a deprecated format, convert automatically to 8888
304    if (config == SkBitmap::kARGB_4444_Config) {
305        config = SkBitmap::kARGB_8888_Config;
306    }
307
308    SkBitmap bitmap;
309    bitmap.setConfig(config, width, height);
310
311    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
312    if (NULL == buff) {
313        return NULL;
314    }
315
316    if (jColors != NULL) {
317        GraphicsJNI::SetPixels(env, jColors, offset, stride,
318                0, 0, width, height, bitmap, true);
319    }
320
321    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
322            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
323}
324
325static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
326                           jint dstConfigHandle, jboolean isMutable) {
327    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
328    SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
329    SkBitmap            result;
330    JavaPixelAllocator  allocator(env);
331
332    if (!src->copyTo(&result, dstConfig, &allocator)) {
333        return NULL;
334    }
335    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
336            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
337}
338
339static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
340    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
341#ifdef USE_OPENGL_RENDERER
342    if (android::uirenderer::Caches::hasInstance()) {
343        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
344        return;
345    }
346#endif // USE_OPENGL_RENDERER
347    delete bitmap;
348}
349
350static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
351    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
352#ifdef USE_OPENGL_RENDERER
353    if (android::uirenderer::Caches::hasInstance()) {
354        bool result;
355        result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
356        return result ? JNI_TRUE : JNI_FALSE;
357    }
358#endif // USE_OPENGL_RENDERER
359    bitmap->setPixels(NULL, NULL);
360    return JNI_TRUE;
361}
362
363static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
364        jint width, jint height, jint configHandle, jint allocSize) {
365    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
366    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
367    if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {
368        // done in native as there's no way to get BytesPerPixel in Java
369        doThrowIAE(env, "Bitmap not large enough to support new configuration");
370        return;
371    }
372    SkPixelRef* ref = bitmap->pixelRef();
373    SkSafeRef(ref);
374    bitmap->setConfig(config, width, height);
375    bitmap->setPixelRef(ref);
376
377    // notifyPixelsChanged will increment the generation ID even though the actual pixel data
378    // hasn't been touched. This signals the renderer that the bitmap (including width, height,
379    // and config) has changed.
380    ref->notifyPixelsChanged();
381    SkSafeUnref(ref);
382}
383
384// These must match the int values in Bitmap.java
385enum JavaEncodeFormat {
386    kJPEG_JavaEncodeFormat = 0,
387    kPNG_JavaEncodeFormat = 1,
388    kWEBP_JavaEncodeFormat = 2
389};
390
391static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
392                                jint format, jint quality,
393                                jobject jstream, jbyteArray jstorage) {
394    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
395    SkImageEncoder::Type fm;
396
397    switch (format) {
398    case kJPEG_JavaEncodeFormat:
399        fm = SkImageEncoder::kJPEG_Type;
400        break;
401    case kPNG_JavaEncodeFormat:
402        fm = SkImageEncoder::kPNG_Type;
403        break;
404    case kWEBP_JavaEncodeFormat:
405        fm = SkImageEncoder::kWEBP_Type;
406        break;
407    default:
408        return JNI_FALSE;
409    }
410
411    bool success = false;
412    if (NULL != bitmap) {
413        SkAutoLockPixels alp(*bitmap);
414
415        if (NULL == bitmap->getPixels()) {
416            return JNI_FALSE;
417        }
418
419        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
420        if (NULL == strm) {
421            return JNI_FALSE;
422        }
423
424        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
425        if (NULL != encoder) {
426            success = encoder->encodeStream(strm, *bitmap, quality);
427            delete encoder;
428        }
429        delete strm;
430    }
431    return success ? JNI_TRUE : JNI_FALSE;
432}
433
434static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
435    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
436    bitmap->eraseColor(color);
437}
438
439static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
440    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
441    return static_cast<jint>(bitmap->rowBytes());
442}
443
444static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
445    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
446    return static_cast<jint>(bitmap->config());
447}
448
449static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
450    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
451    return static_cast<jint>(bitmap->getGenerationID());
452}
453
454static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
455    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
456    return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
457}
458
459static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
460                                            jboolean hasAlpha, jboolean isPremul) {
461    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
462    if (!hasAlpha) {
463        bitmap->setAlphaType(kOpaque_SkAlphaType);
464    } else if (isPremul) {
465        bitmap->setAlphaType(kPremul_SkAlphaType);
466    } else {
467        bitmap->setAlphaType(kUnpremul_SkAlphaType);
468    }
469    bitmap->setIsOpaque(!hasAlpha);
470}
471
472static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
473    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
474    return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
475}
476
477static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
478                                jboolean hasMipMap) {
479    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
480    bitmap->setHasHardwareMipMap(hasMipMap);
481}
482
483///////////////////////////////////////////////////////////////////////////////
484
485static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
486    if (parcel == NULL) {
487        SkDebugf("-------- unparcel parcel is NULL\n");
488        return NULL;
489    }
490
491    android::Parcel* p = android::parcelForJavaObject(env, parcel);
492
493    const bool              isMutable = p->readInt32() != 0;
494    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
495    const int               width = p->readInt32();
496    const int               height = p->readInt32();
497    const int               rowBytes = p->readInt32();
498    const int               density = p->readInt32();
499
500    if (SkBitmap::kARGB_8888_Config != config &&
501            SkBitmap::kRGB_565_Config != config &&
502            SkBitmap::kARGB_4444_Config != config &&
503            SkBitmap::kIndex8_Config != config &&
504            SkBitmap::kA8_Config != config) {
505        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
506        return NULL;
507    }
508
509    SkBitmap* bitmap = new SkBitmap;
510
511    bitmap->setConfig(config, width, height, rowBytes);
512
513    SkColorTable* ctable = NULL;
514    if (config == SkBitmap::kIndex8_Config) {
515        int count = p->readInt32();
516        if (count > 0) {
517            size_t size = count * sizeof(SkPMColor);
518            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
519            ctable = new SkColorTable(src, count);
520        }
521    }
522
523    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
524    if (NULL == buffer) {
525        SkSafeUnref(ctable);
526        delete bitmap;
527        return NULL;
528    }
529
530    SkSafeUnref(ctable);
531
532    size_t size = bitmap->getSize();
533
534    android::Parcel::ReadableBlob blob;
535    android::status_t status = p->readBlob(size, &blob);
536    if (status) {
537        doThrowRE(env, "Could not read bitmap from parcel blob.");
538        delete bitmap;
539        return NULL;
540    }
541
542    bitmap->lockPixels();
543    memcpy(bitmap->getPixels(), blob.data(), size);
544    bitmap->unlockPixels();
545
546    blob.release();
547
548    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
549            NULL, NULL, density);
550}
551
552static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
553                                     jlong bitmapHandle,
554                                     jboolean isMutable, jint density,
555                                     jobject parcel) {
556    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
557    if (parcel == NULL) {
558        SkDebugf("------- writeToParcel null parcel\n");
559        return JNI_FALSE;
560    }
561
562    android::Parcel* p = android::parcelForJavaObject(env, parcel);
563
564    p->writeInt32(isMutable);
565    p->writeInt32(bitmap->config());
566    p->writeInt32(bitmap->width());
567    p->writeInt32(bitmap->height());
568    p->writeInt32(bitmap->rowBytes());
569    p->writeInt32(density);
570
571    if (bitmap->config() == SkBitmap::kIndex8_Config) {
572        SkColorTable* ctable = bitmap->getColorTable();
573        if (ctable != NULL) {
574            int count = ctable->count();
575            p->writeInt32(count);
576            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
577                   ctable->lockColors(), count * sizeof(SkPMColor));
578            ctable->unlockColors();
579        } else {
580            p->writeInt32(0);   // indicate no ctable
581        }
582    }
583
584    size_t size = bitmap->getSize();
585
586    android::Parcel::WritableBlob blob;
587    android::status_t status = p->writeBlob(size, &blob);
588    if (status) {
589        doThrowRE(env, "Could not write bitmap to parcel blob.");
590        return JNI_FALSE;
591    }
592
593    bitmap->lockPixels();
594    const void* pSrc =  bitmap->getPixels();
595    if (pSrc == NULL) {
596        memset(blob.data(), 0, size);
597    } else {
598        memcpy(blob.data(), pSrc, size);
599    }
600    bitmap->unlockPixels();
601
602    blob.release();
603    return JNI_TRUE;
604}
605
606static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
607                                   jlong srcHandle, jlong paintHandle,
608                                   jintArray offsetXY) {
609    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
610    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
611    SkIPoint  offset;
612    SkBitmap* dst = new SkBitmap;
613    JavaPixelAllocator allocator(env);
614
615    src->extractAlpha(dst, paint, &allocator, &offset);
616    // If Skia can't allocate pixels for destination bitmap, it resets
617    // it, that is set its pixels buffer to NULL, and zero width and height.
618    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
619        delete dst;
620        doThrowOOME(env, "failed to allocate pixels for alpha");
621        return NULL;
622    }
623    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
624        int* array = env->GetIntArrayElements(offsetXY, NULL);
625        array[0] = offset.fX;
626        array[1] = offset.fY;
627        env->ReleaseIntArrayElements(offsetXY, array, 0);
628    }
629
630    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
631            GraphicsJNI::kBitmapCreateFlag_Mutable, NULL, NULL);
632}
633
634///////////////////////////////////////////////////////////////////////////////
635
636static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
637        jint x, jint y, jboolean isPremultiplied) {
638    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
639    SkAutoLockPixels alp(*bitmap);
640
641    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
642    if (NULL == proc) {
643        return 0;
644    }
645    const void* src = bitmap->getAddr(x, y);
646    if (NULL == src) {
647        return 0;
648    }
649
650    SkColor dst[1];
651    proc(dst, src, 1, bitmap->getColorTable());
652    return static_cast<jint>(dst[0]);
653}
654
655static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
656        jintArray pixelArray, jint offset, jint stride,
657        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
658    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
659    SkAutoLockPixels alp(*bitmap);
660
661    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
662    if (NULL == proc) {
663        return;
664    }
665    const void* src = bitmap->getAddr(x, y);
666    if (NULL == src) {
667        return;
668    }
669
670    SkColorTable* ctable = bitmap->getColorTable();
671    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
672    SkColor* d = (SkColor*)dst + offset;
673    while (--height >= 0) {
674        proc(d, src, width, ctable);
675        d += stride;
676        src = (void*)((const char*)src + bitmap->rowBytes());
677    }
678    env->ReleaseIntArrayElements(pixelArray, dst, 0);
679}
680
681///////////////////////////////////////////////////////////////////////////////
682
683static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
684        jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
685    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
686    SkColor color = static_cast<SkColor>(colorHandle);
687    SkAutoLockPixels alp(*bitmap);
688    if (NULL == bitmap->getPixels()) {
689        return;
690    }
691
692    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
693    if (NULL == proc) {
694        return;
695    }
696
697    proc(bitmap->getAddr(x, y), &color, 1, x, y);
698    bitmap->notifyPixelsChanged();
699}
700
701static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
702        jintArray pixelArray, jint offset, jint stride,
703        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
704    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
705    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
706            x, y, width, height, *bitmap, isPremultiplied);
707}
708
709static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
710                                      jlong bitmapHandle, jobject jbuffer) {
711    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
712    SkAutoLockPixels alp(*bitmap);
713    const void* src = bitmap->getPixels();
714
715    if (NULL != src) {
716        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
717
718        // the java side has already checked that buffer is large enough
719        memcpy(abp.pointer(), src, bitmap->getSize());
720    }
721}
722
723static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
724                                        jlong bitmapHandle, jobject jbuffer) {
725    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
726    SkAutoLockPixels alp(*bitmap);
727    void* dst = bitmap->getPixels();
728
729    if (NULL != dst) {
730        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
731        // the java side has already checked that buffer is large enough
732        memcpy(dst, abp.pointer(), bitmap->getSize());
733        bitmap->notifyPixelsChanged();
734    }
735}
736
737static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
738                              jlong bm1Handle) {
739    const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
740    const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
741    if (bm0->width() != bm1->width() ||
742        bm0->height() != bm1->height() ||
743        bm0->config() != bm1->config()) {
744        return JNI_FALSE;
745    }
746
747    SkAutoLockPixels alp0(*bm0);
748    SkAutoLockPixels alp1(*bm1);
749
750    // if we can't load the pixels, return false
751    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
752        return JNI_FALSE;
753    }
754
755    if (bm0->config() == SkBitmap::kIndex8_Config) {
756        SkColorTable* ct0 = bm0->getColorTable();
757        SkColorTable* ct1 = bm1->getColorTable();
758        if (NULL == ct0 || NULL == ct1) {
759            return JNI_FALSE;
760        }
761        if (ct0->count() != ct1->count()) {
762            return JNI_FALSE;
763        }
764
765        SkAutoLockColors alc0(ct0);
766        SkAutoLockColors alc1(ct1);
767        const size_t size = ct0->count() * sizeof(SkPMColor);
768        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
769            return JNI_FALSE;
770        }
771    }
772
773    // now compare each scanline. We can't do the entire buffer at once,
774    // since we don't care about the pixel values that might extend beyond
775    // the width (since the scanline might be larger than the logical width)
776    const int h = bm0->height();
777    const size_t size = bm0->width() * bm0->bytesPerPixel();
778    for (int y = 0; y < h; y++) {
779        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
780            return JNI_FALSE;
781        }
782    }
783    return JNI_TRUE;
784}
785
786static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
787    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
788    bitmap->lockPixels();
789    bitmap->unlockPixels();
790}
791
792///////////////////////////////////////////////////////////////////////////////
793
794#include <android_runtime/AndroidRuntime.h>
795
796static JNINativeMethod gBitmapMethods[] = {
797    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
798        (void*)Bitmap_creator },
799    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
800        (void*)Bitmap_copy },
801    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },
802    {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
803    {   "nativeReconfigure",        "(JIIII)V", (void*)Bitmap_reconfigure },
804    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
805        (void*)Bitmap_compress },
806    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
807    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
808    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
809    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
810    {   "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
811    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
812    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
813    {   "nativeCreateFromParcel",
814        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
815        (void*)Bitmap_createFromParcel },
816    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",
817        (void*)Bitmap_writeToParcel },
818    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
819        (void*)Bitmap_extractAlpha },
820    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
821    {   "nativeGetPixel",           "(JIIZ)I", (void*)Bitmap_getPixel },
822    {   "nativeGetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
823    {   "nativeSetPixel",           "(JIIIZ)V", (void*)Bitmap_setPixel },
824    {   "nativeSetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
825    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
826                                            (void*)Bitmap_copyPixelsToBuffer },
827    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
828                                            (void*)Bitmap_copyPixelsFromBuffer },
829    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
830    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
831};
832
833#define kClassPathName  "android/graphics/Bitmap"
834
835int register_android_graphics_Bitmap(JNIEnv* env)
836{
837    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
838                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
839}
840