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
30 typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
31 int x, int y);
32
FromColor_D32(void * dst,const SkColor src[],int width,int,int)33 static 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
FromColor_D32_Raw(void * dst,const SkColor src[],int width,int,int)42 static 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
FromColor_D565(void * dst,const SkColor src[],int width,int x,int y)59 static 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
FromColor_D4444(void * dst,const SkColor src[],int width,int x,int y)71 static 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
FromColor_D4444_Raw(void * dst,const SkColor src[],int width,int x,int y)83 static 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
ChooseFromColorProc(SkBitmap::Config config,bool isPremultiplied)100 static 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
SetPixels(JNIEnv * env,jintArray srcColors,int srcOffset,int srcStride,int x,int y,int width,int height,const SkBitmap & dstBitmap,bool isPremultiplied)114 bool 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
146 typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
147 SkColorTable*);
148
ToColor_S32_Alpha(SkColor dst[],const void * src,int width,SkColorTable *)149 static 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
ToColor_S32_Raw(SkColor dst[],const void * src,int width,SkColorTable *)158 static 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
ToColor_S32_Opaque(SkColor dst[],const void * src,int width,SkColorTable *)169 static 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
ToColor_S4444_Alpha(SkColor dst[],const void * src,int width,SkColorTable *)180 static 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
ToColor_S4444_Raw(SkColor dst[],const void * src,int width,SkColorTable *)189 static 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
ToColor_S4444_Opaque(SkColor dst[],const void * src,int width,SkColorTable *)200 static 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
ToColor_S565(SkColor dst[],const void * src,int width,SkColorTable *)211 static 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
ToColor_SI8_Alpha(SkColor dst[],const void * src,int width,SkColorTable * ctable)222 static 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
ToColor_SI8_Raw(SkColor dst[],const void * src,int width,SkColorTable * ctable)233 static 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
ToColor_SI8_Opaque(SkColor dst[],const void * src,int width,SkColorTable * ctable)246 static 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
ChooseToColorProc(const SkBitmap & src,bool isPremultiplied)260 static 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
getPremulBitmapCreateFlags(bool isMutable)285 static int getPremulBitmapCreateFlags(bool isMutable) {
286 int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
287 if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
288 return flags;
289 }
290
Bitmap_creator(JNIEnv * env,jobject,jintArray jColors,int offset,int stride,int width,int height,SkBitmap::Config config,jboolean isMutable)291 static 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
Bitmap_copy(JNIEnv * env,jobject,const SkBitmap * src,SkBitmap::Config dstConfig,jboolean isMutable)324 static 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
Bitmap_destructor(JNIEnv * env,jobject,SkBitmap * bitmap)336 static 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
Bitmap_recycle(JNIEnv * env,jobject,SkBitmap * bitmap)346 static 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
Bitmap_reconfigure(JNIEnv * env,jobject clazz,jint bitmapInt,int width,int height,SkBitmap::Config config,int allocSize)356 static 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
377 enum JavaEncodeFormat {
378 kJPEG_JavaEncodeFormat = 0,
379 kPNG_JavaEncodeFormat = 1,
380 kWEBP_JavaEncodeFormat = 2
381 };
382
Bitmap_compress(JNIEnv * env,jobject clazz,SkBitmap * bitmap,int format,int quality,jobject jstream,jbyteArray jstorage)383 static 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
Bitmap_erase(JNIEnv * env,jobject,SkBitmap * bitmap,jint color)425 static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
426 bitmap->eraseColor(color);
427 }
428
Bitmap_rowBytes(JNIEnv * env,jobject,SkBitmap * bitmap)429 static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
430 return bitmap->rowBytes();
431 }
432
Bitmap_config(JNIEnv * env,jobject,SkBitmap * bitmap)433 static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
434 return bitmap->config();
435 }
436
Bitmap_getGenerationId(JNIEnv * env,jobject,SkBitmap * bitmap)437 static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {
438 return bitmap->getGenerationID();
439 }
440
Bitmap_hasAlpha(JNIEnv * env,jobject,SkBitmap * bitmap)441 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
442 return !bitmap->isOpaque();
443 }
444
Bitmap_setAlphaAndPremultiplied(JNIEnv * env,jobject,SkBitmap * bitmap,jboolean hasAlpha,jboolean isPremul)445 static 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
Bitmap_hasMipMap(JNIEnv * env,jobject,SkBitmap * bitmap)456 static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap) {
457 return bitmap->hasHardwareMipMap();
458 }
459
Bitmap_setHasMipMap(JNIEnv * env,jobject,SkBitmap * bitmap,jboolean hasMipMap)460 static void Bitmap_setHasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap,
461 jboolean hasMipMap) {
462 bitmap->setHasHardwareMipMap(hasMipMap);
463 }
464
465 ///////////////////////////////////////////////////////////////////////////////
466
Bitmap_createFromParcel(JNIEnv * env,jobject,jobject parcel)467 static 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
Bitmap_writeToParcel(JNIEnv * env,jobject,const SkBitmap * bitmap,jboolean isMutable,jint density,jobject parcel)534 static 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
Bitmap_extractAlpha(JNIEnv * env,jobject clazz,const SkBitmap * src,const SkPaint * paint,jintArray offsetXY)587 static 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
Bitmap_getPixel(JNIEnv * env,jobject,const SkBitmap * bitmap,int x,int y,bool isPremultiplied)615 static 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
Bitmap_getPixels(JNIEnv * env,jobject,const SkBitmap * bitmap,jintArray pixelArray,int offset,int stride,int x,int y,int width,int height,bool isPremultiplied)633 static 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
Bitmap_setPixel(JNIEnv * env,jobject,const SkBitmap * bitmap,int x,int y,SkColor color,bool isPremultiplied)660 static 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
Bitmap_setPixels(JNIEnv * env,jobject,const SkBitmap * bitmap,jintArray pixelArray,int offset,int stride,int x,int y,int width,int height,bool isPremultiplied)676 static 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
Bitmap_copyPixelsToBuffer(JNIEnv * env,jobject,const SkBitmap * bitmap,jobject jbuffer)683 static 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
Bitmap_copyPixelsFromBuffer(JNIEnv * env,jobject,const SkBitmap * bitmap,jobject jbuffer)696 static 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
Bitmap_sameAs(JNIEnv * env,jobject,const SkBitmap * bm0,const SkBitmap * bm1)709 static 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
Bitmap_prepareToDraw(JNIEnv * env,jobject,SkBitmap * bitmap)756 static 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
765 static 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
register_android_graphics_Bitmap(JNIEnv * env)804 int register_android_graphics_Bitmap(JNIEnv* env)
805 {
806 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
807 gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
808 }
809