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