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