Bitmap.cpp revision 4b9a19a8f865970e0079f431c8c2c2a8e4333ae9
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_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap,
446                               jboolean hasAlpha) {
447    bitmap->setIsOpaque(!hasAlpha);
448}
449
450static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap) {
451    return bitmap->hasHardwareMipMap();
452}
453
454static void Bitmap_setHasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap,
455                                jboolean hasMipMap) {
456    bitmap->setHasHardwareMipMap(hasMipMap);
457}
458
459///////////////////////////////////////////////////////////////////////////////
460
461static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
462    if (parcel == NULL) {
463        SkDebugf("-------- unparcel parcel is NULL\n");
464        return NULL;
465    }
466
467    android::Parcel* p = android::parcelForJavaObject(env, parcel);
468
469    const bool              isMutable = p->readInt32() != 0;
470    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
471    const int               width = p->readInt32();
472    const int               height = p->readInt32();
473    const int               rowBytes = p->readInt32();
474    const int               density = p->readInt32();
475
476    if (SkBitmap::kARGB_8888_Config != config &&
477            SkBitmap::kRGB_565_Config != config &&
478            SkBitmap::kARGB_4444_Config != config &&
479            SkBitmap::kIndex8_Config != config &&
480            SkBitmap::kA8_Config != config) {
481        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
482        return NULL;
483    }
484
485    SkBitmap* bitmap = new SkBitmap;
486
487    bitmap->setConfig(config, width, height, rowBytes);
488
489    SkColorTable* ctable = NULL;
490    if (config == SkBitmap::kIndex8_Config) {
491        int count = p->readInt32();
492        if (count > 0) {
493            size_t size = count * sizeof(SkPMColor);
494            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
495            ctable = new SkColorTable(src, count);
496        }
497    }
498
499    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
500    if (NULL == buffer) {
501        SkSafeUnref(ctable);
502        delete bitmap;
503        return NULL;
504    }
505
506    SkSafeUnref(ctable);
507
508    size_t size = bitmap->getSize();
509
510    android::Parcel::ReadableBlob blob;
511    android::status_t status = p->readBlob(size, &blob);
512    if (status) {
513        doThrowRE(env, "Could not read bitmap from parcel blob.");
514        delete bitmap;
515        return NULL;
516    }
517
518    bitmap->lockPixels();
519    memcpy(bitmap->getPixels(), blob.data(), size);
520    bitmap->unlockPixels();
521
522    blob.release();
523
524    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
525            NULL, NULL, density);
526}
527
528static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
529                                     const SkBitmap* bitmap,
530                                     jboolean isMutable, jint density,
531                                     jobject parcel) {
532    if (parcel == NULL) {
533        SkDebugf("------- writeToParcel null parcel\n");
534        return false;
535    }
536
537    android::Parcel* p = android::parcelForJavaObject(env, parcel);
538
539    p->writeInt32(isMutable);
540    p->writeInt32(bitmap->config());
541    p->writeInt32(bitmap->width());
542    p->writeInt32(bitmap->height());
543    p->writeInt32(bitmap->rowBytes());
544    p->writeInt32(density);
545
546    if (bitmap->config() == SkBitmap::kIndex8_Config) {
547        SkColorTable* ctable = bitmap->getColorTable();
548        if (ctable != NULL) {
549            int count = ctable->count();
550            p->writeInt32(count);
551            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
552                   ctable->lockColors(), count * sizeof(SkPMColor));
553            ctable->unlockColors();
554        } else {
555            p->writeInt32(0);   // indicate no ctable
556        }
557    }
558
559    size_t size = bitmap->getSize();
560
561    android::Parcel::WritableBlob blob;
562    android::status_t status = p->writeBlob(size, &blob);
563    if (status) {
564        doThrowRE(env, "Could not write bitmap to parcel blob.");
565        return false;
566    }
567
568    bitmap->lockPixels();
569    const void* pSrc =  bitmap->getPixels();
570    if (pSrc == NULL) {
571        memset(blob.data(), 0, size);
572    } else {
573        memcpy(blob.data(), pSrc, size);
574    }
575    bitmap->unlockPixels();
576
577    blob.release();
578    return true;
579}
580
581static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
582                                   const SkBitmap* src, const SkPaint* paint,
583                                   jintArray offsetXY) {
584    SkIPoint  offset;
585    SkBitmap* dst = new SkBitmap;
586    JavaPixelAllocator allocator(env);
587
588    src->extractAlpha(dst, paint, &allocator, &offset);
589    // If Skia can't allocate pixels for destination bitmap, it resets
590    // it, that is set its pixels buffer to NULL, and zero width and height.
591    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
592        delete dst;
593        doThrowOOME(env, "failed to allocate pixels for alpha");
594        return NULL;
595    }
596    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
597        int* array = env->GetIntArrayElements(offsetXY, NULL);
598        array[0] = offset.fX;
599        array[1] = offset.fY;
600        env->ReleaseIntArrayElements(offsetXY, array, 0);
601    }
602
603    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
604            GraphicsJNI::kBitmapCreateFlag_Mutable, NULL, NULL);
605}
606
607///////////////////////////////////////////////////////////////////////////////
608
609static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
610        int x, int y, bool isPremultiplied) {
611    SkAutoLockPixels alp(*bitmap);
612
613    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
614    if (NULL == proc) {
615        return 0;
616    }
617    const void* src = bitmap->getAddr(x, y);
618    if (NULL == src) {
619        return 0;
620    }
621
622    SkColor dst[1];
623    proc(dst, src, 1, bitmap->getColorTable());
624    return dst[0];
625}
626
627static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
628        jintArray pixelArray, int offset, int stride,
629        int x, int y, int width, int height, bool isPremultiplied) {
630    SkAutoLockPixels alp(*bitmap);
631
632    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
633    if (NULL == proc) {
634        return;
635    }
636    const void* src = bitmap->getAddr(x, y);
637    if (NULL == src) {
638        return;
639    }
640
641    SkColorTable* ctable = bitmap->getColorTable();
642    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
643    SkColor* d = (SkColor*)dst + offset;
644    while (--height >= 0) {
645        proc(d, src, width, ctable);
646        d += stride;
647        src = (void*)((const char*)src + bitmap->rowBytes());
648    }
649    env->ReleaseIntArrayElements(pixelArray, dst, 0);
650}
651
652///////////////////////////////////////////////////////////////////////////////
653
654static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
655        int x, int y, SkColor color, bool isPremultiplied) {
656    SkAutoLockPixels alp(*bitmap);
657    if (NULL == bitmap->getPixels()) {
658        return;
659    }
660
661    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
662    if (NULL == proc) {
663        return;
664    }
665
666    proc(bitmap->getAddr(x, y), &color, 1, x, y);
667    bitmap->notifyPixelsChanged();
668}
669
670static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
671        jintArray pixelArray, int offset, int stride,
672        int x, int y, int width, int height, bool isPremultiplied) {
673    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
674            x, y, width, height, *bitmap, isPremultiplied);
675}
676
677static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
678                                      const SkBitmap* bitmap, jobject jbuffer) {
679    SkAutoLockPixels alp(*bitmap);
680    const void* src = bitmap->getPixels();
681
682    if (NULL != src) {
683        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
684
685        // the java side has already checked that buffer is large enough
686        memcpy(abp.pointer(), src, bitmap->getSize());
687    }
688}
689
690static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
691                                    const SkBitmap* bitmap, jobject jbuffer) {
692    SkAutoLockPixels alp(*bitmap);
693    void* dst = bitmap->getPixels();
694
695    if (NULL != dst) {
696        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
697        // the java side has already checked that buffer is large enough
698        memcpy(dst, abp.pointer(), bitmap->getSize());
699        bitmap->notifyPixelsChanged();
700    }
701}
702
703static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0,
704                             const SkBitmap* bm1) {
705    if (bm0->width() != bm1->width() ||
706        bm0->height() != bm1->height() ||
707        bm0->config() != bm1->config()) {
708        return false;
709    }
710
711    SkAutoLockPixels alp0(*bm0);
712    SkAutoLockPixels alp1(*bm1);
713
714    // if we can't load the pixels, return false
715    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
716        return false;
717    }
718
719    if (bm0->config() == SkBitmap::kIndex8_Config) {
720        SkColorTable* ct0 = bm0->getColorTable();
721        SkColorTable* ct1 = bm1->getColorTable();
722        if (NULL == ct0 || NULL == ct1) {
723            return false;
724        }
725        if (ct0->count() != ct1->count()) {
726            return false;
727        }
728
729        SkAutoLockColors alc0(ct0);
730        SkAutoLockColors alc1(ct1);
731        const size_t size = ct0->count() * sizeof(SkPMColor);
732        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
733            return false;
734        }
735    }
736
737    // now compare each scanline. We can't do the entire buffer at once,
738    // since we don't care about the pixel values that might extend beyond
739    // the width (since the scanline might be larger than the logical width)
740    const int h = bm0->height();
741    const size_t size = bm0->width() * bm0->bytesPerPixel();
742    for (int y = 0; y < h; y++) {
743        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
744            return false;
745        }
746    }
747    return true;
748}
749
750static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
751    bitmap->lockPixels();
752    bitmap->unlockPixels();
753}
754
755///////////////////////////////////////////////////////////////////////////////
756
757#include <android_runtime/AndroidRuntime.h>
758
759static JNINativeMethod gBitmapMethods[] = {
760    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
761        (void*)Bitmap_creator },
762    {   "nativeCopy",               "(IIZ)Landroid/graphics/Bitmap;",
763        (void*)Bitmap_copy },
764    {   "nativeDestructor",         "(I)V", (void*)Bitmap_destructor },
765    {   "nativeRecycle",            "(I)Z", (void*)Bitmap_recycle },
766    {   "nativeReconfigure",        "(IIIII)V", (void*)Bitmap_reconfigure },
767    {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",
768        (void*)Bitmap_compress },
769    {   "nativeErase",              "(II)V", (void*)Bitmap_erase },
770    {   "nativeRowBytes",           "(I)I", (void*)Bitmap_rowBytes },
771    {   "nativeConfig",             "(I)I", (void*)Bitmap_config },
772    {   "nativeHasAlpha",           "(I)Z", (void*)Bitmap_hasAlpha },
773    {   "nativeSetHasAlpha",        "(IZ)V", (void*)Bitmap_setHasAlpha },
774    {   "nativeHasMipMap",          "(I)Z", (void*)Bitmap_hasMipMap },
775    {   "nativeSetHasMipMap",       "(IZ)V", (void*)Bitmap_setHasMipMap },
776    {   "nativeCreateFromParcel",
777        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
778        (void*)Bitmap_createFromParcel },
779    {   "nativeWriteToParcel",      "(IZILandroid/os/Parcel;)Z",
780        (void*)Bitmap_writeToParcel },
781    {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",
782        (void*)Bitmap_extractAlpha },
783    {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },
784    {   "nativeGetPixel",           "(IIIZ)I", (void*)Bitmap_getPixel },
785    {   "nativeGetPixels",          "(I[IIIIIIIZ)V", (void*)Bitmap_getPixels },
786    {   "nativeSetPixel",           "(IIIIZ)V", (void*)Bitmap_setPixel },
787    {   "nativeSetPixels",          "(I[IIIIIIIZ)V", (void*)Bitmap_setPixels },
788    {   "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
789                                            (void*)Bitmap_copyPixelsToBuffer },
790    {   "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
791                                            (void*)Bitmap_copyPixelsFromBuffer },
792    {   "nativeSameAs",             "(II)Z", (void*)Bitmap_sameAs },
793    {   "nativePrepareToDraw",      "(I)V", (void*)Bitmap_prepareToDraw },
794};
795
796#define kClassPathName  "android/graphics/Bitmap"
797
798int register_android_graphics_Bitmap(JNIEnv* env)
799{
800    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
801                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
802}
803