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