Bitmap.cpp revision 0bbae0836426ba2704e38e7f90a9d0ca502ab71d
1#include "SkBitmap.h"
2#include "SkImageEncoder.h"
3#include "SkColorPriv.h"
4#include "GraphicsJNI.h"
5#include "SkDither.h"
6#include "SkUnPreMultiply.h"
7
8#include <binder/Parcel.h>
9#include "android_util_Binder.h"
10#include "android_nio_utils.h"
11#include "CreateJavaOutputStreamAdaptor.h"
12
13#include <jni.h>
14
15#if 0
16    #define TRACE_BITMAP(code)  code
17#else
18    #define TRACE_BITMAP(code)
19#endif
20
21///////////////////////////////////////////////////////////////////////////////
22// Conversions to/from SkColor, for get/setPixels, and the create method, which
23// is basically like setPixels
24
25typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
26                              int x, int y);
27
28static void FromColor_D32(void* dst, const SkColor src[], int width,
29                          int, int) {
30    SkPMColor* d = (SkPMColor*)dst;
31
32    for (int i = 0; i < width; i++) {
33        *d++ = SkPreMultiplyColor(*src++);
34    }
35}
36
37static void FromColor_D565(void* dst, const SkColor src[], int width,
38                           int x, int y) {
39    uint16_t* d = (uint16_t*)dst;
40
41    DITHER_565_SCAN(y);
42    for (int stop = x + width; x < stop; x++) {
43        SkColor c = *src++;
44        *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
45                                DITHER_VALUE(x));
46    }
47}
48
49static void FromColor_D4444(void* dst, const SkColor src[], int width,
50                            int x, int y) {
51    SkPMColor16* d = (SkPMColor16*)dst;
52
53    DITHER_4444_SCAN(y);
54    for (int stop = x + width; x < stop; x++) {
55        SkPMColor c = SkPreMultiplyColor(*src++);
56        *d++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
57//        *d++ = SkPixel32ToPixel4444(c);
58    }
59}
60
61// can return NULL
62static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {
63    switch (config) {
64        case SkBitmap::kARGB_8888_Config:
65            return FromColor_D32;
66        case SkBitmap::kARGB_4444_Config:
67            return FromColor_D4444;
68        case SkBitmap::kRGB_565_Config:
69            return FromColor_D565;
70        default:
71            break;
72    }
73    return NULL;
74}
75
76bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors,
77                            int srcOffset, int srcStride,
78                            int x, int y, int width, int height,
79                            const SkBitmap& dstBitmap) {
80    SkAutoLockPixels alp(dstBitmap);
81    void* dst = dstBitmap.getPixels();
82    FromColorProc proc = ChooseFromColorProc(dstBitmap.config());
83
84    if (NULL == dst || NULL == proc) {
85        return false;
86    }
87
88    const jint* array = env->GetIntArrayElements(srcColors, NULL);
89    const SkColor* src = (const SkColor*)array + srcOffset;
90
91    // reset to to actual choice from caller
92    dst = dstBitmap.getAddr(x, y);
93    // now copy/convert each scanline
94    for (int y = 0; y < height; y++) {
95        proc(dst, src, width, x, y);
96        src += srcStride;
97        dst = (char*)dst + dstBitmap.rowBytes();
98    }
99
100    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
101                                 JNI_ABORT);
102    return true;
103}
104
105//////////////////// ToColor procs
106
107typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
108                            SkColorTable*);
109
110static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
111                              SkColorTable*) {
112    SkASSERT(width > 0);
113    const SkPMColor* s = (const SkPMColor*)src;
114    do {
115        *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
116    } while (--width != 0);
117}
118
119static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
120                               SkColorTable*) {
121    SkASSERT(width > 0);
122    const SkPMColor* s = (const SkPMColor*)src;
123    do {
124        SkPMColor c = *s++;
125        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
126                               SkGetPackedB32(c));
127    } while (--width != 0);
128}
129
130static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
131                                SkColorTable*) {
132    SkASSERT(width > 0);
133    const SkPMColor16* s = (const SkPMColor16*)src;
134    do {
135        *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
136    } while (--width != 0);
137}
138
139static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
140                                 SkColorTable*) {
141    SkASSERT(width > 0);
142    const SkPMColor* s = (const SkPMColor*)src;
143    do {
144        SkPMColor c = SkPixel4444ToPixel32(*s++);
145        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
146                               SkGetPackedB32(c));
147    } while (--width != 0);
148}
149
150static void ToColor_S565(SkColor dst[], const void* src, int width,
151                         SkColorTable*) {
152    SkASSERT(width > 0);
153    const uint16_t* s = (const uint16_t*)src;
154    do {
155        uint16_t c = *s++;
156        *dst++ =  SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
157                                SkPacked16ToB32(c));
158    } while (--width != 0);
159}
160
161static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
162                              SkColorTable* ctable) {
163    SkASSERT(width > 0);
164    const uint8_t* s = (const uint8_t*)src;
165    const SkPMColor* colors = ctable->lockColors();
166    do {
167        *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
168    } while (--width != 0);
169    ctable->unlockColors(false);
170}
171
172static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
173                               SkColorTable* ctable) {
174    SkASSERT(width > 0);
175    const uint8_t* s = (const uint8_t*)src;
176    const SkPMColor* colors = ctable->lockColors();
177    do {
178        SkPMColor c = colors[*s++];
179        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
180                               SkGetPackedB32(c));
181    } while (--width != 0);
182    ctable->unlockColors(false);
183}
184
185// can return NULL
186static ToColorProc ChooseToColorProc(const SkBitmap& src) {
187    switch (src.config()) {
188        case SkBitmap::kARGB_8888_Config:
189            return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha;
190        case SkBitmap::kARGB_4444_Config:
191            return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha;
192        case SkBitmap::kRGB_565_Config:
193            return ToColor_S565;
194        case SkBitmap::kIndex8_Config:
195            if (src.getColorTable() == NULL) {
196                return NULL;
197            }
198            return src.isOpaque() ? ToColor_SI8_Opaque : ToColor_SI8_Alpha;
199        default:
200            break;
201    }
202    return NULL;
203}
204
205///////////////////////////////////////////////////////////////////////////////
206///////////////////////////////////////////////////////////////////////////////
207
208static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
209                              int offset, int stride, int width, int height,
210                              SkBitmap::Config config, jboolean isMutable) {
211    if (width <= 0 || height <= 0) {
212        doThrowIAE(env, "width and height must be > 0");
213        return NULL;
214    }
215
216    if (NULL != jColors) {
217        size_t n = env->GetArrayLength(jColors);
218        if (n < SkAbs32(stride) * (size_t)height) {
219            doThrowAIOOBE(env);
220            return NULL;
221        }
222    }
223
224    SkBitmap bitmap;
225
226    bitmap.setConfig(config, width, height);
227    if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) {
228        return NULL;
229    }
230
231    if (jColors != NULL) {
232        GraphicsJNI::SetPixels(env, jColors, offset, stride,
233                               0, 0, width, height, bitmap);
234    }
235
236    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable,
237                                     NULL);
238}
239
240static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
241                           SkBitmap::Config dstConfig, jboolean isMutable) {
242    SkBitmap            result;
243    JavaPixelAllocator  allocator(env, true);
244
245    if (!src->copyTo(&result, dstConfig, &allocator)) {
246        return NULL;
247    }
248
249    return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable,
250                                     NULL);
251}
252
253static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
254    delete bitmap;
255}
256
257static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {
258    bitmap->setPixels(NULL, NULL);
259}
260
261// These must match the int values in Bitmap.java
262enum JavaEncodeFormat {
263    kJPEG_JavaEncodeFormat = 0,
264    kPNG_JavaEncodeFormat = 1
265};
266
267static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap,
268                            int format, int quality,
269                            jobject jstream, jbyteArray jstorage) {
270    SkImageEncoder::Type fm;
271
272    switch (format) {
273    case kJPEG_JavaEncodeFormat:
274        fm = SkImageEncoder::kJPEG_Type;
275        break;
276    case kPNG_JavaEncodeFormat:
277        fm = SkImageEncoder::kPNG_Type;
278        break;
279    default:
280        return false;
281    }
282
283    bool success = false;
284    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
285    if (NULL != strm) {
286        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
287        if (NULL != encoder) {
288            success = encoder->encodeStream(strm, *bitmap, quality);
289            delete encoder;
290        }
291        delete strm;
292    }
293    return success;
294}
295
296static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
297    bitmap->eraseColor(color);
298}
299
300static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) {
301    return bitmap->width();
302}
303
304static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) {
305    return bitmap->height();
306}
307
308static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
309    return bitmap->rowBytes();
310}
311
312static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
313    return bitmap->config();
314}
315
316static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {
317    return bitmap->getGenerationID();
318}
319
320static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
321    return !bitmap->isOpaque();
322}
323
324static void Bitmap_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap,
325                               jboolean hasAlpha) {
326    bitmap->setIsOpaque(!hasAlpha);
327}
328
329///////////////////////////////////////////////////////////////////////////////
330
331static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
332    if (parcel == NULL) {
333        SkDebugf("-------- unparcel parcel is NULL\n");
334        return NULL;
335    }
336
337    android::Parcel* p = android::parcelForJavaObject(env, parcel);
338
339    const bool              isMutable = p->readInt32() != 0;
340    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
341    const int               width = p->readInt32();
342    const int               height = p->readInt32();
343    const int               rowBytes = p->readInt32();
344    const int               density = p->readInt32();
345
346    if (SkBitmap::kARGB_8888_Config != config &&
347            SkBitmap::kRGB_565_Config != config &&
348            SkBitmap::kARGB_4444_Config != config &&
349            SkBitmap::kIndex8_Config != config &&
350            SkBitmap::kA8_Config != config) {
351        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
352        return NULL;
353    }
354
355    SkBitmap* bitmap = new SkBitmap;
356
357    bitmap->setConfig(config, width, height, rowBytes);
358
359    SkColorTable* ctable = NULL;
360    if (config == SkBitmap::kIndex8_Config) {
361        int count = p->readInt32();
362        if (count > 0) {
363            size_t size = count * sizeof(SkPMColor);
364            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
365            ctable = new SkColorTable(src, count);
366        }
367    }
368
369    if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, true)) {
370        ctable->safeUnref();
371        delete bitmap;
372        return NULL;
373    }
374
375    ctable->safeUnref();
376
377    size_t size = bitmap->getSize();
378    bitmap->lockPixels();
379    memcpy(bitmap->getPixels(), p->readInplace(size), size);
380    bitmap->unlockPixels();
381
382    return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL, density);
383}
384
385static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
386                                     const SkBitmap* bitmap,
387                                     jboolean isMutable, jint density,
388                                     jobject parcel) {
389    if (parcel == NULL) {
390        SkDebugf("------- writeToParcel null parcel\n");
391        return false;
392    }
393
394    android::Parcel* p = android::parcelForJavaObject(env, parcel);
395
396    p->writeInt32(isMutable);
397    p->writeInt32(bitmap->config());
398    p->writeInt32(bitmap->width());
399    p->writeInt32(bitmap->height());
400    p->writeInt32(bitmap->rowBytes());
401    p->writeInt32(density);
402
403    if (bitmap->getConfig() == SkBitmap::kIndex8_Config) {
404        SkColorTable* ctable = bitmap->getColorTable();
405        if (ctable != NULL) {
406            int count = ctable->count();
407            p->writeInt32(count);
408            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
409                   ctable->lockColors(), count * sizeof(SkPMColor));
410            ctable->unlockColors(false);
411        } else {
412            p->writeInt32(0);   // indicate no ctable
413        }
414    }
415
416    size_t size = bitmap->getSize();
417    bitmap->lockPixels();
418    memcpy(p->writeInplace(size), bitmap->getPixels(), size);
419    bitmap->unlockPixels();
420    return true;
421}
422
423static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
424                                   const SkBitmap* src, const SkPaint* paint,
425                                   jintArray offsetXY) {
426    SkIPoint  offset;
427    SkBitmap* dst = new SkBitmap;
428
429    src->extractAlpha(dst, paint, &offset);
430    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
431        int* array = env->GetIntArrayElements(offsetXY, NULL);
432        array[0] = offset.fX;
433        array[1] = offset.fY;
434        env->ReleaseIntArrayElements(offsetXY, array, 0);
435    }
436
437    return GraphicsJNI::createBitmap(env, dst, true, NULL);
438}
439
440///////////////////////////////////////////////////////////////////////////////
441
442static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
443                           int x, int y) {
444    SkAutoLockPixels alp(*bitmap);
445
446    ToColorProc proc = ChooseToColorProc(*bitmap);
447    if (NULL == proc) {
448        return 0;
449    }
450    const void* src = bitmap->getAddr(x, y);
451    if (NULL == src) {
452        return 0;
453    }
454
455    SkColor dst[1];
456    proc(dst, src, 1, bitmap->getColorTable());
457    return dst[0];
458}
459
460static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
461                             jintArray pixelArray, int offset, int stride,
462                             int x, int y, int width, int height) {
463    SkAutoLockPixels alp(*bitmap);
464
465    ToColorProc proc = ChooseToColorProc(*bitmap);
466    if (NULL == proc) {
467        return;
468    }
469    const void* src = bitmap->getAddr(x, y);
470    if (NULL == src) {
471        return;
472    }
473
474    SkColorTable* ctable = bitmap->getColorTable();
475    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
476    SkColor* d = (SkColor*)dst + offset;
477    while (--height >= 0) {
478        proc(d, src, width, ctable);
479        d += stride;
480        src = (void*)((const char*)src + bitmap->rowBytes());
481    }
482    env->ReleaseIntArrayElements(pixelArray, dst, 0);
483}
484
485///////////////////////////////////////////////////////////////////////////////
486
487static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
488                            int x, int y, SkColor color) {
489    SkAutoLockPixels alp(*bitmap);
490    if (NULL == bitmap->getPixels()) {
491        return;
492    }
493
494    FromColorProc proc = ChooseFromColorProc(bitmap->config());
495    if (NULL == proc) {
496        return;
497    }
498
499    proc(bitmap->getAddr(x, y), &color, 1, x, y);
500}
501
502static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
503                             jintArray pixelArray, int offset, int stride,
504                             int x, int y, int width, int height) {
505    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
506                           x, y, width, height, *bitmap);
507}
508
509static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
510                                      const SkBitmap* bitmap, jobject jbuffer) {
511    SkAutoLockPixels alp(*bitmap);
512    const void* src = bitmap->getPixels();
513
514    if (NULL != src) {
515        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
516
517        // the java side has already checked that buffer is large enough
518        memcpy(abp.pointer(), src, bitmap->getSize());
519    }
520}
521
522static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
523                                    const SkBitmap* bitmap, jobject jbuffer) {
524    SkAutoLockPixels alp(*bitmap);
525    void* dst = bitmap->getPixels();
526
527    if (NULL != dst) {
528        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
529        // the java side has already checked that buffer is large enough
530        memcpy(dst, abp.pointer(), bitmap->getSize());
531    }
532}
533
534static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0,
535                             const SkBitmap* bm1) {
536    if (bm0->width() != bm1->width() ||
537        bm0->height() != bm1->height() ||
538        bm0->config() != bm1->config()) {
539        return false;
540    }
541
542    SkAutoLockPixels alp0(*bm0);
543    SkAutoLockPixels alp1(*bm1);
544
545    // if we can't load the pixels, return false
546    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
547        return false;
548    }
549
550    if (bm0->config() == SkBitmap::kIndex8_Config) {
551        SkColorTable* ct0 = bm0->getColorTable();
552        SkColorTable* ct1 = bm1->getColorTable();
553        if (NULL == ct0 || NULL == ct1) {
554            return false;
555        }
556        if (ct0->count() != ct1->count()) {
557            return false;
558        }
559
560        SkAutoLockColors alc0(ct0);
561        SkAutoLockColors alc1(ct1);
562        const size_t size = ct0->count() * sizeof(SkPMColor);
563        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
564            return false;
565        }
566    }
567
568    // now compare each scanline. We can't do the entire buffer at once,
569    // since we don't care about the pixel values that might extend beyond
570    // the width (since the scanline might be larger than the logical width)
571    const int h = bm0->height();
572    const size_t size = bm0->width() * bm0->bytesPerPixel();
573    for (int y = 0; y < h; y++) {
574        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
575            return false;
576        }
577    }
578    return true;
579}
580
581static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
582    bitmap->lockPixels();
583    bitmap->unlockPixels();
584}
585
586///////////////////////////////////////////////////////////////////////////////
587
588#include <android_runtime/AndroidRuntime.h>
589
590static JNINativeMethod gBitmapMethods[] = {
591    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
592        (void*)Bitmap_creator },
593    {   "nativeCopy",               "(IIZ)Landroid/graphics/Bitmap;",
594        (void*)Bitmap_copy },
595    {   "nativeDestructor",         "(I)V", (void*)Bitmap_destructor },
596    {   "nativeRecycle",            "(I)V", (void*)Bitmap_recycle },
597    {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",
598        (void*)Bitmap_compress },
599    {   "nativeErase",              "(II)V", (void*)Bitmap_erase },
600    {   "nativeWidth",              "(I)I", (void*)Bitmap_width },
601    {   "nativeHeight",             "(I)I", (void*)Bitmap_height },
602    {   "nativeRowBytes",           "(I)I", (void*)Bitmap_rowBytes },
603    {   "nativeConfig",             "(I)I", (void*)Bitmap_config },
604    {   "nativeHasAlpha",           "(I)Z", (void*)Bitmap_hasAlpha },
605    {   "nativeSetHasAlpha",        "(IZ)V", (void*)Bitmap_setHasAlpha },
606    {   "nativeCreateFromParcel",
607        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
608        (void*)Bitmap_createFromParcel },
609    {   "nativeWriteToParcel",      "(IZILandroid/os/Parcel;)Z",
610        (void*)Bitmap_writeToParcel },
611    {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",
612        (void*)Bitmap_extractAlpha },
613    {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },
614    {   "nativeGetPixel",           "(III)I", (void*)Bitmap_getPixel },
615    {   "nativeGetPixels",          "(I[IIIIIII)V", (void*)Bitmap_getPixels },
616    {   "nativeSetPixel",           "(IIII)V", (void*)Bitmap_setPixel },
617    {   "nativeSetPixels",          "(I[IIIIIII)V", (void*)Bitmap_setPixels },
618    {   "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
619                                            (void*)Bitmap_copyPixelsToBuffer },
620    {   "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
621                                            (void*)Bitmap_copyPixelsFromBuffer },
622    {   "nativeSameAs",             "(II)Z", (void*)Bitmap_sameAs },
623    {   "nativePrepareToDraw",      "(I)V", (void*)Bitmap_prepareToDraw },
624};
625
626#define kClassPathName  "android/graphics/Bitmap"
627
628int register_android_graphics_Bitmap(JNIEnv* env);
629int register_android_graphics_Bitmap(JNIEnv* env)
630{
631    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
632                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
633}
634
635