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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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(false); 231 } 232 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(false); 244 } 245 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(false); 257 } 258 259 // can return NULL 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 285 static int getPremulBitmapCreateFlags(bool isMutable) { 286 int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied; 287 if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable; 288 return flags; 289 } 290 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 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 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 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 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 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 425 static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) { 426 bitmap->eraseColor(color); 427 } 428 429 static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) { 430 return bitmap->rowBytes(); 431 } 432 433 static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) { 434 return bitmap->config(); 435 } 436 437 static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) { 438 return bitmap->getGenerationID(); 439 } 440 441 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) { 442 return !bitmap->isOpaque(); 443 } 444 445 static void Bitmap_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap, 446 jboolean hasAlpha) { 447 bitmap->setIsOpaque(!hasAlpha); 448 } 449 450 static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap) { 451 return bitmap->hasHardwareMipMap(); 452 } 453 454 static void Bitmap_setHasMipMap(JNIEnv* env, jobject, SkBitmap* bitmap, 455 jboolean hasMipMap) { 456 bitmap->setHasHardwareMipMap(hasMipMap); 457 } 458 459 /////////////////////////////////////////////////////////////////////////////// 460 461 static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { 462 if (parcel == NULL) { 463 SkDebugf("-------- unparcel parcel is NULL\n"); 464 return NULL; 465 } 466 467 android::Parcel* p = android::parcelForJavaObject(env, parcel); 468 469 const bool isMutable = p->readInt32() != 0; 470 const SkBitmap::Config config = (SkBitmap::Config)p->readInt32(); 471 const int width = p->readInt32(); 472 const int height = p->readInt32(); 473 const int rowBytes = p->readInt32(); 474 const int density = p->readInt32(); 475 476 if (SkBitmap::kARGB_8888_Config != config && 477 SkBitmap::kRGB_565_Config != config && 478 SkBitmap::kARGB_4444_Config != config && 479 SkBitmap::kIndex8_Config != config && 480 SkBitmap::kA8_Config != config) { 481 SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config); 482 return NULL; 483 } 484 485 SkBitmap* bitmap = new SkBitmap; 486 487 bitmap->setConfig(config, width, height, rowBytes); 488 489 SkColorTable* ctable = NULL; 490 if (config == SkBitmap::kIndex8_Config) { 491 int count = p->readInt32(); 492 if (count > 0) { 493 size_t size = count * sizeof(SkPMColor); 494 const SkPMColor* src = (const SkPMColor*)p->readInplace(size); 495 ctable = new SkColorTable(src, count); 496 } 497 } 498 499 jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); 500 if (NULL == buffer) { 501 SkSafeUnref(ctable); 502 delete bitmap; 503 return NULL; 504 } 505 506 SkSafeUnref(ctable); 507 508 size_t size = bitmap->getSize(); 509 510 android::Parcel::ReadableBlob blob; 511 android::status_t status = p->readBlob(size, &blob); 512 if (status) { 513 doThrowRE(env, "Could not read bitmap from parcel blob."); 514 delete bitmap; 515 return NULL; 516 } 517 518 bitmap->lockPixels(); 519 memcpy(bitmap->getPixels(), blob.data(), size); 520 bitmap->unlockPixels(); 521 522 blob.release(); 523 524 return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable), 525 NULL, NULL, density); 526 } 527 528 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, 529 const SkBitmap* bitmap, 530 jboolean isMutable, jint density, 531 jobject parcel) { 532 if (parcel == NULL) { 533 SkDebugf("------- writeToParcel null parcel\n"); 534 return false; 535 } 536 537 android::Parcel* p = android::parcelForJavaObject(env, parcel); 538 539 p->writeInt32(isMutable); 540 p->writeInt32(bitmap->config()); 541 p->writeInt32(bitmap->width()); 542 p->writeInt32(bitmap->height()); 543 p->writeInt32(bitmap->rowBytes()); 544 p->writeInt32(density); 545 546 if (bitmap->getConfig() == SkBitmap::kIndex8_Config) { 547 SkColorTable* ctable = bitmap->getColorTable(); 548 if (ctable != NULL) { 549 int count = ctable->count(); 550 p->writeInt32(count); 551 memcpy(p->writeInplace(count * sizeof(SkPMColor)), 552 ctable->lockColors(), count * sizeof(SkPMColor)); 553 ctable->unlockColors(false); 554 } else { 555 p->writeInt32(0); // indicate no ctable 556 } 557 } 558 559 size_t size = bitmap->getSize(); 560 561 android::Parcel::WritableBlob blob; 562 android::status_t status = p->writeBlob(size, &blob); 563 if (status) { 564 doThrowRE(env, "Could not write bitmap to parcel blob."); 565 return false; 566 } 567 568 bitmap->lockPixels(); 569 const void* pSrc = bitmap->getPixels(); 570 if (pSrc == NULL) { 571 memset(blob.data(), 0, size); 572 } else { 573 memcpy(blob.data(), pSrc, size); 574 } 575 bitmap->unlockPixels(); 576 577 blob.release(); 578 return true; 579 } 580 581 static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, 582 const SkBitmap* src, const SkPaint* paint, 583 jintArray offsetXY) { 584 SkIPoint offset; 585 SkBitmap* dst = new SkBitmap; 586 JavaPixelAllocator allocator(env); 587 588 src->extractAlpha(dst, paint, &allocator, &offset); 589 // If Skia can't allocate pixels for destination bitmap, it resets 590 // it, that is set its pixels buffer to NULL, and zero width and height. 591 if (dst->getPixels() == NULL && src->getPixels() != NULL) { 592 delete dst; 593 doThrowOOME(env, "failed to allocate pixels for alpha"); 594 return NULL; 595 } 596 if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { 597 int* array = env->GetIntArrayElements(offsetXY, NULL); 598 array[0] = offset.fX; 599 array[1] = offset.fY; 600 env->ReleaseIntArrayElements(offsetXY, array, 0); 601 } 602 603 return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), 604 GraphicsJNI::kBitmapCreateFlag_Mutable, NULL, NULL); 605 } 606 607 /////////////////////////////////////////////////////////////////////////////// 608 609 static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, 610 int x, int y, bool isPremultiplied) { 611 SkAutoLockPixels alp(*bitmap); 612 613 ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied); 614 if (NULL == proc) { 615 return 0; 616 } 617 const void* src = bitmap->getAddr(x, y); 618 if (NULL == src) { 619 return 0; 620 } 621 622 SkColor dst[1]; 623 proc(dst, src, 1, bitmap->getColorTable()); 624 return dst[0]; 625 } 626 627 static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, 628 jintArray pixelArray, int offset, int stride, 629 int x, int y, int width, int height, bool isPremultiplied) { 630 SkAutoLockPixels alp(*bitmap); 631 632 ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied); 633 if (NULL == proc) { 634 return; 635 } 636 const void* src = bitmap->getAddr(x, y); 637 if (NULL == src) { 638 return; 639 } 640 641 SkColorTable* ctable = bitmap->getColorTable(); 642 jint* dst = env->GetIntArrayElements(pixelArray, NULL); 643 SkColor* d = (SkColor*)dst + offset; 644 while (--height >= 0) { 645 proc(d, src, width, ctable); 646 d += stride; 647 src = (void*)((const char*)src + bitmap->rowBytes()); 648 } 649 env->ReleaseIntArrayElements(pixelArray, dst, 0); 650 } 651 652 /////////////////////////////////////////////////////////////////////////////// 653 654 static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, 655 int x, int y, SkColor color, bool isPremultiplied) { 656 SkAutoLockPixels alp(*bitmap); 657 if (NULL == bitmap->getPixels()) { 658 return; 659 } 660 661 FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied); 662 if (NULL == proc) { 663 return; 664 } 665 666 proc(bitmap->getAddr(x, y), &color, 1, x, y); 667 bitmap->notifyPixelsChanged(); 668 } 669 670 static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, 671 jintArray pixelArray, int offset, int stride, 672 int x, int y, int width, int height, bool isPremultiplied) { 673 GraphicsJNI::SetPixels(env, pixelArray, offset, stride, 674 x, y, width, height, *bitmap, isPremultiplied); 675 } 676 677 static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, 678 const SkBitmap* bitmap, jobject jbuffer) { 679 SkAutoLockPixels alp(*bitmap); 680 const void* src = bitmap->getPixels(); 681 682 if (NULL != src) { 683 android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); 684 685 // the java side has already checked that buffer is large enough 686 memcpy(abp.pointer(), src, bitmap->getSize()); 687 } 688 } 689 690 static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, 691 const SkBitmap* bitmap, jobject jbuffer) { 692 SkAutoLockPixels alp(*bitmap); 693 void* dst = bitmap->getPixels(); 694 695 if (NULL != dst) { 696 android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); 697 // the java side has already checked that buffer is large enough 698 memcpy(dst, abp.pointer(), bitmap->getSize()); 699 bitmap->notifyPixelsChanged(); 700 } 701 } 702 703 static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0, 704 const SkBitmap* bm1) { 705 if (bm0->width() != bm1->width() || 706 bm0->height() != bm1->height() || 707 bm0->config() != bm1->config()) { 708 return false; 709 } 710 711 SkAutoLockPixels alp0(*bm0); 712 SkAutoLockPixels alp1(*bm1); 713 714 // if we can't load the pixels, return false 715 if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) { 716 return false; 717 } 718 719 if (bm0->config() == SkBitmap::kIndex8_Config) { 720 SkColorTable* ct0 = bm0->getColorTable(); 721 SkColorTable* ct1 = bm1->getColorTable(); 722 if (NULL == ct0 || NULL == ct1) { 723 return false; 724 } 725 if (ct0->count() != ct1->count()) { 726 return false; 727 } 728 729 SkAutoLockColors alc0(ct0); 730 SkAutoLockColors alc1(ct1); 731 const size_t size = ct0->count() * sizeof(SkPMColor); 732 if (memcmp(alc0.colors(), alc1.colors(), size) != 0) { 733 return false; 734 } 735 } 736 737 // now compare each scanline. We can't do the entire buffer at once, 738 // since we don't care about the pixel values that might extend beyond 739 // the width (since the scanline might be larger than the logical width) 740 const int h = bm0->height(); 741 const size_t size = bm0->width() * bm0->bytesPerPixel(); 742 for (int y = 0; y < h; y++) { 743 if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) { 744 return false; 745 } 746 } 747 return true; 748 } 749 750 static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) { 751 bitmap->lockPixels(); 752 bitmap->unlockPixels(); 753 } 754 755 /////////////////////////////////////////////////////////////////////////////// 756 757 #include <android_runtime/AndroidRuntime.h> 758 759 static JNINativeMethod gBitmapMethods[] = { 760 { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;", 761 (void*)Bitmap_creator }, 762 { "nativeCopy", "(IIZ)Landroid/graphics/Bitmap;", 763 (void*)Bitmap_copy }, 764 { "nativeDestructor", "(I)V", (void*)Bitmap_destructor }, 765 { "nativeRecycle", "(I)Z", (void*)Bitmap_recycle }, 766 { "nativeReconfigure", "(IIIII)V", (void*)Bitmap_reconfigure }, 767 { "nativeCompress", "(IIILjava/io/OutputStream;[B)Z", 768 (void*)Bitmap_compress }, 769 { "nativeErase", "(II)V", (void*)Bitmap_erase }, 770 { "nativeRowBytes", "(I)I", (void*)Bitmap_rowBytes }, 771 { "nativeConfig", "(I)I", (void*)Bitmap_config }, 772 { "nativeHasAlpha", "(I)Z", (void*)Bitmap_hasAlpha }, 773 { "nativeSetHasAlpha", "(IZ)V", (void*)Bitmap_setHasAlpha }, 774 { "nativeHasMipMap", "(I)Z", (void*)Bitmap_hasMipMap }, 775 { "nativeSetHasMipMap", "(IZ)V", (void*)Bitmap_setHasMipMap }, 776 { "nativeCreateFromParcel", 777 "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", 778 (void*)Bitmap_createFromParcel }, 779 { "nativeWriteToParcel", "(IZILandroid/os/Parcel;)Z", 780 (void*)Bitmap_writeToParcel }, 781 { "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;", 782 (void*)Bitmap_extractAlpha }, 783 { "nativeGenerationId", "(I)I", (void*)Bitmap_getGenerationId }, 784 { "nativeGetPixel", "(IIIZ)I", (void*)Bitmap_getPixel }, 785 { "nativeGetPixels", "(I[IIIIIIIZ)V", (void*)Bitmap_getPixels }, 786 { "nativeSetPixel", "(IIIIZ)V", (void*)Bitmap_setPixel }, 787 { "nativeSetPixels", "(I[IIIIIIIZ)V", (void*)Bitmap_setPixels }, 788 { "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V", 789 (void*)Bitmap_copyPixelsToBuffer }, 790 { "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V", 791 (void*)Bitmap_copyPixelsFromBuffer }, 792 { "nativeSameAs", "(II)Z", (void*)Bitmap_sameAs }, 793 { "nativePrepareToDraw", "(I)V", (void*)Bitmap_prepareToDraw }, 794 }; 795 796 #define kClassPathName "android/graphics/Bitmap" 797 798 int register_android_graphics_Bitmap(JNIEnv* env) 799 { 800 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 801 gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods)); 802 } 803