Bitmap.cpp revision 17a8bfc38a565ae96f43d36b223779be840bb50c
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, SkBitmapConfigToColorType(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        jboolean requestPremul) {
366    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
367    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
368    SkColorType colorType = SkBitmapConfigToColorType(config);
369
370    // ARGB_4444 is a deprecated format, convert automatically to 8888
371    if (colorType == kARGB_4444_SkColorType) {
372        colorType = kN32_SkColorType;
373    }
374
375    if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) {
376        // done in native as there's no way to get BytesPerPixel in Java
377        doThrowIAE(env, "Bitmap not large enough to support new configuration");
378        return;
379    }
380    SkPixelRef* ref = bitmap->pixelRef();
381    ref->ref();
382    SkAlphaType alphaType;
383    if (bitmap->colorType() != kRGB_565_SkColorType
384            && bitmap->alphaType() == kOpaque_SkAlphaType) {
385        // If the original bitmap was set to opaque, keep that setting, unless it
386        // was 565, which is required to be opaque.
387        alphaType = kOpaque_SkAlphaType;
388    } else {
389        // Otherwise respect the premultiplied request.
390        alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
391    }
392    bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType));
393    // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for
394    // its alphatype), so it would make more sense from Skia's perspective to create a
395    // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key
396    // for its cache, so it won't realize this is the same Java Bitmap.
397    SkImageInfo& info = const_cast<SkImageInfo&>(ref->info());
398    // Use the updated from the SkBitmap, which may have corrected an invalid alphatype.
399    // (e.g. 565 non-opaque)
400    info = bitmap->info();
401    bitmap->setPixelRef(ref);
402
403    // notifyPixelsChanged will increment the generation ID even though the actual pixel data
404    // hasn't been touched. This signals the renderer that the bitmap (including width, height,
405    // colortype and alphatype) has changed.
406    ref->notifyPixelsChanged();
407    ref->unref();
408}
409
410// These must match the int values in Bitmap.java
411enum JavaEncodeFormat {
412    kJPEG_JavaEncodeFormat = 0,
413    kPNG_JavaEncodeFormat = 1,
414    kWEBP_JavaEncodeFormat = 2
415};
416
417static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
418                                jint format, jint quality,
419                                jobject jstream, jbyteArray jstorage) {
420    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
421    SkImageEncoder::Type fm;
422
423    switch (format) {
424    case kJPEG_JavaEncodeFormat:
425        fm = SkImageEncoder::kJPEG_Type;
426        break;
427    case kPNG_JavaEncodeFormat:
428        fm = SkImageEncoder::kPNG_Type;
429        break;
430    case kWEBP_JavaEncodeFormat:
431        fm = SkImageEncoder::kWEBP_Type;
432        break;
433    default:
434        return JNI_FALSE;
435    }
436
437    bool success = false;
438    if (NULL != bitmap) {
439        SkAutoLockPixels alp(*bitmap);
440
441        if (NULL == bitmap->getPixels()) {
442            return JNI_FALSE;
443        }
444
445        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
446        if (NULL == strm) {
447            return JNI_FALSE;
448        }
449
450        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
451        if (NULL != encoder) {
452            success = encoder->encodeStream(strm, *bitmap, quality);
453            delete encoder;
454        }
455        delete strm;
456    }
457    return success ? JNI_TRUE : JNI_FALSE;
458}
459
460static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
461    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
462    bitmap->eraseColor(color);
463}
464
465static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
466    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
467    return static_cast<jint>(bitmap->rowBytes());
468}
469
470static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
471    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
472    return static_cast<jint>(bitmap->config());
473}
474
475static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
476    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
477    return static_cast<jint>(bitmap->getGenerationID());
478}
479
480static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
481    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
482    return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
483}
484
485static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
486                                            jboolean hasAlpha, jboolean isPremul) {
487    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
488    if (!hasAlpha) {
489        bitmap->setAlphaType(kOpaque_SkAlphaType);
490    } else if (isPremul) {
491        bitmap->setAlphaType(kPremul_SkAlphaType);
492    } else {
493        bitmap->setAlphaType(kUnpremul_SkAlphaType);
494    }
495}
496
497static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
498    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
499    return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
500}
501
502static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
503                                jboolean hasMipMap) {
504    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
505    bitmap->setHasHardwareMipMap(hasMipMap);
506}
507
508///////////////////////////////////////////////////////////////////////////////
509
510static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
511    if (parcel == NULL) {
512        SkDebugf("-------- unparcel parcel is NULL\n");
513        return NULL;
514    }
515
516    android::Parcel* p = android::parcelForJavaObject(env, parcel);
517
518    const bool              isMutable = p->readInt32() != 0;
519    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
520    const int               width = p->readInt32();
521    const int               height = p->readInt32();
522    const int               rowBytes = p->readInt32();
523    const int               density = p->readInt32();
524
525    if (SkBitmap::kARGB_8888_Config != config &&
526            SkBitmap::kRGB_565_Config != config &&
527            SkBitmap::kARGB_4444_Config != config &&
528            SkBitmap::kIndex8_Config != config &&
529            SkBitmap::kA8_Config != config) {
530        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
531        return NULL;
532    }
533
534    SkBitmap* bitmap = new SkBitmap;
535
536    bitmap->setConfig(config, width, height, rowBytes);
537
538    SkColorTable* ctable = NULL;
539    if (config == SkBitmap::kIndex8_Config) {
540        int count = p->readInt32();
541        if (count > 0) {
542            size_t size = count * sizeof(SkPMColor);
543            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
544            ctable = new SkColorTable(src, count);
545        }
546    }
547
548    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
549    if (NULL == buffer) {
550        SkSafeUnref(ctable);
551        delete bitmap;
552        return NULL;
553    }
554
555    SkSafeUnref(ctable);
556
557    size_t size = bitmap->getSize();
558
559    android::Parcel::ReadableBlob blob;
560    android::status_t status = p->readBlob(size, &blob);
561    if (status) {
562        doThrowRE(env, "Could not read bitmap from parcel blob.");
563        delete bitmap;
564        return NULL;
565    }
566
567    bitmap->lockPixels();
568    memcpy(bitmap->getPixels(), blob.data(), size);
569    bitmap->unlockPixels();
570
571    blob.release();
572
573    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
574            NULL, NULL, density);
575}
576
577static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
578                                     jlong bitmapHandle,
579                                     jboolean isMutable, jint density,
580                                     jobject parcel) {
581    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
582    if (parcel == NULL) {
583        SkDebugf("------- writeToParcel null parcel\n");
584        return JNI_FALSE;
585    }
586
587    android::Parcel* p = android::parcelForJavaObject(env, parcel);
588
589    p->writeInt32(isMutable);
590    p->writeInt32(bitmap->config());
591    p->writeInt32(bitmap->width());
592    p->writeInt32(bitmap->height());
593    p->writeInt32(bitmap->rowBytes());
594    p->writeInt32(density);
595
596    if (bitmap->config() == SkBitmap::kIndex8_Config) {
597        SkColorTable* ctable = bitmap->getColorTable();
598        if (ctable != NULL) {
599            int count = ctable->count();
600            p->writeInt32(count);
601            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
602                   ctable->lockColors(), count * sizeof(SkPMColor));
603            ctable->unlockColors();
604        } else {
605            p->writeInt32(0);   // indicate no ctable
606        }
607    }
608
609    size_t size = bitmap->getSize();
610
611    android::Parcel::WritableBlob blob;
612    android::status_t status = p->writeBlob(size, &blob);
613    if (status) {
614        doThrowRE(env, "Could not write bitmap to parcel blob.");
615        return JNI_FALSE;
616    }
617
618    bitmap->lockPixels();
619    const void* pSrc =  bitmap->getPixels();
620    if (pSrc == NULL) {
621        memset(blob.data(), 0, size);
622    } else {
623        memcpy(blob.data(), pSrc, size);
624    }
625    bitmap->unlockPixels();
626
627    blob.release();
628    return JNI_TRUE;
629}
630
631static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
632                                   jlong srcHandle, jlong paintHandle,
633                                   jintArray offsetXY) {
634    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
635    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
636    SkIPoint  offset;
637    SkBitmap* dst = new SkBitmap;
638    JavaPixelAllocator allocator(env);
639
640    src->extractAlpha(dst, paint, &allocator, &offset);
641    // If Skia can't allocate pixels for destination bitmap, it resets
642    // it, that is set its pixels buffer to NULL, and zero width and height.
643    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
644        delete dst;
645        doThrowOOME(env, "failed to allocate pixels for alpha");
646        return NULL;
647    }
648    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
649        int* array = env->GetIntArrayElements(offsetXY, NULL);
650        array[0] = offset.fX;
651        array[1] = offset.fY;
652        env->ReleaseIntArrayElements(offsetXY, array, 0);
653    }
654
655    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
656            getPremulBitmapCreateFlags(true), NULL, NULL);
657}
658
659///////////////////////////////////////////////////////////////////////////////
660
661static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
662        jint x, jint y, jboolean isPremultiplied) {
663    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
664    SkAutoLockPixels alp(*bitmap);
665
666    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
667    if (NULL == proc) {
668        return 0;
669    }
670    const void* src = bitmap->getAddr(x, y);
671    if (NULL == src) {
672        return 0;
673    }
674
675    SkColor dst[1];
676    proc(dst, src, 1, bitmap->getColorTable());
677    return static_cast<jint>(dst[0]);
678}
679
680static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
681        jintArray pixelArray, jint offset, jint stride,
682        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
683    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
684    SkAutoLockPixels alp(*bitmap);
685
686    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
687    if (NULL == proc) {
688        return;
689    }
690    const void* src = bitmap->getAddr(x, y);
691    if (NULL == src) {
692        return;
693    }
694
695    SkColorTable* ctable = bitmap->getColorTable();
696    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
697    SkColor* d = (SkColor*)dst + offset;
698    while (--height >= 0) {
699        proc(d, src, width, ctable);
700        d += stride;
701        src = (void*)((const char*)src + bitmap->rowBytes());
702    }
703    env->ReleaseIntArrayElements(pixelArray, dst, 0);
704}
705
706///////////////////////////////////////////////////////////////////////////////
707
708static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
709        jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
710    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
711    SkColor color = static_cast<SkColor>(colorHandle);
712    SkAutoLockPixels alp(*bitmap);
713    if (NULL == bitmap->getPixels()) {
714        return;
715    }
716
717    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
718    if (NULL == proc) {
719        return;
720    }
721
722    proc(bitmap->getAddr(x, y), &color, 1, x, y);
723    bitmap->notifyPixelsChanged();
724}
725
726static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
727        jintArray pixelArray, jint offset, jint stride,
728        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
729    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
730    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
731            x, y, width, height, *bitmap, isPremultiplied);
732}
733
734static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
735                                      jlong bitmapHandle, jobject jbuffer) {
736    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
737    SkAutoLockPixels alp(*bitmap);
738    const void* src = bitmap->getPixels();
739
740    if (NULL != src) {
741        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
742
743        // the java side has already checked that buffer is large enough
744        memcpy(abp.pointer(), src, bitmap->getSize());
745    }
746}
747
748static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
749                                        jlong bitmapHandle, jobject jbuffer) {
750    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
751    SkAutoLockPixels alp(*bitmap);
752    void* dst = bitmap->getPixels();
753
754    if (NULL != dst) {
755        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
756        // the java side has already checked that buffer is large enough
757        memcpy(dst, abp.pointer(), bitmap->getSize());
758        bitmap->notifyPixelsChanged();
759    }
760}
761
762static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
763                              jlong bm1Handle) {
764    const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
765    const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
766    if (bm0->width() != bm1->width() ||
767        bm0->height() != bm1->height() ||
768        bm0->config() != bm1->config()) {
769        return JNI_FALSE;
770    }
771
772    SkAutoLockPixels alp0(*bm0);
773    SkAutoLockPixels alp1(*bm1);
774
775    // if we can't load the pixels, return false
776    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
777        return JNI_FALSE;
778    }
779
780    if (bm0->config() == SkBitmap::kIndex8_Config) {
781        SkColorTable* ct0 = bm0->getColorTable();
782        SkColorTable* ct1 = bm1->getColorTable();
783        if (NULL == ct0 || NULL == ct1) {
784            return JNI_FALSE;
785        }
786        if (ct0->count() != ct1->count()) {
787            return JNI_FALSE;
788        }
789
790        SkAutoLockColors alc0(ct0);
791        SkAutoLockColors alc1(ct1);
792        const size_t size = ct0->count() * sizeof(SkPMColor);
793        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
794            return JNI_FALSE;
795        }
796    }
797
798    // now compare each scanline. We can't do the entire buffer at once,
799    // since we don't care about the pixel values that might extend beyond
800    // the width (since the scanline might be larger than the logical width)
801    const int h = bm0->height();
802    const size_t size = bm0->width() * bm0->bytesPerPixel();
803    for (int y = 0; y < h; y++) {
804        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
805            return JNI_FALSE;
806        }
807    }
808    return JNI_TRUE;
809}
810
811static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
812    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
813    bitmap->lockPixels();
814    bitmap->unlockPixels();
815}
816
817///////////////////////////////////////////////////////////////////////////////
818
819#include <android_runtime/AndroidRuntime.h>
820
821static JNINativeMethod gBitmapMethods[] = {
822    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
823        (void*)Bitmap_creator },
824    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
825        (void*)Bitmap_copy },
826    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },
827    {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
828    {   "nativeReconfigure",        "(JIIIIZ)V", (void*)Bitmap_reconfigure },
829    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
830        (void*)Bitmap_compress },
831    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
832    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
833    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
834    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
835    {   "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
836    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
837    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
838    {   "nativeCreateFromParcel",
839        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
840        (void*)Bitmap_createFromParcel },
841    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",
842        (void*)Bitmap_writeToParcel },
843    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
844        (void*)Bitmap_extractAlpha },
845    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
846    {   "nativeGetPixel",           "(JIIZ)I", (void*)Bitmap_getPixel },
847    {   "nativeGetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
848    {   "nativeSetPixel",           "(JIIIZ)V", (void*)Bitmap_setPixel },
849    {   "nativeSetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
850    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
851                                            (void*)Bitmap_copyPixelsToBuffer },
852    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
853                                            (void*)Bitmap_copyPixelsFromBuffer },
854    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
855    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
856};
857
858#define kClassPathName  "android/graphics/Bitmap"
859
860int register_android_graphics_Bitmap(JNIEnv* env)
861{
862    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
863                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
864}
865