OSDN Git Service

Make Bitmap_createFromParcel check the color count. DO NOT MERGE
[android-x86/frameworks-base.git] / core / jni / android / graphics / Bitmap.cpp
1 #include "SkBitmap.h"\r
2 #include "SkPixelRef.h"\r
3 #include "SkImageEncoder.h"\r
4 #include "SkColorPriv.h"\r
5 #include "GraphicsJNI.h"\r
6 #include "SkDither.h"\r
7 #include "SkUnPreMultiply.h"\r
8 #include "SkStream.h"\r
9 \r
10 #include <binder/Parcel.h>\r
11 #include "android_os_Parcel.h"\r
12 #include "android_util_Binder.h"\r
13 #include "android_nio_utils.h"\r
14 #include "CreateJavaOutputStreamAdaptor.h"\r
15 \r
16 #include <jni.h>\r
17 \r
18 #include <Caches.h>\r
19 \r
20 #if 0\r
21     #define TRACE_BITMAP(code)  code\r
22 #else\r
23     #define TRACE_BITMAP(code)\r
24 #endif\r
25 \r
26 ///////////////////////////////////////////////////////////////////////////////\r
27 // Conversions to/from SkColor, for get/setPixels, and the create method, which\r
28 // is basically like setPixels\r
29 \r
30 typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,\r
31                               int x, int y);\r
32 \r
33 static void FromColor_D32(void* dst, const SkColor src[], int width,\r
34                           int, int) {\r
35     SkPMColor* d = (SkPMColor*)dst;\r
36 \r
37     for (int i = 0; i < width; i++) {\r
38         *d++ = SkPreMultiplyColor(*src++);\r
39     }\r
40 }\r
41 \r
42 static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,\r
43                           int, int) {\r
44     // SkColor's ordering may be different from SkPMColor\r
45     if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {\r
46         memcpy(dst, src, width * sizeof(SkColor));\r
47         return;\r
48     }\r
49 \r
50     // order isn't same, repack each pixel manually\r
51     SkPMColor* d = (SkPMColor*)dst;\r
52     for (int i = 0; i < width; i++) {\r
53         SkColor c = *src++;\r
54         *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),\r
55                                    SkColorGetG(c), SkColorGetB(c));\r
56     }\r
57 }\r
58 \r
59 static void FromColor_D565(void* dst, const SkColor src[], int width,\r
60                            int x, int y) {\r
61     uint16_t* d = (uint16_t*)dst;\r
62 \r
63     DITHER_565_SCAN(y);\r
64     for (int stop = x + width; x < stop; x++) {\r
65         SkColor c = *src++;\r
66         *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),\r
67                                 DITHER_VALUE(x));\r
68     }\r
69 }\r
70 \r
71 static void FromColor_D4444(void* dst, const SkColor src[], int width,\r
72                             int x, int y) {\r
73     SkPMColor16* d = (SkPMColor16*)dst;\r
74 \r
75     DITHER_4444_SCAN(y);\r
76     for (int stop = x + width; x < stop; x++) {\r
77         SkPMColor pmc = SkPreMultiplyColor(*src++);\r
78         *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));\r
79 //        *d++ = SkPixel32ToPixel4444(pmc);\r
80     }\r
81 }\r
82 \r
83 static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,\r
84                             int x, int y) {\r
85     SkPMColor16* d = (SkPMColor16*)dst;\r
86 \r
87     DITHER_4444_SCAN(y);\r
88     for (int stop = x + width; x < stop; x++) {\r
89         SkColor c = *src++;\r
90 \r
91         // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied\r
92         SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),\r
93                                             SkColorGetG(c), SkColorGetB(c));\r
94         *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));\r
95 //        *d++ = SkPixel32ToPixel4444(pmc);\r
96     }\r
97 }\r
98 \r
99 // can return NULL\r
100 static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {\r
101     switch (config) {\r
102         case SkBitmap::kARGB_8888_Config:\r
103             return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;\r
104         case SkBitmap::kARGB_4444_Config:\r
105             return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;\r
106         case SkBitmap::kRGB_565_Config:\r
107             return FromColor_D565;\r
108         default:\r
109             break;\r
110     }\r
111     return NULL;\r
112 }\r
113 \r
114 bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,\r
115         int x, int y, int width, int height,\r
116         const SkBitmap& dstBitmap, bool isPremultiplied) {\r
117     SkAutoLockPixels alp(dstBitmap);\r
118     void* dst = dstBitmap.getPixels();\r
119     FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);\r
120 \r
121     if (NULL == dst || NULL == proc) {\r
122         return false;\r
123     }\r
124 \r
125     const jint* array = env->GetIntArrayElements(srcColors, NULL);\r
126     const SkColor* src = (const SkColor*)array + srcOffset;\r
127 \r
128     // reset to to actual choice from caller\r
129     dst = dstBitmap.getAddr(x, y);\r
130     // now copy/convert each scanline\r
131     for (int y = 0; y < height; y++) {\r
132         proc(dst, src, width, x, y);\r
133         src += srcStride;\r
134         dst = (char*)dst + dstBitmap.rowBytes();\r
135     }\r
136 \r
137     dstBitmap.notifyPixelsChanged();\r
138 \r
139     env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),\r
140                                  JNI_ABORT);\r
141     return true;\r
142 }\r
143 \r
144 //////////////////// ToColor procs\r
145 \r
146 typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,\r
147                             SkColorTable*);\r
148 \r
149 static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,\r
150                               SkColorTable*) {\r
151     SkASSERT(width > 0);\r
152     const SkPMColor* s = (const SkPMColor*)src;\r
153     do {\r
154         *dst++ = SkUnPreMultiply::PMColorToColor(*s++);\r
155     } while (--width != 0);\r
156 }\r
157 \r
158 static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,\r
159                               SkColorTable*) {\r
160     SkASSERT(width > 0);\r
161     const SkPMColor* s = (const SkPMColor*)src;\r
162     do {\r
163         SkPMColor c = *s++;\r
164         *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),\r
165                                 SkGetPackedG32(c), SkGetPackedB32(c));\r
166     } while (--width != 0);\r
167 }\r
168 \r
169 static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,\r
170                                SkColorTable*) {\r
171     SkASSERT(width > 0);\r
172     const SkPMColor* s = (const SkPMColor*)src;\r
173     do {\r
174         SkPMColor c = *s++;\r
175         *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),\r
176                                SkGetPackedB32(c));\r
177     } while (--width != 0);\r
178 }\r
179 \r
180 static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,\r
181                                 SkColorTable*) {\r
182     SkASSERT(width > 0);\r
183     const SkPMColor16* s = (const SkPMColor16*)src;\r
184     do {\r
185         *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));\r
186     } while (--width != 0);\r
187 }\r
188 \r
189 static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,\r
190                                 SkColorTable*) {\r
191     SkASSERT(width > 0);\r
192     const SkPMColor16* s = (const SkPMColor16*)src;\r
193     do {\r
194         SkPMColor c = SkPixel4444ToPixel32(*s++);\r
195         *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),\r
196                                 SkGetPackedG32(c), SkGetPackedB32(c));\r
197     } while (--width != 0);\r
198 }\r
199 \r
200 static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,\r
201                                  SkColorTable*) {\r
202     SkASSERT(width > 0);\r
203     const SkPMColor16* s = (const SkPMColor16*)src;\r
204     do {\r
205         SkPMColor c = SkPixel4444ToPixel32(*s++);\r
206         *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),\r
207                                SkGetPackedB32(c));\r
208     } while (--width != 0);\r
209 }\r
210 \r
211 static void ToColor_S565(SkColor dst[], const void* src, int width,\r
212                          SkColorTable*) {\r
213     SkASSERT(width > 0);\r
214     const uint16_t* s = (const uint16_t*)src;\r
215     do {\r
216         uint16_t c = *s++;\r
217         *dst++ =  SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),\r
218                                 SkPacked16ToB32(c));\r
219     } while (--width != 0);\r
220 }\r
221 \r
222 static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,\r
223                               SkColorTable* ctable) {\r
224     SkASSERT(width > 0);\r
225     const uint8_t* s = (const uint8_t*)src;\r
226     const SkPMColor* colors = ctable->lockColors();\r
227     do {\r
228         *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);\r
229     } while (--width != 0);\r
230     ctable->unlockColors();\r
231 }\r
232 \r
233 static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,\r
234                               SkColorTable* ctable) {\r
235     SkASSERT(width > 0);\r
236     const uint8_t* s = (const uint8_t*)src;\r
237     const SkPMColor* colors = ctable->lockColors();\r
238     do {\r
239         SkPMColor c = colors[*s++];\r
240         *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),\r
241                                 SkGetPackedG32(c), SkGetPackedB32(c));\r
242     } while (--width != 0);\r
243     ctable->unlockColors();\r
244 }\r
245 \r
246 static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,\r
247                                SkColorTable* ctable) {\r
248     SkASSERT(width > 0);\r
249     const uint8_t* s = (const uint8_t*)src;\r
250     const SkPMColor* colors = ctable->lockColors();\r
251     do {\r
252         SkPMColor c = colors[*s++];\r
253         *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),\r
254                                SkGetPackedB32(c));\r
255     } while (--width != 0);\r
256     ctable->unlockColors();\r
257 }\r
258 \r
259 // can return NULL\r
260 static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {\r
261     switch (src.config()) {\r
262         case SkBitmap::kARGB_8888_Config:\r
263             if (src.isOpaque()) return ToColor_S32_Opaque;\r
264             return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;\r
265         case SkBitmap::kARGB_4444_Config:\r
266             if (src.isOpaque()) return ToColor_S4444_Opaque;\r
267             return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;\r
268         case SkBitmap::kRGB_565_Config:\r
269             return ToColor_S565;\r
270         case SkBitmap::kIndex8_Config:\r
271             if (src.getColorTable() == NULL) {\r
272                 return NULL;\r
273             }\r
274             if (src.isOpaque()) return ToColor_SI8_Opaque;\r
275             return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;\r
276         default:\r
277             break;\r
278     }\r
279     return NULL;\r
280 }\r
281 \r
282 ///////////////////////////////////////////////////////////////////////////////\r
283 ///////////////////////////////////////////////////////////////////////////////\r
284 \r
285 static int getPremulBitmapCreateFlags(bool isMutable) {\r
286     int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;\r
287     if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;\r
288     return flags;\r
289 }\r
290 \r
291 static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,\r
292                               int offset, int stride, int width, int height,\r
293                               SkBitmap::Config config, jboolean isMutable) {\r
294     if (NULL != jColors) {\r
295         size_t n = env->GetArrayLength(jColors);\r
296         if (n < SkAbs32(stride) * (size_t)height) {\r
297             doThrowAIOOBE(env);\r
298             return NULL;\r
299         }\r
300     }\r
301 \r
302     // ARGB_4444 is a deprecated format, convert automatically to 8888\r
303     if (config == SkBitmap::kARGB_4444_Config) {\r
304         config = SkBitmap::kARGB_8888_Config;\r
305     }\r
306 \r
307     SkBitmap bitmap;\r
308     bitmap.setConfig(config, width, height);\r
309 \r
310     jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);\r
311     if (NULL == buff) {\r
312         return NULL;\r
313     }\r
314 \r
315     if (jColors != NULL) {\r
316         GraphicsJNI::SetPixels(env, jColors, offset, stride,\r
317                 0, 0, width, height, bitmap, true);\r
318     }\r
319 \r
320     return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,\r
321             getPremulBitmapCreateFlags(isMutable), NULL, NULL);\r
322 }\r
323 \r
324 static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,\r
325                            SkBitmap::Config dstConfig, jboolean isMutable) {\r
326     SkBitmap            result;\r
327     JavaPixelAllocator  allocator(env);\r
328 \r
329     if (!src->copyTo(&result, dstConfig, &allocator)) {\r
330         return NULL;\r
331     }\r
332     return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),\r
333             getPremulBitmapCreateFlags(isMutable), NULL, NULL);\r
334 }\r
335 \r
336 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
337 #ifdef USE_OPENGL_RENDERER\r
338     if (android::uirenderer::Caches::hasInstance()) {\r
339         android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);\r
340         return;\r
341     }\r
342 #endif // USE_OPENGL_RENDERER\r
343     delete bitmap;\r
344 }\r
345 \r
346 static jboolean Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
347 #ifdef USE_OPENGL_RENDERER\r
348     if (android::uirenderer::Caches::hasInstance()) {\r
349         return android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);\r
350     }\r
351 #endif // USE_OPENGL_RENDERER\r
352     bitmap->setPixels(NULL, NULL);\r
353     return true;\r
354 }\r
355 \r
356 static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jint bitmapInt,\r
357         int width, int height, SkBitmap::Config config, int allocSize) {\r
358     if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {\r
359         // done in native as there's no way to get BytesPerPixel in Java\r
360         doThrowIAE(env, "Bitmap not large enough to support new configuration");\r
361         return;\r
362     }\r
363     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapInt);\r
364     SkPixelRef* ref = bitmap->pixelRef();\r
365     SkSafeRef(ref);\r
366     bitmap->setConfig(config, width, height);\r
367     bitmap->setPixelRef(ref);\r
368 \r
369     // notifyPixelsChanged will increment the generation ID even though the actual pixel data\r
370     // hasn't been touched. This signals the renderer that the bitmap (including width, height,\r
371     // and config) has changed.\r
372     ref->notifyPixelsChanged();\r
373     SkSafeUnref(ref);\r
374 }\r
375 \r
376 // These must match the int values in Bitmap.java\r
377 enum JavaEncodeFormat {\r
378     kJPEG_JavaEncodeFormat = 0,\r
379     kPNG_JavaEncodeFormat = 1,\r
380     kWEBP_JavaEncodeFormat = 2\r
381 };\r
382 \r
383 static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap,\r
384                             int format, int quality,\r
385                             jobject jstream, jbyteArray jstorage) {\r
386     SkImageEncoder::Type fm;\r
387 \r
388     switch (format) {\r
389     case kJPEG_JavaEncodeFormat:\r
390         fm = SkImageEncoder::kJPEG_Type;\r
391         break;\r
392     case kPNG_JavaEncodeFormat:\r
393         fm = SkImageEncoder::kPNG_Type;\r
394         break;\r
395     case kWEBP_JavaEncodeFormat:\r
396         fm = SkImageEncoder::kWEBP_Type;\r
397         break;\r
398     default:\r
399         return false;\r
400     }\r
401 \r
402     bool success = false;\r
403     if (NULL != bitmap) {\r
404         SkAutoLockPixels alp(*bitmap);\r
405 \r
406         if (NULL == bitmap->getPixels()) {\r
407             return false;\r
408         }\r
409 \r
410         SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);\r
411         if (NULL == strm) {\r
412             return false;\r
413         }\r
414 \r
415         SkImageEncoder* encoder = SkImageEncoder::Create(fm);\r
416         if (NULL != encoder) {\r
417             success = encoder->encodeStream(strm, *bitmap, quality);\r
418             delete encoder;\r
419         }\r
420         delete strm;\r
421     }\r
422     return success;\r
423 }\r
424 \r
425 static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {\r
426     bitmap->eraseColor(color);\r
427 }\r
428 \r
429 static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
430     return bitmap->rowBytes();\r
431 }\r
432 \r
433 static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
434     return bitmap->config();\r
435 }\r
436 \r
437 static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
438     return bitmap->getGenerationID();\r
439 }\r
440 \r
441 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
442     return !bitmap->isOpaque();\r
443 }\r
444 \r
445 static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, SkBitmap* bitmap,\r
446                                             jboolean hasAlpha, jboolean isPremul) {\r
447     if (!hasAlpha) {\r
448         bitmap->setAlphaType(kOpaque_SkAlphaType);\r
449     } else if (isPremul) {\r
450         bitmap->setAlphaType(kPremul_SkAlphaType);\r
451     } else {\r
452         bitmap->setAlphaType(kUnpremul_SkAlphaType);\r
453     }\r
454 }\r
455 \r
456 static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
457     return bitmap->hasHardwareMipMap();\r
458 }\r
459 \r
460 static void Bitmap_setHasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap,\r
461                                 jboolean hasMipMap) {\r
462     bitmap->setHasHardwareMipMap(hasMipMap);\r
463 }\r
464 \r
465 ///////////////////////////////////////////////////////////////////////////////\r
466 \r
467 static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {\r
468     if (parcel == NULL) {\r
469         SkDebugf("-------- unparcel parcel is NULL\n");\r
470         return NULL;\r
471     }\r
472 \r
473     android::Parcel* p = android::parcelForJavaObject(env, parcel);\r
474 \r
475     const bool              isMutable = p->readInt32() != 0;\r
476     const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();\r
477     const int               width = p->readInt32();\r
478     const int               height = p->readInt32();\r
479     const int               rowBytes = p->readInt32();\r
480     const int               density = p->readInt32();\r
481 \r
482     if (SkBitmap::kARGB_8888_Config != config &&\r
483             SkBitmap::kRGB_565_Config != config &&\r
484             SkBitmap::kARGB_4444_Config != config &&\r
485             SkBitmap::kIndex8_Config != config &&\r
486             SkBitmap::kA8_Config != config) {\r
487         SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);\r
488         return NULL;\r
489     }\r
490 \r
491     SkAutoTDelete<SkBitmap> bitmap(new SkBitmap);\r
492 \r
493     if (!bitmap->setConfig(config, width, height, rowBytes)) {\r
494         return NULL;\r
495     }\r
496 \r
497     SkColorTable* ctable = NULL;\r
498     if (config == SkBitmap::kIndex8_Config) {\r
499         int count = p->readInt32();\r
500         if (count < 0 || count > 256) {\r
501             // The data is corrupt, since SkColorTable enforces a value between 0 and 256,\r
502             // inclusive.\r
503             return NULL;\r
504         }\r
505         if (count > 0) {\r
506             size_t size = count * sizeof(SkPMColor);\r
507             const SkPMColor* src = (const SkPMColor*)p->readInplace(size);\r
508             if (src == NULL) {\r
509                 return NULL;\r
510             }\r
511             ctable = new SkColorTable(src, count);\r
512         }\r
513     }\r
514 \r
515     jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);\r
516     if (NULL == buffer) {\r
517         SkSafeUnref(ctable);\r
518         return NULL;\r
519     }\r
520 \r
521     SkSafeUnref(ctable);\r
522 \r
523     size_t size = bitmap->getSize();\r
524 \r
525     android::Parcel::ReadableBlob blob;\r
526     android::status_t status = p->readBlob(size, &blob);\r
527     if (status) {\r
528         doThrowRE(env, "Could not read bitmap from parcel blob.");\r
529         return NULL;\r
530     }\r
531 \r
532     bitmap->lockPixels();\r
533     memcpy(bitmap->getPixels(), blob.data(), size);\r
534     bitmap->unlockPixels();\r
535 \r
536     blob.release();\r
537 \r
538     return GraphicsJNI::createBitmap(env, bitmap.detach(), buffer,\r
539             getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);\r
540 }\r
541 \r
542 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,\r
543                                      const SkBitmap* bitmap,\r
544                                      jboolean isMutable, jint density,\r
545                                      jobject parcel) {\r
546     if (parcel == NULL) {\r
547         SkDebugf("------- writeToParcel null parcel\n");\r
548         return false;\r
549     }\r
550 \r
551     android::Parcel* p = android::parcelForJavaObject(env, parcel);\r
552 \r
553     p->writeInt32(isMutable);\r
554     p->writeInt32(bitmap->config());\r
555     p->writeInt32(bitmap->width());\r
556     p->writeInt32(bitmap->height());\r
557     p->writeInt32(bitmap->rowBytes());\r
558     p->writeInt32(density);\r
559 \r
560     if (bitmap->config() == SkBitmap::kIndex8_Config) {\r
561         SkColorTable* ctable = bitmap->getColorTable();\r
562         if (ctable != NULL) {\r
563             int count = ctable->count();\r
564             p->writeInt32(count);\r
565             memcpy(p->writeInplace(count * sizeof(SkPMColor)),\r
566                    ctable->lockColors(), count * sizeof(SkPMColor));\r
567             ctable->unlockColors();\r
568         } else {\r
569             p->writeInt32(0);   // indicate no ctable\r
570         }\r
571     }\r
572 \r
573     size_t size = bitmap->getSize();\r
574 \r
575     android::Parcel::WritableBlob blob;\r
576     android::status_t status = p->writeBlob(size, &blob);\r
577     if (status) {\r
578         doThrowRE(env, "Could not write bitmap to parcel blob.");\r
579         return false;\r
580     }\r
581 \r
582     bitmap->lockPixels();\r
583     const void* pSrc =  bitmap->getPixels();\r
584     if (pSrc == NULL) {\r
585         memset(blob.data(), 0, size);\r
586     } else {\r
587         memcpy(blob.data(), pSrc, size);\r
588     }\r
589     bitmap->unlockPixels();\r
590 \r
591     blob.release();\r
592     return true;\r
593 }\r
594 \r
595 static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,\r
596                                    const SkBitmap* src, const SkPaint* paint,\r
597                                    jintArray offsetXY) {\r
598     SkIPoint  offset;\r
599     SkBitmap* dst = new SkBitmap;\r
600     JavaPixelAllocator allocator(env);\r
601 \r
602     src->extractAlpha(dst, paint, &allocator, &offset);\r
603     // If Skia can't allocate pixels for destination bitmap, it resets\r
604     // it, that is set its pixels buffer to NULL, and zero width and height.\r
605     if (dst->getPixels() == NULL && src->getPixels() != NULL) {\r
606         delete dst;\r
607         doThrowOOME(env, "failed to allocate pixels for alpha");\r
608         return NULL;\r
609     }\r
610     if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {\r
611         int* array = env->GetIntArrayElements(offsetXY, NULL);\r
612         array[0] = offset.fX;\r
613         array[1] = offset.fY;\r
614         env->ReleaseIntArrayElements(offsetXY, array, 0);\r
615     }\r
616 \r
617     return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),\r
618             GraphicsJNI::kBitmapCreateFlag_Mutable, NULL, NULL);\r
619 }\r
620 \r
621 ///////////////////////////////////////////////////////////////////////////////\r
622 \r
623 static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
624         int x, int y, bool isPremultiplied) {\r
625     SkAutoLockPixels alp(*bitmap);\r
626 \r
627     ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);\r
628     if (NULL == proc) {\r
629         return 0;\r
630     }\r
631     const void* src = bitmap->getAddr(x, y);\r
632     if (NULL == src) {\r
633         return 0;\r
634     }\r
635 \r
636     SkColor dst[1];\r
637     proc(dst, src, 1, bitmap->getColorTable());\r
638     return dst[0];\r
639 }\r
640 \r
641 static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
642         jintArray pixelArray, int offset, int stride,\r
643         int x, int y, int width, int height, bool isPremultiplied) {\r
644     SkAutoLockPixels alp(*bitmap);\r
645 \r
646     ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);\r
647     if (NULL == proc) {\r
648         return;\r
649     }\r
650     const void* src = bitmap->getAddr(x, y);\r
651     if (NULL == src) {\r
652         return;\r
653     }\r
654 \r
655     SkColorTable* ctable = bitmap->getColorTable();\r
656     jint* dst = env->GetIntArrayElements(pixelArray, NULL);\r
657     SkColor* d = (SkColor*)dst + offset;\r
658     while (--height >= 0) {\r
659         proc(d, src, width, ctable);\r
660         d += stride;\r
661         src = (void*)((const char*)src + bitmap->rowBytes());\r
662     }\r
663     env->ReleaseIntArrayElements(pixelArray, dst, 0);\r
664 }\r
665 \r
666 ///////////////////////////////////////////////////////////////////////////////\r
667 \r
668 static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
669         int x, int y, SkColor color, bool isPremultiplied) {\r
670     SkAutoLockPixels alp(*bitmap);\r
671     if (NULL == bitmap->getPixels()) {\r
672         return;\r
673     }\r
674 \r
675     FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);\r
676     if (NULL == proc) {\r
677         return;\r
678     }\r
679 \r
680     proc(bitmap->getAddr(x, y), &color, 1, x, y);\r
681     bitmap->notifyPixelsChanged();\r
682 }\r
683 \r
684 static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
685         jintArray pixelArray, int offset, int stride,\r
686         int x, int y, int width, int height, bool isPremultiplied) {\r
687     GraphicsJNI::SetPixels(env, pixelArray, offset, stride,\r
688             x, y, width, height, *bitmap, isPremultiplied);\r
689 }\r
690 \r
691 static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,\r
692                                       const SkBitmap* bitmap, jobject jbuffer) {\r
693     SkAutoLockPixels alp(*bitmap);\r
694     const void* src = bitmap->getPixels();\r
695 \r
696     if (NULL != src) {\r
697         android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);\r
698 \r
699         // the java side has already checked that buffer is large enough\r
700         memcpy(abp.pointer(), src, bitmap->getSize());\r
701     }\r
702 }\r
703 \r
704 static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,\r
705                                     const SkBitmap* bitmap, jobject jbuffer) {\r
706     SkAutoLockPixels alp(*bitmap);\r
707     void* dst = bitmap->getPixels();\r
708 \r
709     if (NULL != dst) {\r
710         android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);\r
711         // the java side has already checked that buffer is large enough\r
712         memcpy(dst, abp.pointer(), bitmap->getSize());\r
713         bitmap->notifyPixelsChanged();\r
714     }\r
715 }\r
716 \r
717 static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0,\r
718                              const SkBitmap* bm1) {\r
719     if (bm0->width() != bm1->width() ||\r
720         bm0->height() != bm1->height() ||\r
721         bm0->config() != bm1->config()) {\r
722         return false;\r
723     }\r
724 \r
725     SkAutoLockPixels alp0(*bm0);\r
726     SkAutoLockPixels alp1(*bm1);\r
727 \r
728     // if we can't load the pixels, return false\r
729     if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {\r
730         return false;\r
731     }\r
732 \r
733     if (bm0->config() == SkBitmap::kIndex8_Config) {\r
734         SkColorTable* ct0 = bm0->getColorTable();\r
735         SkColorTable* ct1 = bm1->getColorTable();\r
736         if (NULL == ct0 || NULL == ct1) {\r
737             return false;\r
738         }\r
739         if (ct0->count() != ct1->count()) {\r
740             return false;\r
741         }\r
742 \r
743         SkAutoLockColors alc0(ct0);\r
744         SkAutoLockColors alc1(ct1);\r
745         const size_t size = ct0->count() * sizeof(SkPMColor);\r
746         if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {\r
747             return false;\r
748         }\r
749     }\r
750 \r
751     // now compare each scanline. We can't do the entire buffer at once,\r
752     // since we don't care about the pixel values that might extend beyond\r
753     // the width (since the scanline might be larger than the logical width)\r
754     const int h = bm0->height();\r
755     const size_t size = bm0->width() * bm0->bytesPerPixel();\r
756     for (int y = 0; y < h; y++) {\r
757         if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {\r
758             return false;\r
759         }\r
760     }\r
761     return true;\r
762 }\r
763 \r
764 static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
765     bitmap->lockPixels();\r
766     bitmap->unlockPixels();\r
767 }\r
768 \r
769 ///////////////////////////////////////////////////////////////////////////////\r
770 \r
771 #include <android_runtime/AndroidRuntime.h>\r
772 \r
773 static JNINativeMethod gBitmapMethods[] = {\r
774     {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",\r
775         (void*)Bitmap_creator },\r
776     {   "nativeCopy",               "(IIZ)Landroid/graphics/Bitmap;",\r
777         (void*)Bitmap_copy },\r
778     {   "nativeDestructor",         "(I)V", (void*)Bitmap_destructor },\r
779     {   "nativeRecycle",            "(I)Z", (void*)Bitmap_recycle },\r
780     {   "nativeReconfigure",        "(IIIII)V", (void*)Bitmap_reconfigure },\r
781     {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",\r
782         (void*)Bitmap_compress },\r
783     {   "nativeErase",              "(II)V", (void*)Bitmap_erase },\r
784     {   "nativeRowBytes",           "(I)I", (void*)Bitmap_rowBytes },\r
785     {   "nativeConfig",             "(I)I", (void*)Bitmap_config },\r
786     {   "nativeHasAlpha",           "(I)Z", (void*)Bitmap_hasAlpha },\r
787     {   "nativeSetAlphaAndPremultiplied", "(IZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},\r
788     {   "nativeHasMipMap",          "(I)Z", (void*)Bitmap_hasMipMap },\r
789     {   "nativeSetHasMipMap",       "(IZ)V", (void*)Bitmap_setHasMipMap },\r
790     {   "nativeCreateFromParcel",\r
791         "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",\r
792         (void*)Bitmap_createFromParcel },\r
793     {   "nativeWriteToParcel",      "(IZILandroid/os/Parcel;)Z",\r
794         (void*)Bitmap_writeToParcel },\r
795     {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",\r
796         (void*)Bitmap_extractAlpha },\r
797     {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },\r
798     {   "nativeGetPixel",           "(IIIZ)I", (void*)Bitmap_getPixel },\r
799     {   "nativeGetPixels",          "(I[IIIIIIIZ)V", (void*)Bitmap_getPixels },\r
800     {   "nativeSetPixel",           "(IIIIZ)V", (void*)Bitmap_setPixel },\r
801     {   "nativeSetPixels",          "(I[IIIIIIIZ)V", (void*)Bitmap_setPixels },\r
802     {   "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",\r
803                                             (void*)Bitmap_copyPixelsToBuffer },\r
804     {   "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",\r
805                                             (void*)Bitmap_copyPixelsFromBuffer },\r
806     {   "nativeSameAs",             "(II)Z", (void*)Bitmap_sameAs },\r
807     {   "nativePrepareToDraw",      "(I)V", (void*)Bitmap_prepareToDraw },\r
808 };\r
809 \r
810 #define kClassPathName  "android/graphics/Bitmap"\r
811 \r
812 int register_android_graphics_Bitmap(JNIEnv* env)\r
813 {\r
814     return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,\r
815                                 gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));\r
816 }\r