Bitmap.cpp revision 2a94a10bec186d832c2b95675cb6dc27b012c2d0
1#define LOG_TAG "Bitmap" 2 3#include "Bitmap.h" 4 5#include "Paint.h" 6#include "SkBitmap.h" 7#include "SkPixelRef.h" 8#include "SkImageEncoder.h" 9#include "SkImageInfo.h" 10#include "SkColorPriv.h" 11#include "GraphicsJNI.h" 12#include "SkDither.h" 13#include "SkUnPreMultiply.h" 14#include "SkStream.h" 15 16#include <binder/Parcel.h> 17#include "android_os_Parcel.h" 18#include "android_util_Binder.h" 19#include "android_nio_utils.h" 20#include "CreateJavaOutputStreamAdaptor.h" 21#include <Caches.h> 22 23#include "core_jni_helpers.h" 24 25#include <jni.h> 26 27namespace android { 28 29class WrappedPixelRef : public SkPixelRef { 30public: 31 WrappedPixelRef(Bitmap* wrapper, void* storage, 32 const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) 33 : SkPixelRef(info) 34 , mBitmap(*wrapper) 35 , mStorage(storage) { 36 reconfigure(info, rowBytes, ctable); 37 } 38 39 ~WrappedPixelRef() { 40 // Tell SkRefCnt that everything is as it expects by forcing 41 // the refcnt to 1 42 internal_dispose_restore_refcnt_to_1(); 43 SkSafeUnref(mColorTable); 44 } 45 46 void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) { 47 if (kIndex_8_SkColorType != info.colorType()) { 48 ctable = nullptr; 49 } 50 mRowBytes = rowBytes; 51 if (mColorTable != ctable) { 52 SkSafeUnref(mColorTable); 53 mColorTable = ctable; 54 SkSafeRef(mColorTable); 55 } 56 // Dirty hack is dirty 57 // TODO: Figure something out here, Skia's current design makes this 58 // really hard to work with. Skia really, really wants immutable objects, 59 // but with the nested-ref-count hackery going on that's just not 60 // feasible without going insane trying to figure it out 61 SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info()); 62 *myInfo = info; 63 64 // Docs say to only call this in the ctor, but we're going to call 65 // it anyway even if this isn't always the ctor. 66 // TODO: Fix this too as part of the above TODO 67 setPreLocked(mStorage, mRowBytes, mColorTable); 68 } 69 70 // Can't mark as override since SkPixelRef::rowBytes isn't virtual 71 // but that's OK since we just want BitmapWrapper to be able to rely 72 // on calling rowBytes() on an unlocked pixelref, which it will be 73 // doing on a WrappedPixelRef type, not a SkPixelRef, so static 74 // dispatching will do what we want. 75 size_t rowBytes() const { return mRowBytes; } 76 SkColorTable* colorTable() const { return mColorTable; } 77 78 bool hasHardwareMipMap() const { 79 return mHasHardwareMipMap; 80 } 81 82 void setHasHardwareMipMap(bool hasMipMap) { 83 mHasHardwareMipMap = hasMipMap; 84 } 85 86protected: 87 virtual bool onNewLockPixels(LockRec* rec) override { 88 rec->fPixels = mStorage; 89 rec->fRowBytes = mRowBytes; 90 rec->fColorTable = mColorTable; 91 return true; 92 } 93 94 virtual void onUnlockPixels() override { 95 // nothing 96 } 97 98 virtual size_t getAllocatedSizeInBytes() const override { 99 return info().getSafeSize(mRowBytes); 100 } 101 102private: 103 Bitmap& mBitmap; 104 void* mStorage; 105 size_t mRowBytes = 0; 106 SkColorTable* mColorTable = nullptr; 107 bool mHasHardwareMipMap = false; 108 109 virtual void internal_dispose() const override { 110 mBitmap.onStrongRefDestroyed(); 111 } 112}; 113 114Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address, 115 const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) 116 : mPixelStorageType(PixelStorageType::Java) { 117 env->GetJavaVM(&mPixelStorage.java.jvm); 118 mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj); 119 mPixelStorage.java.jstrongRef = nullptr; 120 mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable)); 121 // Note: this will trigger a call to onStrongRefDestroyed(), but 122 // we want the pixel ref to have a ref count of 0 at this point 123 mPixelRef->unref(); 124} 125 126Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, 127 const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) 128 : mPixelStorageType(PixelStorageType::External) { 129 mPixelStorage.external.address = address; 130 mPixelStorage.external.context = context; 131 mPixelStorage.external.freeFunc = freeFunc; 132 mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable)); 133 // Note: this will trigger a call to onStrongRefDestroyed(), but 134 // we want the pixel ref to have a ref count of 0 at this point 135 mPixelRef->unref(); 136} 137 138Bitmap::~Bitmap() { 139 doFreePixels(); 140} 141 142void Bitmap::freePixels() { 143 AutoMutex _lock(mLock); 144 if (mPinnedRefCount == 0) { 145 doFreePixels(); 146 mPixelStorageType = PixelStorageType::Invalid; 147 } 148} 149 150void Bitmap::doFreePixels() { 151 switch (mPixelStorageType) { 152 case PixelStorageType::Invalid: 153 // already free'd, nothing to do 154 break; 155 case PixelStorageType::External: 156 mPixelStorage.external.freeFunc(mPixelStorage.external.address, 157 mPixelStorage.external.context); 158 break; 159 case PixelStorageType::Java: 160 JNIEnv* env = jniEnv(); 161 LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef, 162 "Deleting a bitmap wrapper while there are outstanding strong " 163 "references! mPinnedRefCount = %d", mPinnedRefCount); 164 env->DeleteWeakGlobalRef(mPixelStorage.java.jweakRef); 165 break; 166 } 167 168 if (android::uirenderer::Caches::hasInstance()) { 169 android::uirenderer::Caches::getInstance().textureCache.releaseTexture( 170 mPixelRef->getStableID()); 171 } 172} 173 174bool Bitmap::hasHardwareMipMap() { 175 return mPixelRef->hasHardwareMipMap(); 176} 177 178void Bitmap::setHasHardwareMipMap(bool hasMipMap) { 179 mPixelRef->setHasHardwareMipMap(hasMipMap); 180} 181 182const SkImageInfo& Bitmap::info() const { 183 assertValid(); 184 return mPixelRef->info(); 185} 186 187size_t Bitmap::rowBytes() const { 188 return mPixelRef->rowBytes(); 189} 190 191SkPixelRef* Bitmap::peekAtPixelRef() const { 192 assertValid(); 193 return mPixelRef.get(); 194} 195 196SkPixelRef* Bitmap::refPixelRef() { 197 assertValid(); 198 android::AutoMutex _lock(mLock); 199 return refPixelRefLocked(); 200} 201 202SkPixelRef* Bitmap::refPixelRefLocked() { 203 mPixelRef->ref(); 204 if (mPixelRef->unique()) { 205 // We just restored this from 0, pin the pixels and inc the strong count 206 // Note that there *might be* an incoming onStrongRefDestroyed from whatever 207 // last unref'd 208 pinPixelsLocked(); 209 mPinnedRefCount++; 210 } 211 return mPixelRef.get(); 212} 213 214void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes, 215 SkColorTable* ctable) { 216 { 217 android::AutoMutex _lock(mLock); 218 if (mPinnedRefCount) { 219 ALOGW("Called reconfigure on a bitmap that is in use! This may" 220 " cause graphical corruption!"); 221 } 222 } 223 mPixelRef->reconfigure(info, rowBytes, ctable); 224} 225 226void Bitmap::reconfigure(const SkImageInfo& info) { 227 reconfigure(info, info.minRowBytes(), nullptr); 228} 229 230void Bitmap::detachFromJava() { 231 bool disposeSelf; 232 { 233 android::AutoMutex _lock(mLock); 234 mAttachedToJava = false; 235 disposeSelf = shouldDisposeSelfLocked(); 236 } 237 if (disposeSelf) { 238 delete this; 239 } 240} 241 242bool Bitmap::shouldDisposeSelfLocked() { 243 return mPinnedRefCount == 0 && !mAttachedToJava; 244} 245 246JNIEnv* Bitmap::jniEnv() { 247 JNIEnv* env; 248 auto success = mPixelStorage.java.jvm->GetEnv((void**)&env, JNI_VERSION_1_6); 249 LOG_ALWAYS_FATAL_IF(success != JNI_OK, 250 "Failed to get JNIEnv* from JVM: %p", mPixelStorage.java.jvm); 251 return env; 252} 253 254void Bitmap::onStrongRefDestroyed() { 255 bool disposeSelf = false; 256 { 257 android::AutoMutex _lock(mLock); 258 if (mPinnedRefCount > 0) { 259 mPinnedRefCount--; 260 if (mPinnedRefCount == 0) { 261 unpinPixelsLocked(); 262 disposeSelf = shouldDisposeSelfLocked(); 263 } 264 } 265 } 266 if (disposeSelf) { 267 delete this; 268 } 269} 270 271void Bitmap::pinPixelsLocked() { 272 switch (mPixelStorageType) { 273 case PixelStorageType::Invalid: 274 LOG_ALWAYS_FATAL("Cannot pin invalid pixels!"); 275 break; 276 case PixelStorageType::External: 277 // Nothing to do 278 break; 279 case PixelStorageType::Java: { 280 JNIEnv* env = jniEnv(); 281 if (!mPixelStorage.java.jstrongRef) { 282 mPixelStorage.java.jstrongRef = reinterpret_cast<jbyteArray>( 283 env->NewGlobalRef(mPixelStorage.java.jweakRef)); 284 if (!mPixelStorage.java.jstrongRef) { 285 LOG_ALWAYS_FATAL("Failed to acquire strong reference to pixels"); 286 } 287 } 288 break; 289 } 290 } 291} 292 293void Bitmap::unpinPixelsLocked() { 294 switch (mPixelStorageType) { 295 case PixelStorageType::Invalid: 296 LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!"); 297 break; 298 case PixelStorageType::External: 299 // Don't need to do anything 300 break; 301 case PixelStorageType::Java: { 302 JNIEnv* env = jniEnv(); 303 if (mPixelStorage.java.jstrongRef) { 304 env->DeleteGlobalRef(mPixelStorage.java.jstrongRef); 305 mPixelStorage.java.jstrongRef = nullptr; 306 } 307 break; 308 } 309 } 310} 311 312void Bitmap::getSkBitmap(SkBitmap* outBitmap) { 313 assertValid(); 314 android::AutoMutex _lock(mLock); 315 // Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes() 316 // would require locking the pixels first. 317 outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes()); 318 outBitmap->setPixelRef(refPixelRefLocked())->unref(); 319 outBitmap->setHasHardwareMipMap(hasHardwareMipMap()); 320} 321 322void Bitmap::assertValid() const { 323 LOG_ALWAYS_FATAL_IF(mPixelStorageType == PixelStorageType::Invalid, 324 "Error, cannot access an invalid/free'd bitmap here!"); 325} 326 327} // namespace android 328 329using namespace android; 330 331// Convenience class that does not take a global ref on the pixels, relying 332// on the caller already having a local JNI ref 333class LocalScopedBitmap { 334public: 335 LocalScopedBitmap(jlong bitmapHandle) 336 : mBitmap(reinterpret_cast<Bitmap*>(bitmapHandle)) {} 337 338 Bitmap* operator->() { 339 return mBitmap; 340 } 341 342 void* pixels() { 343 return mBitmap->peekAtPixelRef()->pixels(); 344 } 345 346 bool valid() { 347 return mBitmap && mBitmap->valid(); 348 } 349 350private: 351 Bitmap* mBitmap; 352}; 353 354/////////////////////////////////////////////////////////////////////////////// 355// Conversions to/from SkColor, for get/setPixels, and the create method, which 356// is basically like setPixels 357 358typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, 359 int x, int y); 360 361static void FromColor_D32(void* dst, const SkColor src[], int width, 362 int, int) { 363 SkPMColor* d = (SkPMColor*)dst; 364 365 for (int i = 0; i < width; i++) { 366 *d++ = SkPreMultiplyColor(*src++); 367 } 368} 369 370static void FromColor_D32_Raw(void* dst, const SkColor src[], int width, 371 int, int) { 372 // Needed to thwart the unreachable code detection from clang. 373 static const bool sk_color_ne_zero = SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER; 374 375 // SkColor's ordering may be different from SkPMColor 376 if (sk_color_ne_zero) { 377 memcpy(dst, src, width * sizeof(SkColor)); 378 return; 379 } 380 381 // order isn't same, repack each pixel manually 382 SkPMColor* d = (SkPMColor*)dst; 383 for (int i = 0; i < width; i++) { 384 SkColor c = *src++; 385 *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), 386 SkColorGetG(c), SkColorGetB(c)); 387 } 388} 389 390static void FromColor_D565(void* dst, const SkColor src[], int width, 391 int x, int y) { 392 uint16_t* d = (uint16_t*)dst; 393 394 DITHER_565_SCAN(y); 395 for (int stop = x + width; x < stop; x++) { 396 SkColor c = *src++; 397 *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), 398 DITHER_VALUE(x)); 399 } 400} 401 402static void FromColor_D4444(void* dst, const SkColor src[], int width, 403 int x, int y) { 404 SkPMColor16* d = (SkPMColor16*)dst; 405 406 DITHER_4444_SCAN(y); 407 for (int stop = x + width; x < stop; x++) { 408 SkPMColor pmc = SkPreMultiplyColor(*src++); 409 *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x)); 410// *d++ = SkPixel32ToPixel4444(pmc); 411 } 412} 413 414static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width, 415 int x, int y) { 416 SkPMColor16* d = (SkPMColor16*)dst; 417 418 DITHER_4444_SCAN(y); 419 for (int stop = x + width; x < stop; x++) { 420 SkColor c = *src++; 421 422 // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied 423 SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), 424 SkColorGetG(c), SkColorGetB(c)); 425 *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x)); 426// *d++ = SkPixel32ToPixel4444(pmc); 427 } 428} 429 430// can return NULL 431static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) { 432 switch (bitmap.colorType()) { 433 case kN32_SkColorType: 434 return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D32 : FromColor_D32_Raw; 435 case kARGB_4444_SkColorType: 436 return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D4444 : 437 FromColor_D4444_Raw; 438 case kRGB_565_SkColorType: 439 return FromColor_D565; 440 default: 441 break; 442 } 443 return NULL; 444} 445 446bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride, 447 int x, int y, int width, int height, const SkBitmap& dstBitmap) { 448 SkAutoLockPixels alp(dstBitmap); 449 void* dst = dstBitmap.getPixels(); 450 FromColorProc proc = ChooseFromColorProc(dstBitmap); 451 452 if (NULL == dst || NULL == proc) { 453 return false; 454 } 455 456 const jint* array = env->GetIntArrayElements(srcColors, NULL); 457 const SkColor* src = (const SkColor*)array + srcOffset; 458 459 // reset to to actual choice from caller 460 dst = dstBitmap.getAddr(x, y); 461 // now copy/convert each scanline 462 for (int y = 0; y < height; y++) { 463 proc(dst, src, width, x, y); 464 src += srcStride; 465 dst = (char*)dst + dstBitmap.rowBytes(); 466 } 467 468 dstBitmap.notifyPixelsChanged(); 469 470 env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), 471 JNI_ABORT); 472 return true; 473} 474 475//////////////////// ToColor procs 476 477typedef void (*ToColorProc)(SkColor dst[], const void* src, int width, 478 SkColorTable*); 479 480static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width, 481 SkColorTable*) { 482 SkASSERT(width > 0); 483 const SkPMColor* s = (const SkPMColor*)src; 484 do { 485 *dst++ = SkUnPreMultiply::PMColorToColor(*s++); 486 } while (--width != 0); 487} 488 489static void ToColor_S32_Raw(SkColor dst[], const void* src, int width, 490 SkColorTable*) { 491 SkASSERT(width > 0); 492 const SkPMColor* s = (const SkPMColor*)src; 493 do { 494 SkPMColor c = *s++; 495 *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), 496 SkGetPackedG32(c), SkGetPackedB32(c)); 497 } while (--width != 0); 498} 499 500static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width, 501 SkColorTable*) { 502 SkASSERT(width > 0); 503 const SkPMColor* s = (const SkPMColor*)src; 504 do { 505 SkPMColor c = *s++; 506 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 507 SkGetPackedB32(c)); 508 } while (--width != 0); 509} 510 511static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width, 512 SkColorTable*) { 513 SkASSERT(width > 0); 514 const SkPMColor16* s = (const SkPMColor16*)src; 515 do { 516 *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++)); 517 } while (--width != 0); 518} 519 520static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width, 521 SkColorTable*) { 522 SkASSERT(width > 0); 523 const SkPMColor16* s = (const SkPMColor16*)src; 524 do { 525 SkPMColor c = SkPixel4444ToPixel32(*s++); 526 *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), 527 SkGetPackedG32(c), SkGetPackedB32(c)); 528 } while (--width != 0); 529} 530 531static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width, 532 SkColorTable*) { 533 SkASSERT(width > 0); 534 const SkPMColor16* s = (const SkPMColor16*)src; 535 do { 536 SkPMColor c = SkPixel4444ToPixel32(*s++); 537 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 538 SkGetPackedB32(c)); 539 } while (--width != 0); 540} 541 542static void ToColor_S565(SkColor dst[], const void* src, int width, 543 SkColorTable*) { 544 SkASSERT(width > 0); 545 const uint16_t* s = (const uint16_t*)src; 546 do { 547 uint16_t c = *s++; 548 *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c), 549 SkPacked16ToB32(c)); 550 } while (--width != 0); 551} 552 553static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width, 554 SkColorTable* ctable) { 555 SkASSERT(width > 0); 556 const uint8_t* s = (const uint8_t*)src; 557 const SkPMColor* colors = ctable->readColors(); 558 do { 559 *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]); 560 } while (--width != 0); 561} 562 563static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width, 564 SkColorTable* ctable) { 565 SkASSERT(width > 0); 566 const uint8_t* s = (const uint8_t*)src; 567 const SkPMColor* colors = ctable->readColors(); 568 do { 569 SkPMColor c = colors[*s++]; 570 *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), 571 SkGetPackedG32(c), SkGetPackedB32(c)); 572 } while (--width != 0); 573} 574 575static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, 576 SkColorTable* ctable) { 577 SkASSERT(width > 0); 578 const uint8_t* s = (const uint8_t*)src; 579 const SkPMColor* colors = ctable->readColors(); 580 do { 581 SkPMColor c = colors[*s++]; 582 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 583 SkGetPackedB32(c)); 584 } while (--width != 0); 585} 586 587// can return NULL 588static ToColorProc ChooseToColorProc(const SkBitmap& src) { 589 switch (src.colorType()) { 590 case kN32_SkColorType: 591 switch (src.alphaType()) { 592 case kOpaque_SkAlphaType: 593 return ToColor_S32_Opaque; 594 case kPremul_SkAlphaType: 595 return ToColor_S32_Alpha; 596 case kUnpremul_SkAlphaType: 597 return ToColor_S32_Raw; 598 default: 599 return NULL; 600 } 601 case kARGB_4444_SkColorType: 602 switch (src.alphaType()) { 603 case kOpaque_SkAlphaType: 604 return ToColor_S4444_Opaque; 605 case kPremul_SkAlphaType: 606 return ToColor_S4444_Alpha; 607 case kUnpremul_SkAlphaType: 608 return ToColor_S4444_Raw; 609 default: 610 return NULL; 611 } 612 case kRGB_565_SkColorType: 613 return ToColor_S565; 614 case kIndex_8_SkColorType: 615 if (src.getColorTable() == NULL) { 616 return NULL; 617 } 618 switch (src.alphaType()) { 619 case kOpaque_SkAlphaType: 620 return ToColor_SI8_Opaque; 621 case kPremul_SkAlphaType: 622 return ToColor_SI8_Alpha; 623 case kUnpremul_SkAlphaType: 624 return ToColor_SI8_Raw; 625 default: 626 return NULL; 627 } 628 default: 629 break; 630 } 631 return NULL; 632} 633 634/////////////////////////////////////////////////////////////////////////////// 635/////////////////////////////////////////////////////////////////////////////// 636 637static int getPremulBitmapCreateFlags(bool isMutable) { 638 int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied; 639 if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable; 640 return flags; 641} 642 643static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 644 jint offset, jint stride, jint width, jint height, 645 jint configHandle, jboolean isMutable) { 646 SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); 647 if (NULL != jColors) { 648 size_t n = env->GetArrayLength(jColors); 649 if (n < SkAbs32(stride) * (size_t)height) { 650 doThrowAIOOBE(env); 651 return NULL; 652 } 653 } 654 655 // ARGB_4444 is a deprecated format, convert automatically to 8888 656 if (colorType == kARGB_4444_SkColorType) { 657 colorType = kN32_SkColorType; 658 } 659 660 SkBitmap bitmap; 661 bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType)); 662 663 Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); 664 if (!nativeBitmap) { 665 return NULL; 666 } 667 668 if (jColors != NULL) { 669 GraphicsJNI::SetPixels(env, jColors, offset, stride, 670 0, 0, width, height, bitmap); 671 } 672 673 return GraphicsJNI::createBitmap(env, nativeBitmap, 674 getPremulBitmapCreateFlags(isMutable)); 675} 676 677static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, 678 jint dstConfigHandle, jboolean isMutable) { 679 SkBitmap src; 680 reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src); 681 SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); 682 SkBitmap result; 683 JavaPixelAllocator allocator(env); 684 685 if (!src.copyTo(&result, dstCT, &allocator)) { 686 return NULL; 687 } 688 Bitmap* bitmap = allocator.getStorageObjAndReset(); 689 return GraphicsJNI::createBitmap(env, bitmap, 690 getPremulBitmapCreateFlags(isMutable)); 691} 692 693static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) { 694 LocalScopedBitmap bitmap(bitmapHandle); 695 bitmap->detachFromJava(); 696} 697 698static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { 699 LocalScopedBitmap bitmap(bitmapHandle); 700 bitmap->freePixels(); 701 return JNI_TRUE; 702} 703 704static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, 705 jint width, jint height, jint configHandle, jint allocSize, 706 jboolean requestPremul) { 707 LocalScopedBitmap bitmap(bitmapHandle); 708 SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); 709 710 // ARGB_4444 is a deprecated format, convert automatically to 8888 711 if (colorType == kARGB_4444_SkColorType) { 712 colorType = kN32_SkColorType; 713 } 714 715 if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) { 716 // done in native as there's no way to get BytesPerPixel in Java 717 doThrowIAE(env, "Bitmap not large enough to support new configuration"); 718 return; 719 } 720 SkAlphaType alphaType; 721 if (bitmap->info().colorType() != kRGB_565_SkColorType 722 && bitmap->info().alphaType() == kOpaque_SkAlphaType) { 723 // If the original bitmap was set to opaque, keep that setting, unless it 724 // was 565, which is required to be opaque. 725 alphaType = kOpaque_SkAlphaType; 726 } else { 727 // Otherwise respect the premultiplied request. 728 alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; 729 } 730 bitmap->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType)); 731} 732 733// These must match the int values in Bitmap.java 734enum JavaEncodeFormat { 735 kJPEG_JavaEncodeFormat = 0, 736 kPNG_JavaEncodeFormat = 1, 737 kWEBP_JavaEncodeFormat = 2 738}; 739 740static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, 741 jint format, jint quality, 742 jobject jstream, jbyteArray jstorage) { 743 744 LocalScopedBitmap bitmap(bitmapHandle); 745 SkImageEncoder::Type fm; 746 747 switch (format) { 748 case kJPEG_JavaEncodeFormat: 749 fm = SkImageEncoder::kJPEG_Type; 750 break; 751 case kPNG_JavaEncodeFormat: 752 fm = SkImageEncoder::kPNG_Type; 753 break; 754 case kWEBP_JavaEncodeFormat: 755 fm = SkImageEncoder::kWEBP_Type; 756 break; 757 default: 758 return JNI_FALSE; 759 } 760 761 if (!bitmap.valid()) { 762 return JNI_FALSE; 763 } 764 765 bool success = false; 766 767 std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage)); 768 if (!strm.get()) { 769 return JNI_FALSE; 770 } 771 772 std::unique_ptr<SkImageEncoder> encoder(SkImageEncoder::Create(fm)); 773 if (encoder.get()) { 774 SkBitmap skbitmap; 775 bitmap->getSkBitmap(&skbitmap); 776 success = encoder->encodeStream(strm.get(), skbitmap, quality); 777 } 778 return success ? JNI_TRUE : JNI_FALSE; 779} 780 781static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { 782 LocalScopedBitmap bitmap(bitmapHandle); 783 SkBitmap skBitmap; 784 bitmap->getSkBitmap(&skBitmap); 785 skBitmap.eraseColor(color); 786} 787 788static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { 789 LocalScopedBitmap bitmap(bitmapHandle); 790 return static_cast<jint>(bitmap->rowBytes()); 791} 792 793static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { 794 LocalScopedBitmap bitmap(bitmapHandle); 795 return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType()); 796} 797 798static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { 799 LocalScopedBitmap bitmap(bitmapHandle); 800 return static_cast<jint>(bitmap->peekAtPixelRef()->getGenerationID()); 801} 802 803static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { 804 LocalScopedBitmap bitmap(bitmapHandle); 805 if (bitmap->info().alphaType() == kPremul_SkAlphaType) { 806 return JNI_TRUE; 807 } 808 return JNI_FALSE; 809} 810 811static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { 812 LocalScopedBitmap bitmap(bitmapHandle); 813 return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE; 814} 815 816static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, 817 jboolean hasAlpha, jboolean requestPremul) { 818 LocalScopedBitmap bitmap(bitmapHandle); 819 if (hasAlpha) { 820 bitmap->peekAtPixelRef()->changeAlphaType( 821 requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); 822 } else { 823 bitmap->peekAtPixelRef()->changeAlphaType(kOpaque_SkAlphaType); 824 } 825} 826 827static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, 828 jboolean isPremul) { 829 LocalScopedBitmap bitmap(bitmapHandle); 830 if (!bitmap->info().isOpaque()) { 831 if (isPremul) { 832 bitmap->peekAtPixelRef()->changeAlphaType(kPremul_SkAlphaType); 833 } else { 834 bitmap->peekAtPixelRef()->changeAlphaType(kUnpremul_SkAlphaType); 835 } 836 } 837} 838 839static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { 840 LocalScopedBitmap bitmap(bitmapHandle); 841 return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; 842} 843 844static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, 845 jboolean hasMipMap) { 846 LocalScopedBitmap bitmap(bitmapHandle); 847 bitmap->setHasHardwareMipMap(hasMipMap); 848} 849 850/////////////////////////////////////////////////////////////////////////////// 851 852static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { 853 if (parcel == NULL) { 854 SkDebugf("-------- unparcel parcel is NULL\n"); 855 return NULL; 856 } 857 858 android::Parcel* p = android::parcelForJavaObject(env, parcel); 859 860 const bool isMutable = p->readInt32() != 0; 861 const SkColorType colorType = (SkColorType)p->readInt32(); 862 const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); 863 const int width = p->readInt32(); 864 const int height = p->readInt32(); 865 const int rowBytes = p->readInt32(); 866 const int density = p->readInt32(); 867 868 if (kN32_SkColorType != colorType && 869 kRGB_565_SkColorType != colorType && 870 kARGB_4444_SkColorType != colorType && 871 kIndex_8_SkColorType != colorType && 872 kAlpha_8_SkColorType != colorType) { 873 SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType); 874 return NULL; 875 } 876 877 std::unique_ptr<SkBitmap> bitmap(new SkBitmap); 878 879 if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes)) { 880 return NULL; 881 } 882 883 SkColorTable* ctable = NULL; 884 if (colorType == kIndex_8_SkColorType) { 885 int count = p->readInt32(); 886 if (count < 0 || count > 256) { 887 // The data is corrupt, since SkColorTable enforces a value between 0 and 256, 888 // inclusive. 889 return NULL; 890 } 891 if (count > 0) { 892 size_t size = count * sizeof(SkPMColor); 893 const SkPMColor* src = (const SkPMColor*)p->readInplace(size); 894 if (src == NULL) { 895 return NULL; 896 } 897 ctable = new SkColorTable(src, count); 898 } 899 } 900 901 android::Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable); 902 if (!nativeBitmap) { 903 SkSafeUnref(ctable); 904 return NULL; 905 } 906 907 SkSafeUnref(ctable); 908 909 size_t size = bitmap->getSize(); 910 911 android::Parcel::ReadableBlob blob; 912 android::status_t status = p->readBlob(size, &blob); 913 if (status) { 914 nativeBitmap->detachFromJava(); 915 doThrowRE(env, "Could not read bitmap from parcel blob."); 916 return NULL; 917 } 918 919 bitmap->lockPixels(); 920 memcpy(bitmap->getPixels(), blob.data(), size); 921 bitmap->unlockPixels(); 922 923 blob.release(); 924 925 return GraphicsJNI::createBitmap(env, nativeBitmap, 926 getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); 927} 928 929static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, 930 jlong bitmapHandle, 931 jboolean isMutable, jint density, 932 jobject parcel) { 933 if (parcel == NULL) { 934 SkDebugf("------- writeToParcel null parcel\n"); 935 return JNI_FALSE; 936 } 937 938 android::Parcel* p = android::parcelForJavaObject(env, parcel); 939 SkBitmap bitmap; 940 reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); 941 942 p->writeInt32(isMutable); 943 p->writeInt32(bitmap.colorType()); 944 p->writeInt32(bitmap.alphaType()); 945 p->writeInt32(bitmap.width()); 946 p->writeInt32(bitmap.height()); 947 p->writeInt32(bitmap.rowBytes()); 948 p->writeInt32(density); 949 950 if (bitmap.colorType() == kIndex_8_SkColorType) { 951 SkColorTable* ctable = bitmap.getColorTable(); 952 if (ctable != NULL) { 953 int count = ctable->count(); 954 p->writeInt32(count); 955 memcpy(p->writeInplace(count * sizeof(SkPMColor)), 956 ctable->readColors(), count * sizeof(SkPMColor)); 957 } else { 958 p->writeInt32(0); // indicate no ctable 959 } 960 } 961 962 size_t size = bitmap.getSize(); 963 964 android::Parcel::WritableBlob blob; 965 android::status_t status = p->writeBlob(size, &blob); 966 if (status) { 967 doThrowRE(env, "Could not write bitmap to parcel blob."); 968 return JNI_FALSE; 969 } 970 971 bitmap.lockPixels(); 972 const void* pSrc = bitmap.getPixels(); 973 if (pSrc == NULL) { 974 memset(blob.data(), 0, size); 975 } else { 976 memcpy(blob.data(), pSrc, size); 977 } 978 bitmap.unlockPixels(); 979 980 blob.release(); 981 return JNI_TRUE; 982} 983 984static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, 985 jlong srcHandle, jlong paintHandle, 986 jintArray offsetXY) { 987 SkBitmap src; 988 reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src); 989 const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle); 990 SkIPoint offset; 991 SkBitmap dst; 992 JavaPixelAllocator allocator(env); 993 994 src.extractAlpha(&dst, paint, &allocator, &offset); 995 // If Skia can't allocate pixels for destination bitmap, it resets 996 // it, that is set its pixels buffer to NULL, and zero width and height. 997 if (dst.getPixels() == NULL && src.getPixels() != NULL) { 998 doThrowOOME(env, "failed to allocate pixels for alpha"); 999 return NULL; 1000 } 1001 if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { 1002 int* array = env->GetIntArrayElements(offsetXY, NULL); 1003 array[0] = offset.fX; 1004 array[1] = offset.fY; 1005 env->ReleaseIntArrayElements(offsetXY, array, 0); 1006 } 1007 1008 return GraphicsJNI::createBitmap(env, allocator.getStorageObjAndReset(), 1009 getPremulBitmapCreateFlags(true)); 1010} 1011 1012/////////////////////////////////////////////////////////////////////////////// 1013 1014static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, 1015 jint x, jint y) { 1016 SkBitmap bitmap; 1017 reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); 1018 SkAutoLockPixels alp(bitmap); 1019 1020 ToColorProc proc = ChooseToColorProc(bitmap); 1021 if (NULL == proc) { 1022 return 0; 1023 } 1024 const void* src = bitmap.getAddr(x, y); 1025 if (NULL == src) { 1026 return 0; 1027 } 1028 1029 SkColor dst[1]; 1030 proc(dst, src, 1, bitmap.getColorTable()); 1031 return static_cast<jint>(dst[0]); 1032} 1033 1034static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, 1035 jintArray pixelArray, jint offset, jint stride, 1036 jint x, jint y, jint width, jint height) { 1037 SkBitmap bitmap; 1038 reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); 1039 SkAutoLockPixels alp(bitmap); 1040 1041 ToColorProc proc = ChooseToColorProc(bitmap); 1042 if (NULL == proc) { 1043 return; 1044 } 1045 const void* src = bitmap.getAddr(x, y); 1046 if (NULL == src) { 1047 return; 1048 } 1049 1050 SkColorTable* ctable = bitmap.getColorTable(); 1051 jint* dst = env->GetIntArrayElements(pixelArray, NULL); 1052 SkColor* d = (SkColor*)dst + offset; 1053 while (--height >= 0) { 1054 proc(d, src, width, ctable); 1055 d += stride; 1056 src = (void*)((const char*)src + bitmap.rowBytes()); 1057 } 1058 env->ReleaseIntArrayElements(pixelArray, dst, 0); 1059} 1060 1061/////////////////////////////////////////////////////////////////////////////// 1062 1063static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, 1064 jint x, jint y, jint colorHandle) { 1065 SkBitmap bitmap; 1066 reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); 1067 SkColor color = static_cast<SkColor>(colorHandle); 1068 SkAutoLockPixels alp(bitmap); 1069 if (NULL == bitmap.getPixels()) { 1070 return; 1071 } 1072 1073 FromColorProc proc = ChooseFromColorProc(bitmap); 1074 if (NULL == proc) { 1075 return; 1076 } 1077 1078 proc(bitmap.getAddr(x, y), &color, 1, x, y); 1079 bitmap.notifyPixelsChanged(); 1080} 1081 1082static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, 1083 jintArray pixelArray, jint offset, jint stride, 1084 jint x, jint y, jint width, jint height) { 1085 SkBitmap bitmap; 1086 reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); 1087 GraphicsJNI::SetPixels(env, pixelArray, offset, stride, 1088 x, y, width, height, bitmap); 1089} 1090 1091static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, 1092 jlong bitmapHandle, jobject jbuffer) { 1093 SkBitmap bitmap; 1094 reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); 1095 SkAutoLockPixels alp(bitmap); 1096 const void* src = bitmap.getPixels(); 1097 1098 if (NULL != src) { 1099 android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); 1100 1101 // the java side has already checked that buffer is large enough 1102 memcpy(abp.pointer(), src, bitmap.getSize()); 1103 } 1104} 1105 1106static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, 1107 jlong bitmapHandle, jobject jbuffer) { 1108 SkBitmap bitmap; 1109 reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); 1110 SkAutoLockPixels alp(bitmap); 1111 void* dst = bitmap.getPixels(); 1112 1113 if (NULL != dst) { 1114 android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); 1115 // the java side has already checked that buffer is large enough 1116 memcpy(dst, abp.pointer(), bitmap.getSize()); 1117 bitmap.notifyPixelsChanged(); 1118 } 1119} 1120 1121static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, 1122 jlong bm1Handle) { 1123 SkBitmap bm0; 1124 SkBitmap bm1; 1125 reinterpret_cast<Bitmap*>(bm0Handle)->getSkBitmap(&bm0); 1126 reinterpret_cast<Bitmap*>(bm1Handle)->getSkBitmap(&bm1); 1127 if (bm0.width() != bm1.width() || 1128 bm0.height() != bm1.height() || 1129 bm0.colorType() != bm1.colorType()) { 1130 return JNI_FALSE; 1131 } 1132 1133 SkAutoLockPixels alp0(bm0); 1134 SkAutoLockPixels alp1(bm1); 1135 1136 // if we can't load the pixels, return false 1137 if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) { 1138 return JNI_FALSE; 1139 } 1140 1141 if (bm0.colorType() == kIndex_8_SkColorType) { 1142 SkColorTable* ct0 = bm0.getColorTable(); 1143 SkColorTable* ct1 = bm1.getColorTable(); 1144 if (NULL == ct0 || NULL == ct1) { 1145 return JNI_FALSE; 1146 } 1147 if (ct0->count() != ct1->count()) { 1148 return JNI_FALSE; 1149 } 1150 1151 const size_t size = ct0->count() * sizeof(SkPMColor); 1152 if (memcmp(ct0->readColors(), ct1->readColors(), size) != 0) { 1153 return JNI_FALSE; 1154 } 1155 } 1156 1157 // now compare each scanline. We can't do the entire buffer at once, 1158 // since we don't care about the pixel values that might extend beyond 1159 // the width (since the scanline might be larger than the logical width) 1160 const int h = bm0.height(); 1161 const size_t size = bm0.width() * bm0.bytesPerPixel(); 1162 for (int y = 0; y < h; y++) { 1163 // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config 1164 // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 1165 // and bm1 both have pixel data() (have passed NULL == getPixels() check), 1166 // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE 1167 // to warn user those 2 unrecognized config bitmaps may be different. 1168 void *bm0Addr = bm0.getAddr(0, y); 1169 void *bm1Addr = bm1.getAddr(0, y); 1170 1171 if(bm0Addr == NULL || bm1Addr == NULL) { 1172 return JNI_FALSE; 1173 } 1174 1175 if (memcmp(bm0Addr, bm1Addr, size) != 0) { 1176 return JNI_FALSE; 1177 } 1178 } 1179 return JNI_TRUE; 1180} 1181 1182static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { 1183 LocalScopedBitmap bitmap(bitmapHandle); 1184 SkPixelRef* pixelRef = bitmap.valid() ? bitmap->peekAtPixelRef() : nullptr; 1185 SkSafeRef(pixelRef); 1186 return reinterpret_cast<jlong>(pixelRef); 1187} 1188 1189/////////////////////////////////////////////////////////////////////////////// 1190 1191static JNINativeMethod gBitmapMethods[] = { 1192 { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;", 1193 (void*)Bitmap_creator }, 1194 { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", 1195 (void*)Bitmap_copy }, 1196 { "nativeDestructor", "(J)V", (void*)Bitmap_destructor }, 1197 { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle }, 1198 { "nativeReconfigure", "(JIIIIZ)V", (void*)Bitmap_reconfigure }, 1199 { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", 1200 (void*)Bitmap_compress }, 1201 { "nativeErase", "(JI)V", (void*)Bitmap_erase }, 1202 { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, 1203 { "nativeConfig", "(J)I", (void*)Bitmap_config }, 1204 { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, 1205 { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, 1206 { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, 1207 { "nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied}, 1208 { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, 1209 { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, 1210 { "nativeCreateFromParcel", 1211 "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", 1212 (void*)Bitmap_createFromParcel }, 1213 { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z", 1214 (void*)Bitmap_writeToParcel }, 1215 { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", 1216 (void*)Bitmap_extractAlpha }, 1217 { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId }, 1218 { "nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel }, 1219 { "nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels }, 1220 { "nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel }, 1221 { "nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels }, 1222 { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", 1223 (void*)Bitmap_copyPixelsToBuffer }, 1224 { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", 1225 (void*)Bitmap_copyPixelsFromBuffer }, 1226 { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, 1227 { "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef }, 1228}; 1229 1230int register_android_graphics_Bitmap(JNIEnv* env) 1231{ 1232 return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, 1233 NELEM(gBitmapMethods)); 1234} 1235