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
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
21 #define TRACE_BITMAP(code) code
\r
23 #define TRACE_BITMAP(code)
\r
26 ///////////////////////////////////////////////////////////////////////////////
\r
27 // Conversions to/from SkColor, for get/setPixels, and the create method, which
\r
28 // is basically like setPixels
\r
30 typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
\r
33 static void FromColor_D32(void* dst, const SkColor src[], int width,
\r
35 SkPMColor* d = (SkPMColor*)dst;
\r
37 for (int i = 0; i < width; i++) {
\r
38 *d++ = SkPreMultiplyColor(*src++);
\r
42 static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,
\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
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
54 *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
\r
55 SkColorGetG(c), SkColorGetB(c));
\r
59 static void FromColor_D565(void* dst, const SkColor src[], int width,
\r
61 uint16_t* d = (uint16_t*)dst;
\r
64 for (int stop = x + width; x < stop; x++) {
\r
66 *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
\r
71 static void FromColor_D4444(void* dst, const SkColor src[], int width,
\r
73 SkPMColor16* d = (SkPMColor16*)dst;
\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
83 static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,
\r
85 SkPMColor16* d = (SkPMColor16*)dst;
\r
87 DITHER_4444_SCAN(y);
\r
88 for (int stop = x + width; x < stop; x++) {
\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
100 static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
\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
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
121 if (NULL == dst || NULL == proc) {
\r
125 const jint* array = env->GetIntArrayElements(srcColors, NULL);
\r
126 const SkColor* src = (const SkColor*)array + srcOffset;
\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
134 dst = (char*)dst + dstBitmap.rowBytes();
\r
137 dstBitmap.notifyPixelsChanged();
\r
139 env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
\r
144 //////////////////// ToColor procs
\r
146 typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
\r
149 static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
\r
151 SkASSERT(width > 0);
\r
152 const SkPMColor* s = (const SkPMColor*)src;
\r
154 *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
\r
155 } while (--width != 0);
\r
158 static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,
\r
160 SkASSERT(width > 0);
\r
161 const SkPMColor* s = (const SkPMColor*)src;
\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
169 static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
\r
171 SkASSERT(width > 0);
\r
172 const SkPMColor* s = (const SkPMColor*)src;
\r
174 SkPMColor c = *s++;
\r
175 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
\r
176 SkGetPackedB32(c));
\r
177 } while (--width != 0);
\r
180 static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
\r
182 SkASSERT(width > 0);
\r
183 const SkPMColor16* s = (const SkPMColor16*)src;
\r
185 *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
\r
186 } while (--width != 0);
\r
189 static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,
\r
191 SkASSERT(width > 0);
\r
192 const SkPMColor16* s = (const SkPMColor16*)src;
\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
200 static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
\r
202 SkASSERT(width > 0);
\r
203 const SkPMColor16* s = (const SkPMColor16*)src;
\r
205 SkPMColor c = SkPixel4444ToPixel32(*s++);
\r
206 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
\r
207 SkGetPackedB32(c));
\r
208 } while (--width != 0);
\r
211 static void ToColor_S565(SkColor dst[], const void* src, int width,
\r
213 SkASSERT(width > 0);
\r
214 const uint16_t* s = (const uint16_t*)src;
\r
217 *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
\r
218 SkPacked16ToB32(c));
\r
219 } while (--width != 0);
\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
228 *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
\r
229 } while (--width != 0);
\r
230 ctable->unlockColors();
\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
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
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
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
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
274 if (src.isOpaque()) return ToColor_SI8_Opaque;
\r
275 return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
\r
282 ///////////////////////////////////////////////////////////////////////////////
\r
283 ///////////////////////////////////////////////////////////////////////////////
\r
285 static int getPremulBitmapCreateFlags(bool isMutable) {
\r
286 int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
\r
287 if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
\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
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
308 bitmap.setConfig(config, width, height);
\r
310 jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
\r
311 if (NULL == buff) {
\r
315 if (jColors != NULL) {
\r
316 GraphicsJNI::SetPixels(env, jColors, offset, stride,
\r
317 0, 0, width, height, bitmap, true);
\r
320 return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
\r
321 getPremulBitmapCreateFlags(isMutable), NULL, NULL);
\r
324 static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
\r
325 SkBitmap::Config dstConfig, jboolean isMutable) {
\r
327 JavaPixelAllocator allocator(env);
\r
329 if (!src->copyTo(&result, dstConfig, &allocator)) {
\r
332 return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
\r
333 getPremulBitmapCreateFlags(isMutable), NULL, NULL);
\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
342 #endif // USE_OPENGL_RENDERER
\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
351 #endif // USE_OPENGL_RENDERER
\r
352 bitmap->setPixels(NULL, NULL);
\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
363 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapInt);
\r
364 SkPixelRef* ref = bitmap->pixelRef();
\r
366 bitmap->setConfig(config, width, height);
\r
367 bitmap->setPixelRef(ref);
\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
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
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
389 case kJPEG_JavaEncodeFormat:
\r
390 fm = SkImageEncoder::kJPEG_Type;
\r
392 case kPNG_JavaEncodeFormat:
\r
393 fm = SkImageEncoder::kPNG_Type;
\r
395 case kWEBP_JavaEncodeFormat:
\r
396 fm = SkImageEncoder::kWEBP_Type;
\r
402 bool success = false;
\r
403 if (NULL != bitmap) {
\r
404 SkAutoLockPixels alp(*bitmap);
\r
406 if (NULL == bitmap->getPixels()) {
\r
410 SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
\r
411 if (NULL == strm) {
\r
415 SkImageEncoder* encoder = SkImageEncoder::Create(fm);
\r
416 if (NULL != encoder) {
\r
417 success = encoder->encodeStream(strm, *bitmap, quality);
\r
425 static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
\r
426 bitmap->eraseColor(color);
\r
429 static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
\r
430 return bitmap->rowBytes();
\r
433 static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
\r
434 return bitmap->config();
\r
437 static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {
\r
438 return bitmap->getGenerationID();
\r
441 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
\r
442 return !bitmap->isOpaque();
\r
445 static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, SkBitmap* bitmap,
\r
446 jboolean hasAlpha, jboolean isPremul) {
\r
448 bitmap->setAlphaType(kOpaque_SkAlphaType);
\r
449 } else if (isPremul) {
\r
450 bitmap->setAlphaType(kPremul_SkAlphaType);
\r
452 bitmap->setAlphaType(kUnpremul_SkAlphaType);
\r
456 static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap) {
\r
457 return bitmap->hasHardwareMipMap();
\r
460 static void Bitmap_setHasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap,
\r
461 jboolean hasMipMap) {
\r
462 bitmap->setHasHardwareMipMap(hasMipMap);
\r
465 ///////////////////////////////////////////////////////////////////////////////
\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
473 android::Parcel* p = android::parcelForJavaObject(env, parcel);
\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
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
491 SkAutoTDelete<SkBitmap> bitmap(new SkBitmap);
\r
493 if (!bitmap->setConfig(config, width, height, rowBytes)) {
\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
506 size_t size = count * sizeof(SkPMColor);
\r
507 const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
\r
511 ctable = new SkColorTable(src, count);
\r
515 jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
\r
516 if (NULL == buffer) {
\r
517 SkSafeUnref(ctable);
\r
521 SkSafeUnref(ctable);
\r
523 size_t size = bitmap->getSize();
\r
525 android::Parcel::ReadableBlob blob;
\r
526 android::status_t status = p->readBlob(size, &blob);
\r
528 doThrowRE(env, "Could not read bitmap from parcel blob.");
\r
532 bitmap->lockPixels();
\r
533 memcpy(bitmap->getPixels(), blob.data(), size);
\r
534 bitmap->unlockPixels();
\r
538 return GraphicsJNI::createBitmap(env, bitmap.detach(), buffer,
\r
539 getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
\r
542 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
\r
543 const SkBitmap* bitmap,
\r
544 jboolean isMutable, jint density,
\r
546 if (parcel == NULL) {
\r
547 SkDebugf("------- writeToParcel null parcel\n");
\r
551 android::Parcel* p = android::parcelForJavaObject(env, parcel);
\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
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
569 p->writeInt32(0); // indicate no ctable
\r
573 size_t size = bitmap->getSize();
\r
575 android::Parcel::WritableBlob blob;
\r
576 android::status_t status = p->writeBlob(size, &blob);
\r
578 doThrowRE(env, "Could not write bitmap to parcel blob.");
\r
582 bitmap->lockPixels();
\r
583 const void* pSrc = bitmap->getPixels();
\r
584 if (pSrc == NULL) {
\r
585 memset(blob.data(), 0, size);
\r
587 memcpy(blob.data(), pSrc, size);
\r
589 bitmap->unlockPixels();
\r
595 static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
\r
596 const SkBitmap* src, const SkPaint* paint,
\r
597 jintArray offsetXY) {
\r
599 SkBitmap* dst = new SkBitmap;
\r
600 JavaPixelAllocator allocator(env);
\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
607 doThrowOOME(env, "failed to allocate pixels for alpha");
\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
617 return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
\r
618 GraphicsJNI::kBitmapCreateFlag_Mutable, NULL, NULL);
\r
621 ///////////////////////////////////////////////////////////////////////////////
\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
627 ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
\r
628 if (NULL == proc) {
\r
631 const void* src = bitmap->getAddr(x, y);
\r
637 proc(dst, src, 1, bitmap->getColorTable());
\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
646 ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
\r
647 if (NULL == proc) {
\r
650 const void* src = bitmap->getAddr(x, y);
\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
661 src = (void*)((const char*)src + bitmap->rowBytes());
\r
663 env->ReleaseIntArrayElements(pixelArray, dst, 0);
\r
666 ///////////////////////////////////////////////////////////////////////////////
\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
675 FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
\r
676 if (NULL == proc) {
\r
680 proc(bitmap->getAddr(x, y), &color, 1, x, y);
\r
681 bitmap->notifyPixelsChanged();
\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
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
697 android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
\r
699 // the java side has already checked that buffer is large enough
\r
700 memcpy(abp.pointer(), src, bitmap->getSize());
\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
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
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
725 SkAutoLockPixels alp0(*bm0);
\r
726 SkAutoLockPixels alp1(*bm1);
\r
728 // if we can't load the pixels, return false
\r
729 if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
\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
739 if (ct0->count() != ct1->count()) {
\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
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
764 static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) {
\r
765 bitmap->lockPixels();
\r
766 bitmap->unlockPixels();
\r
769 ///////////////////////////////////////////////////////////////////////////////
\r
771 #include <android_runtime/AndroidRuntime.h>
\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
810 #define kClassPathName "android/graphics/Bitmap"
\r
812 int register_android_graphics_Bitmap(JNIEnv* env)
\r
814 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
\r
815 gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
\r