Bitmap.cpp revision 17a8bfc38a565ae96f43d36b223779be840bb50c
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 30typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, 31 int x, int y); 32 33static 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 42static 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 59static 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 71static 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 83static 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 100static 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 114bool 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 146typedef void (*ToColorProc)(SkColor dst[], const void* src, int width, 147 SkColorTable*); 148 149static 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 158static 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 169static 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 180static 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 189static 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 200static 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 211static 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 222static 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 233static 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 246static 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 260static 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 285static int getPremulBitmapCreateFlags(bool isMutable) { 286 int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied; 287 if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable; 288 return flags; 289} 290 291static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 292 jint offset, jint stride, jint width, jint height, 293 jint configHandle, jboolean isMutable) { 294 SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle); 295 if (NULL != jColors) { 296 size_t n = env->GetArrayLength(jColors); 297 if (n < SkAbs32(stride) * (size_t)height) { 298 doThrowAIOOBE(env); 299 return NULL; 300 } 301 } 302 303 // ARGB_4444 is a deprecated format, convert automatically to 8888 304 if (config == SkBitmap::kARGB_4444_Config) { 305 config = SkBitmap::kARGB_8888_Config; 306 } 307 308 SkBitmap bitmap; 309 bitmap.setConfig(config, width, height); 310 311 jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); 312 if (NULL == buff) { 313 return NULL; 314 } 315 316 if (jColors != NULL) { 317 GraphicsJNI::SetPixels(env, jColors, offset, stride, 318 0, 0, width, height, bitmap, true); 319 } 320 321 return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, 322 getPremulBitmapCreateFlags(isMutable), NULL, NULL); 323} 324 325static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, 326 jint dstConfigHandle, jboolean isMutable) { 327 const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); 328 SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle); 329 SkBitmap result; 330 JavaPixelAllocator allocator(env); 331 332 if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) { 333 return NULL; 334 } 335 return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), 336 getPremulBitmapCreateFlags(isMutable), NULL, NULL); 337} 338 339static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) { 340 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 341#ifdef USE_OPENGL_RENDERER 342 if (android::uirenderer::Caches::hasInstance()) { 343 android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap); 344 return; 345 } 346#endif // USE_OPENGL_RENDERER 347 delete bitmap; 348} 349 350static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { 351 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 352#ifdef USE_OPENGL_RENDERER 353 if (android::uirenderer::Caches::hasInstance()) { 354 bool result; 355 result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap); 356 return result ? JNI_TRUE : JNI_FALSE; 357 } 358#endif // USE_OPENGL_RENDERER 359 bitmap->setPixels(NULL, NULL); 360 return JNI_TRUE; 361} 362 363static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, 364 jint width, jint height, jint configHandle, jint allocSize, 365 jboolean requestPremul) { 366 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 367 SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle); 368 SkColorType colorType = SkBitmapConfigToColorType(config); 369 370 // ARGB_4444 is a deprecated format, convert automatically to 8888 371 if (colorType == kARGB_4444_SkColorType) { 372 colorType = kN32_SkColorType; 373 } 374 375 if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) { 376 // done in native as there's no way to get BytesPerPixel in Java 377 doThrowIAE(env, "Bitmap not large enough to support new configuration"); 378 return; 379 } 380 SkPixelRef* ref = bitmap->pixelRef(); 381 ref->ref(); 382 SkAlphaType alphaType; 383 if (bitmap->colorType() != kRGB_565_SkColorType 384 && bitmap->alphaType() == kOpaque_SkAlphaType) { 385 // If the original bitmap was set to opaque, keep that setting, unless it 386 // was 565, which is required to be opaque. 387 alphaType = kOpaque_SkAlphaType; 388 } else { 389 // Otherwise respect the premultiplied request. 390 alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; 391 } 392 bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); 393 // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for 394 // its alphatype), so it would make more sense from Skia's perspective to create a 395 // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key 396 // for its cache, so it won't realize this is the same Java Bitmap. 397 SkImageInfo& info = const_cast<SkImageInfo&>(ref->info()); 398 // Use the updated from the SkBitmap, which may have corrected an invalid alphatype. 399 // (e.g. 565 non-opaque) 400 info = bitmap->info(); 401 bitmap->setPixelRef(ref); 402 403 // notifyPixelsChanged will increment the generation ID even though the actual pixel data 404 // hasn't been touched. This signals the renderer that the bitmap (including width, height, 405 // colortype and alphatype) has changed. 406 ref->notifyPixelsChanged(); 407 ref->unref(); 408} 409 410// These must match the int values in Bitmap.java 411enum JavaEncodeFormat { 412 kJPEG_JavaEncodeFormat = 0, 413 kPNG_JavaEncodeFormat = 1, 414 kWEBP_JavaEncodeFormat = 2 415}; 416 417static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, 418 jint format, jint quality, 419 jobject jstream, jbyteArray jstorage) { 420 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 421 SkImageEncoder::Type fm; 422 423 switch (format) { 424 case kJPEG_JavaEncodeFormat: 425 fm = SkImageEncoder::kJPEG_Type; 426 break; 427 case kPNG_JavaEncodeFormat: 428 fm = SkImageEncoder::kPNG_Type; 429 break; 430 case kWEBP_JavaEncodeFormat: 431 fm = SkImageEncoder::kWEBP_Type; 432 break; 433 default: 434 return JNI_FALSE; 435 } 436 437 bool success = false; 438 if (NULL != bitmap) { 439 SkAutoLockPixels alp(*bitmap); 440 441 if (NULL == bitmap->getPixels()) { 442 return JNI_FALSE; 443 } 444 445 SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); 446 if (NULL == strm) { 447 return JNI_FALSE; 448 } 449 450 SkImageEncoder* encoder = SkImageEncoder::Create(fm); 451 if (NULL != encoder) { 452 success = encoder->encodeStream(strm, *bitmap, quality); 453 delete encoder; 454 } 455 delete strm; 456 } 457 return success ? JNI_TRUE : JNI_FALSE; 458} 459 460static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { 461 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 462 bitmap->eraseColor(color); 463} 464 465static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { 466 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 467 return static_cast<jint>(bitmap->rowBytes()); 468} 469 470static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { 471 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 472 return static_cast<jint>(bitmap->config()); 473} 474 475static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { 476 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 477 return static_cast<jint>(bitmap->getGenerationID()); 478} 479 480static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { 481 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 482 return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE; 483} 484 485static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, 486 jboolean hasAlpha, jboolean isPremul) { 487 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 488 if (!hasAlpha) { 489 bitmap->setAlphaType(kOpaque_SkAlphaType); 490 } else if (isPremul) { 491 bitmap->setAlphaType(kPremul_SkAlphaType); 492 } else { 493 bitmap->setAlphaType(kUnpremul_SkAlphaType); 494 } 495} 496 497static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { 498 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 499 return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; 500} 501 502static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, 503 jboolean hasMipMap) { 504 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 505 bitmap->setHasHardwareMipMap(hasMipMap); 506} 507 508/////////////////////////////////////////////////////////////////////////////// 509 510static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { 511 if (parcel == NULL) { 512 SkDebugf("-------- unparcel parcel is NULL\n"); 513 return NULL; 514 } 515 516 android::Parcel* p = android::parcelForJavaObject(env, parcel); 517 518 const bool isMutable = p->readInt32() != 0; 519 const SkBitmap::Config config = (SkBitmap::Config)p->readInt32(); 520 const int width = p->readInt32(); 521 const int height = p->readInt32(); 522 const int rowBytes = p->readInt32(); 523 const int density = p->readInt32(); 524 525 if (SkBitmap::kARGB_8888_Config != config && 526 SkBitmap::kRGB_565_Config != config && 527 SkBitmap::kARGB_4444_Config != config && 528 SkBitmap::kIndex8_Config != config && 529 SkBitmap::kA8_Config != config) { 530 SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config); 531 return NULL; 532 } 533 534 SkBitmap* bitmap = new SkBitmap; 535 536 bitmap->setConfig(config, width, height, rowBytes); 537 538 SkColorTable* ctable = NULL; 539 if (config == SkBitmap::kIndex8_Config) { 540 int count = p->readInt32(); 541 if (count > 0) { 542 size_t size = count * sizeof(SkPMColor); 543 const SkPMColor* src = (const SkPMColor*)p->readInplace(size); 544 ctable = new SkColorTable(src, count); 545 } 546 } 547 548 jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); 549 if (NULL == buffer) { 550 SkSafeUnref(ctable); 551 delete bitmap; 552 return NULL; 553 } 554 555 SkSafeUnref(ctable); 556 557 size_t size = bitmap->getSize(); 558 559 android::Parcel::ReadableBlob blob; 560 android::status_t status = p->readBlob(size, &blob); 561 if (status) { 562 doThrowRE(env, "Could not read bitmap from parcel blob."); 563 delete bitmap; 564 return NULL; 565 } 566 567 bitmap->lockPixels(); 568 memcpy(bitmap->getPixels(), blob.data(), size); 569 bitmap->unlockPixels(); 570 571 blob.release(); 572 573 return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable), 574 NULL, NULL, density); 575} 576 577static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, 578 jlong bitmapHandle, 579 jboolean isMutable, jint density, 580 jobject parcel) { 581 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 582 if (parcel == NULL) { 583 SkDebugf("------- writeToParcel null parcel\n"); 584 return JNI_FALSE; 585 } 586 587 android::Parcel* p = android::parcelForJavaObject(env, parcel); 588 589 p->writeInt32(isMutable); 590 p->writeInt32(bitmap->config()); 591 p->writeInt32(bitmap->width()); 592 p->writeInt32(bitmap->height()); 593 p->writeInt32(bitmap->rowBytes()); 594 p->writeInt32(density); 595 596 if (bitmap->config() == SkBitmap::kIndex8_Config) { 597 SkColorTable* ctable = bitmap->getColorTable(); 598 if (ctable != NULL) { 599 int count = ctable->count(); 600 p->writeInt32(count); 601 memcpy(p->writeInplace(count * sizeof(SkPMColor)), 602 ctable->lockColors(), count * sizeof(SkPMColor)); 603 ctable->unlockColors(); 604 } else { 605 p->writeInt32(0); // indicate no ctable 606 } 607 } 608 609 size_t size = bitmap->getSize(); 610 611 android::Parcel::WritableBlob blob; 612 android::status_t status = p->writeBlob(size, &blob); 613 if (status) { 614 doThrowRE(env, "Could not write bitmap to parcel blob."); 615 return JNI_FALSE; 616 } 617 618 bitmap->lockPixels(); 619 const void* pSrc = bitmap->getPixels(); 620 if (pSrc == NULL) { 621 memset(blob.data(), 0, size); 622 } else { 623 memcpy(blob.data(), pSrc, size); 624 } 625 bitmap->unlockPixels(); 626 627 blob.release(); 628 return JNI_TRUE; 629} 630 631static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, 632 jlong srcHandle, jlong paintHandle, 633 jintArray offsetXY) { 634 const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); 635 const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); 636 SkIPoint offset; 637 SkBitmap* dst = new SkBitmap; 638 JavaPixelAllocator allocator(env); 639 640 src->extractAlpha(dst, paint, &allocator, &offset); 641 // If Skia can't allocate pixels for destination bitmap, it resets 642 // it, that is set its pixels buffer to NULL, and zero width and height. 643 if (dst->getPixels() == NULL && src->getPixels() != NULL) { 644 delete dst; 645 doThrowOOME(env, "failed to allocate pixels for alpha"); 646 return NULL; 647 } 648 if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { 649 int* array = env->GetIntArrayElements(offsetXY, NULL); 650 array[0] = offset.fX; 651 array[1] = offset.fY; 652 env->ReleaseIntArrayElements(offsetXY, array, 0); 653 } 654 655 return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), 656 getPremulBitmapCreateFlags(true), NULL, NULL); 657} 658 659/////////////////////////////////////////////////////////////////////////////// 660 661static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, 662 jint x, jint y, jboolean isPremultiplied) { 663 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 664 SkAutoLockPixels alp(*bitmap); 665 666 ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied); 667 if (NULL == proc) { 668 return 0; 669 } 670 const void* src = bitmap->getAddr(x, y); 671 if (NULL == src) { 672 return 0; 673 } 674 675 SkColor dst[1]; 676 proc(dst, src, 1, bitmap->getColorTable()); 677 return static_cast<jint>(dst[0]); 678} 679 680static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, 681 jintArray pixelArray, jint offset, jint stride, 682 jint x, jint y, jint width, jint height, jboolean isPremultiplied) { 683 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 684 SkAutoLockPixels alp(*bitmap); 685 686 ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied); 687 if (NULL == proc) { 688 return; 689 } 690 const void* src = bitmap->getAddr(x, y); 691 if (NULL == src) { 692 return; 693 } 694 695 SkColorTable* ctable = bitmap->getColorTable(); 696 jint* dst = env->GetIntArrayElements(pixelArray, NULL); 697 SkColor* d = (SkColor*)dst + offset; 698 while (--height >= 0) { 699 proc(d, src, width, ctable); 700 d += stride; 701 src = (void*)((const char*)src + bitmap->rowBytes()); 702 } 703 env->ReleaseIntArrayElements(pixelArray, dst, 0); 704} 705 706/////////////////////////////////////////////////////////////////////////////// 707 708static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, 709 jint x, jint y, jint colorHandle, jboolean isPremultiplied) { 710 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 711 SkColor color = static_cast<SkColor>(colorHandle); 712 SkAutoLockPixels alp(*bitmap); 713 if (NULL == bitmap->getPixels()) { 714 return; 715 } 716 717 FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied); 718 if (NULL == proc) { 719 return; 720 } 721 722 proc(bitmap->getAddr(x, y), &color, 1, x, y); 723 bitmap->notifyPixelsChanged(); 724} 725 726static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, 727 jintArray pixelArray, jint offset, jint stride, 728 jint x, jint y, jint width, jint height, jboolean isPremultiplied) { 729 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 730 GraphicsJNI::SetPixels(env, pixelArray, offset, stride, 731 x, y, width, height, *bitmap, isPremultiplied); 732} 733 734static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, 735 jlong bitmapHandle, jobject jbuffer) { 736 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 737 SkAutoLockPixels alp(*bitmap); 738 const void* src = bitmap->getPixels(); 739 740 if (NULL != src) { 741 android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); 742 743 // the java side has already checked that buffer is large enough 744 memcpy(abp.pointer(), src, bitmap->getSize()); 745 } 746} 747 748static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, 749 jlong bitmapHandle, jobject jbuffer) { 750 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 751 SkAutoLockPixels alp(*bitmap); 752 void* dst = bitmap->getPixels(); 753 754 if (NULL != dst) { 755 android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); 756 // the java side has already checked that buffer is large enough 757 memcpy(dst, abp.pointer(), bitmap->getSize()); 758 bitmap->notifyPixelsChanged(); 759 } 760} 761 762static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, 763 jlong bm1Handle) { 764 const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle); 765 const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle); 766 if (bm0->width() != bm1->width() || 767 bm0->height() != bm1->height() || 768 bm0->config() != bm1->config()) { 769 return JNI_FALSE; 770 } 771 772 SkAutoLockPixels alp0(*bm0); 773 SkAutoLockPixels alp1(*bm1); 774 775 // if we can't load the pixels, return false 776 if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) { 777 return JNI_FALSE; 778 } 779 780 if (bm0->config() == SkBitmap::kIndex8_Config) { 781 SkColorTable* ct0 = bm0->getColorTable(); 782 SkColorTable* ct1 = bm1->getColorTable(); 783 if (NULL == ct0 || NULL == ct1) { 784 return JNI_FALSE; 785 } 786 if (ct0->count() != ct1->count()) { 787 return JNI_FALSE; 788 } 789 790 SkAutoLockColors alc0(ct0); 791 SkAutoLockColors alc1(ct1); 792 const size_t size = ct0->count() * sizeof(SkPMColor); 793 if (memcmp(alc0.colors(), alc1.colors(), size) != 0) { 794 return JNI_FALSE; 795 } 796 } 797 798 // now compare each scanline. We can't do the entire buffer at once, 799 // since we don't care about the pixel values that might extend beyond 800 // the width (since the scanline might be larger than the logical width) 801 const int h = bm0->height(); 802 const size_t size = bm0->width() * bm0->bytesPerPixel(); 803 for (int y = 0; y < h; y++) { 804 if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) { 805 return JNI_FALSE; 806 } 807 } 808 return JNI_TRUE; 809} 810 811static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) { 812 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 813 bitmap->lockPixels(); 814 bitmap->unlockPixels(); 815} 816 817/////////////////////////////////////////////////////////////////////////////// 818 819#include <android_runtime/AndroidRuntime.h> 820 821static JNINativeMethod gBitmapMethods[] = { 822 { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;", 823 (void*)Bitmap_creator }, 824 { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", 825 (void*)Bitmap_copy }, 826 { "nativeDestructor", "(J)V", (void*)Bitmap_destructor }, 827 { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle }, 828 { "nativeReconfigure", "(JIIIIZ)V", (void*)Bitmap_reconfigure }, 829 { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", 830 (void*)Bitmap_compress }, 831 { "nativeErase", "(JI)V", (void*)Bitmap_erase }, 832 { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, 833 { "nativeConfig", "(J)I", (void*)Bitmap_config }, 834 { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, 835 { "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied}, 836 { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, 837 { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, 838 { "nativeCreateFromParcel", 839 "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", 840 (void*)Bitmap_createFromParcel }, 841 { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z", 842 (void*)Bitmap_writeToParcel }, 843 { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", 844 (void*)Bitmap_extractAlpha }, 845 { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId }, 846 { "nativeGetPixel", "(JIIZ)I", (void*)Bitmap_getPixel }, 847 { "nativeGetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels }, 848 { "nativeSetPixel", "(JIIIZ)V", (void*)Bitmap_setPixel }, 849 { "nativeSetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels }, 850 { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", 851 (void*)Bitmap_copyPixelsToBuffer }, 852 { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", 853 (void*)Bitmap_copyPixelsFromBuffer }, 854 { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, 855 { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, 856}; 857 858#define kClassPathName "android/graphics/Bitmap" 859 860int register_android_graphics_Bitmap(JNIEnv* env) 861{ 862 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 863 gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods)); 864} 865