Bitmap.cpp revision 3ca3975f066433a473d89b37432f43c74b1c5143
1#define LOG_TAG "Bitmap" 2#include "Bitmap.h" 3 4#include "GraphicBuffer.h" 5#include "SkBitmap.h" 6#include "SkPixelRef.h" 7#include "SkImageEncoder.h" 8#include "SkImageInfo.h" 9#include "SkColor.h" 10#include "SkColorPriv.h" 11#include "SkColorSpace.h" 12#include "SkColorSpaceXform.h" 13#include "SkHalf.h" 14#include "SkMatrix44.h" 15#include "SkPM4f.h" 16#include "SkPM4fPriv.h" 17#include "GraphicsJNI.h" 18#include "SkDither.h" 19#include "SkUnPreMultiply.h" 20#include "SkStream.h" 21 22#include <binder/Parcel.h> 23#include "android_os_Parcel.h" 24#include "android_util_Binder.h" 25#include "android_nio_utils.h" 26#include "CreateJavaOutputStreamAdaptor.h" 27#include <hwui/Paint.h> 28#include <hwui/Bitmap.h> 29#include <renderthread/RenderProxy.h> 30 31#include "core_jni_helpers.h" 32 33#include <jni.h> 34#include <string.h> 35#include <memory> 36#include <string> 37 38#define DEBUG_PARCEL 0 39#define ASHMEM_BITMAP_MIN_SIZE (128 * (1 << 10)) 40 41static jclass gBitmap_class; 42static jfieldID gBitmap_nativePtr; 43static jmethodID gBitmap_constructorMethodID; 44static jmethodID gBitmap_reinitMethodID; 45static jmethodID gBitmap_getAllocationByteCountMethodID; 46 47namespace android { 48 49class BitmapWrapper { 50public: 51 BitmapWrapper(Bitmap* bitmap) 52 : mBitmap(bitmap) { } 53 54 void freePixels() { 55 mInfo = mBitmap->info(); 56 mHasHardwareMipMap = mBitmap->hasHardwareMipMap(); 57 mAllocationSize = mBitmap->getAllocationByteCount(); 58 mRowBytes = mBitmap->rowBytes(); 59 mGenerationId = mBitmap->getGenerationID(); 60 mIsHardware = mBitmap->isHardware(); 61 mBitmap.reset(); 62 } 63 64 bool valid() { 65 return mBitmap; 66 } 67 68 Bitmap& bitmap() { 69 assertValid(); 70 return *mBitmap; 71 } 72 73 void assertValid() { 74 LOG_ALWAYS_FATAL_IF(!valid(), "Error, cannot access an invalid/free'd bitmap here!"); 75 } 76 77 void getSkBitmap(SkBitmap* outBitmap) { 78 assertValid(); 79 mBitmap->getSkBitmap(outBitmap); 80 } 81 82 bool hasHardwareMipMap() { 83 if (mBitmap) { 84 return mBitmap->hasHardwareMipMap(); 85 } 86 return mHasHardwareMipMap; 87 } 88 89 void setHasHardwareMipMap(bool hasMipMap) { 90 assertValid(); 91 mBitmap->setHasHardwareMipMap(hasMipMap); 92 } 93 94 void setAlphaType(SkAlphaType alphaType) { 95 assertValid(); 96 mBitmap->setAlphaType(alphaType); 97 } 98 99 const SkImageInfo& info() { 100 if (mBitmap) { 101 return mBitmap->info(); 102 } 103 return mInfo; 104 } 105 106 size_t getAllocationByteCount() const { 107 if (mBitmap) { 108 return mBitmap->getAllocationByteCount(); 109 } 110 return mAllocationSize; 111 } 112 113 size_t rowBytes() const { 114 if (mBitmap) { 115 return mBitmap->rowBytes(); 116 } 117 return mRowBytes; 118 } 119 120 uint32_t getGenerationID() const { 121 if (mBitmap) { 122 return mBitmap->getGenerationID(); 123 } 124 return mGenerationId; 125 } 126 127 bool isHardware() { 128 if (mBitmap) { 129 return mBitmap->isHardware(); 130 } 131 return mIsHardware; 132 } 133 134 ~BitmapWrapper() { } 135 136private: 137 sk_sp<Bitmap> mBitmap; 138 SkImageInfo mInfo; 139 bool mHasHardwareMipMap; 140 size_t mAllocationSize; 141 size_t mRowBytes; 142 uint32_t mGenerationId; 143 bool mIsHardware; 144}; 145 146// Convenience class that does not take a global ref on the pixels, relying 147// on the caller already having a local JNI ref 148class LocalScopedBitmap { 149public: 150 explicit LocalScopedBitmap(jlong bitmapHandle) 151 : mBitmapWrapper(reinterpret_cast<BitmapWrapper*>(bitmapHandle)) {} 152 153 BitmapWrapper* operator->() { 154 return mBitmapWrapper; 155 } 156 157 void* pixels() { 158 return mBitmapWrapper->bitmap().pixels(); 159 } 160 161 bool valid() { 162 return mBitmapWrapper && mBitmapWrapper->valid(); 163 } 164 165private: 166 BitmapWrapper* mBitmapWrapper; 167}; 168 169namespace bitmap { 170 171// Assert that bitmap's SkAlphaType is consistent with isPremultiplied. 172static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) { 173 // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is 174 // irrelevant. This just tests to ensure that the SkAlphaType is not 175 // opposite of isPremultiplied. 176 if (isPremultiplied) { 177 SkASSERT(info.alphaType() != kUnpremul_SkAlphaType); 178 } else { 179 SkASSERT(info.alphaType() != kPremul_SkAlphaType); 180 } 181} 182 183void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, 184 bool isPremultiplied) 185{ 186 // The caller needs to have already set the alpha type properly, so the 187 // native SkBitmap stays in sync with the Java Bitmap. 188 assert_premultiplied(info, isPremultiplied); 189 190 env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID, 191 info.width(), info.height(), isPremultiplied); 192} 193 194int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap) 195{ 196 return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID); 197} 198 199jobject createBitmap(JNIEnv* env, Bitmap* bitmap, 200 int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, 201 int density) { 202 bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable; 203 bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied; 204 // The caller needs to have already set the alpha type properly, so the 205 // native SkBitmap stays in sync with the Java Bitmap. 206 assert_premultiplied(bitmap->info(), isPremultiplied); 207 BitmapWrapper* bitmapWrapper = new BitmapWrapper(bitmap); 208 jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID, 209 reinterpret_cast<jlong>(bitmapWrapper), bitmap->width(), bitmap->height(), density, 210 isMutable, isPremultiplied, ninePatchChunk, ninePatchInsets); 211 212 if (env->ExceptionCheck() != 0) { 213 ALOGE("*** Uncaught exception returned from Java call!\n"); 214 env->ExceptionDescribe(); 215 } 216 return obj; 217} 218 219void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap) { 220 LocalScopedBitmap bitmap(bitmapHandle); 221 bitmap->getSkBitmap(outBitmap); 222} 223 224Bitmap& toBitmap(JNIEnv* env, jobject bitmap) { 225 SkASSERT(env); 226 SkASSERT(bitmap); 227 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); 228 jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); 229 LocalScopedBitmap localBitmap(bitmapHandle); 230 return localBitmap->bitmap(); 231} 232 233Bitmap& toBitmap(JNIEnv* env, jlong bitmapHandle) { 234 SkASSERT(env); 235 LocalScopedBitmap localBitmap(bitmapHandle); 236 return localBitmap->bitmap(); 237} 238 239void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info) { 240 SkASSERT(info); 241 SkASSERT(env); 242 SkASSERT(bitmap); 243 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); 244 jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); 245 LocalScopedBitmap localBitmap(bitmapHandle); 246 247 const SkImageInfo& imageInfo = localBitmap->info(); 248 info->width = imageInfo.width(); 249 info->height = imageInfo.height(); 250 info->stride = localBitmap->rowBytes(); 251 info->flags = 0; 252 switch (imageInfo.colorType()) { 253 case kN32_SkColorType: 254 info->format = ANDROID_BITMAP_FORMAT_RGBA_8888; 255 break; 256 case kRGB_565_SkColorType: 257 info->format = ANDROID_BITMAP_FORMAT_RGB_565; 258 break; 259 case kARGB_4444_SkColorType: 260 info->format = ANDROID_BITMAP_FORMAT_RGBA_4444; 261 break; 262 case kAlpha_8_SkColorType: 263 info->format = ANDROID_BITMAP_FORMAT_A_8; 264 break; 265 default: 266 info->format = ANDROID_BITMAP_FORMAT_NONE; 267 break; 268 } 269} 270 271void* lockPixels(JNIEnv* env, jobject bitmap) { 272 SkASSERT(env); 273 SkASSERT(bitmap); 274 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); 275 jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); 276 277 LocalScopedBitmap localBitmap(bitmapHandle); 278 if (!localBitmap->valid()) return nullptr; 279 280 SkPixelRef& pixelRef = localBitmap->bitmap(); 281 pixelRef.lockPixels(); 282 if (!pixelRef.pixels()) { 283 pixelRef.unlockPixels(); 284 return nullptr; 285 } 286 pixelRef.ref(); 287 return pixelRef.pixels(); 288} 289 290bool unlockPixels(JNIEnv* env, jobject bitmap) { 291 SkASSERT(env); 292 SkASSERT(bitmap); 293 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); 294 jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); 295 296 LocalScopedBitmap localBitmap(bitmapHandle); 297 if (!localBitmap->valid()) return false; 298 299 SkPixelRef& pixelRef = localBitmap->bitmap(); 300 pixelRef.notifyPixelsChanged(); 301 pixelRef.unlockPixels(); 302 pixelRef.unref(); 303 return true; 304} 305 306} // namespace bitmap 307 308} // namespace android 309 310using namespace android; 311using namespace android::bitmap; 312 313/////////////////////////////////////////////////////////////////////////////// 314// Conversions to/from SkColor, for get/setPixels, and the create method, which 315// is basically like setPixels 316 317typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, 318 int x, int y); 319 320static void FromColor_F16(void* dst, const SkColor src[], int width, 321 int, int) { 322 uint64_t* d = (uint64_t*)dst; 323 324 for (int i = 0; i < width; i++) { 325 *d++ = SkColor4f::FromColor(*src++).premul().toF16(); 326 } 327} 328 329static void FromColor_F16_Raw(void* dst, const SkColor src[], int width, 330 int, int) { 331 uint64_t* d = (uint64_t*)dst; 332 333 for (int i = 0; i < width; i++) { 334 const float* color = SkColor4f::FromColor(*src++).vec(); 335 uint16_t* scratch = reinterpret_cast<uint16_t*>(d++); 336 for (int i = 0; i < 4; ++i) { 337 scratch[i] = SkFloatToHalf(color[i]); 338 } 339 } 340} 341 342static void FromColor_D32(void* dst, const SkColor src[], int width, 343 int, int) { 344 SkPMColor* d = (SkPMColor*)dst; 345 346 for (int i = 0; i < width; i++) { 347 *d++ = SkPreMultiplyColor(*src++); 348 } 349} 350 351static void FromColor_D32_Raw(void* dst, const SkColor src[], int width, 352 int, int) { 353 // Needed to thwart the unreachable code detection from clang. 354 static const bool sk_color_ne_zero = SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER; 355 356 // SkColor's ordering may be different from SkPMColor 357 if (sk_color_ne_zero) { 358 memcpy(dst, src, width * sizeof(SkColor)); 359 return; 360 } 361 362 // order isn't same, repack each pixel manually 363 SkPMColor* d = (SkPMColor*)dst; 364 for (int i = 0; i < width; i++) { 365 SkColor c = *src++; 366 *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), 367 SkColorGetG(c), SkColorGetB(c)); 368 } 369} 370 371static void FromColor_D565(void* dst, const SkColor src[], int width, 372 int x, int y) { 373 uint16_t* d = (uint16_t*)dst; 374 375 DITHER_565_SCAN(y); 376 for (int stop = x + width; x < stop; x++) { 377 SkColor c = *src++; 378 *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), 379 DITHER_VALUE(x)); 380 } 381} 382 383static void FromColor_D4444(void* dst, const SkColor src[], int width, 384 int x, int y) { 385 SkPMColor16* d = (SkPMColor16*)dst; 386 387 DITHER_4444_SCAN(y); 388 for (int stop = x + width; x < stop; x++) { 389 SkPMColor pmc = SkPreMultiplyColor(*src++); 390 *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x)); 391// *d++ = SkPixel32ToPixel4444(pmc); 392 } 393} 394 395static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width, 396 int x, int y) { 397 SkPMColor16* d = (SkPMColor16*)dst; 398 399 DITHER_4444_SCAN(y); 400 for (int stop = x + width; x < stop; x++) { 401 SkColor c = *src++; 402 403 // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied 404 SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), 405 SkColorGetG(c), SkColorGetB(c)); 406 *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x)); 407// *d++ = SkPixel32ToPixel4444(pmc); 408 } 409} 410 411static void FromColor_DA8(void* dst, const SkColor src[], int width, int x, int y) { 412 uint8_t* d = (uint8_t*)dst; 413 414 for (int stop = x + width; x < stop; x++) { 415 *d++ = SkColorGetA(*src++); 416 } 417} 418 419// can return NULL 420static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) { 421 switch (bitmap.colorType()) { 422 case kN32_SkColorType: 423 return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D32 : FromColor_D32_Raw; 424 case kARGB_4444_SkColorType: 425 return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D4444 : 426 FromColor_D4444_Raw; 427 case kRGB_565_SkColorType: 428 return FromColor_D565; 429 case kAlpha_8_SkColorType: 430 return FromColor_DA8; 431 case kRGBA_F16_SkColorType: 432 return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_F16 : FromColor_F16_Raw; 433 default: 434 break; 435 } 436 return NULL; 437} 438 439bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride, 440 int x, int y, int width, int height, const SkBitmap& dstBitmap) { 441 SkAutoLockPixels alp(dstBitmap); 442 void* dst = dstBitmap.getPixels(); 443 FromColorProc proc = ChooseFromColorProc(dstBitmap); 444 445 if (NULL == dst || NULL == proc) { 446 return false; 447 } 448 449 const jint* array = env->GetIntArrayElements(srcColors, NULL); 450 const SkColor* src = (const SkColor*)array + srcOffset; 451 452 // reset to to actual choice from caller 453 dst = dstBitmap.getAddr(x, y); 454 455 SkColorSpace* colorSpace = dstBitmap.colorSpace(); 456 if (dstBitmap.colorType() == kRGBA_F16_SkColorType || 457 GraphicsJNI::isColorSpaceSRGB(colorSpace)) { 458 // now copy/convert each scanline 459 for (int y = 0; y < height; y++) { 460 proc(dst, src, width, x, y); 461 src += srcStride; 462 dst = (char*)dst + dstBitmap.rowBytes(); 463 } 464 } else { 465 auto sRGB = SkColorSpace::MakeSRGB(); 466 auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace); 467 468 std::unique_ptr<SkColor[]> row(new SkColor[width]); 469 470 // now copy/convert each scanline 471 for (int y = 0; y < height; y++) { 472 memcpy(row.get(), src, sizeof(SkColor) * width); 473 xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, row.get(), 474 SkColorSpaceXform::kBGRA_8888_ColorFormat, row.get(), width, 475 SkAlphaType::kUnpremul_SkAlphaType); 476 477 proc(dst, row.get(), width, x, y); 478 src += srcStride; 479 dst = (char*)dst + dstBitmap.rowBytes(); 480 } 481 } 482 483 dstBitmap.notifyPixelsChanged(); 484 485 env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), JNI_ABORT); 486 return true; 487} 488 489//////////////////// ToColor procs 490 491typedef void (*ToColorProc)(SkColor dst[], const void* src, int width, 492 SkColorTable*); 493 494static void ToColor_F16_Alpha(SkColor dst[], const void* src, int width, 495 SkColorTable*) { 496 SkASSERT(width > 0); 497 uint64_t* s = (uint64_t*)src; 498 do { 499 *dst++ = SkPM4f::FromF16((const uint16_t*) s++).unpremul().toSkColor(); 500 } while (--width != 0); 501} 502 503static void ToColor_F16_Raw(SkColor dst[], const void* src, int width, 504 SkColorTable*) { 505 SkASSERT(width > 0); 506 uint64_t* s = (uint64_t*)src; 507 do { 508 *dst++ = Sk4f_toS32(swizzle_rb(SkHalfToFloat_finite_ftz(*s++))); 509 } while (--width != 0); 510} 511 512static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width, 513 SkColorTable*) { 514 SkASSERT(width > 0); 515 const SkPMColor* s = (const SkPMColor*)src; 516 do { 517 *dst++ = SkUnPreMultiply::PMColorToColor(*s++); 518 } while (--width != 0); 519} 520 521static void ToColor_S32_Raw(SkColor dst[], const void* src, int width, 522 SkColorTable*) { 523 SkASSERT(width > 0); 524 const SkPMColor* s = (const SkPMColor*)src; 525 do { 526 SkPMColor c = *s++; 527 *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), 528 SkGetPackedG32(c), SkGetPackedB32(c)); 529 } while (--width != 0); 530} 531 532static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width, 533 SkColorTable*) { 534 SkASSERT(width > 0); 535 const SkPMColor* s = (const SkPMColor*)src; 536 do { 537 SkPMColor c = *s++; 538 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 539 SkGetPackedB32(c)); 540 } while (--width != 0); 541} 542 543static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width, 544 SkColorTable*) { 545 SkASSERT(width > 0); 546 const SkPMColor16* s = (const SkPMColor16*)src; 547 do { 548 *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++)); 549 } while (--width != 0); 550} 551 552static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width, 553 SkColorTable*) { 554 SkASSERT(width > 0); 555 const SkPMColor16* s = (const SkPMColor16*)src; 556 do { 557 SkPMColor c = SkPixel4444ToPixel32(*s++); 558 *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), 559 SkGetPackedG32(c), SkGetPackedB32(c)); 560 } while (--width != 0); 561} 562 563static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width, 564 SkColorTable*) { 565 SkASSERT(width > 0); 566 const SkPMColor16* s = (const SkPMColor16*)src; 567 do { 568 SkPMColor c = SkPixel4444ToPixel32(*s++); 569 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 570 SkGetPackedB32(c)); 571 } while (--width != 0); 572} 573 574static void ToColor_S565(SkColor dst[], const void* src, int width, 575 SkColorTable*) { 576 SkASSERT(width > 0); 577 const uint16_t* s = (const uint16_t*)src; 578 do { 579 uint16_t c = *s++; 580 *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c), 581 SkPacked16ToB32(c)); 582 } while (--width != 0); 583} 584 585static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width, 586 SkColorTable* ctable) { 587 SkASSERT(width > 0); 588 const uint8_t* s = (const uint8_t*)src; 589 const SkPMColor* colors = ctable->readColors(); 590 do { 591 *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]); 592 } while (--width != 0); 593} 594 595static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width, 596 SkColorTable* ctable) { 597 SkASSERT(width > 0); 598 const uint8_t* s = (const uint8_t*)src; 599 const SkPMColor* colors = ctable->readColors(); 600 do { 601 SkPMColor c = colors[*s++]; 602 *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), 603 SkGetPackedG32(c), SkGetPackedB32(c)); 604 } while (--width != 0); 605} 606 607static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, 608 SkColorTable* ctable) { 609 SkASSERT(width > 0); 610 const uint8_t* s = (const uint8_t*)src; 611 const SkPMColor* colors = ctable->readColors(); 612 do { 613 SkPMColor c = colors[*s++]; 614 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 615 SkGetPackedB32(c)); 616 } while (--width != 0); 617} 618 619static void ToColor_SA8(SkColor dst[], const void* src, int width, SkColorTable*) { 620 SkASSERT(width > 0); 621 const uint8_t* s = (const uint8_t*)src; 622 do { 623 uint8_t c = *s++; 624 *dst++ = SkColorSetARGB(c, 0, 0, 0); 625 } while (--width != 0); 626} 627 628// can return NULL 629static ToColorProc ChooseToColorProc(const SkBitmap& src) { 630 switch (src.colorType()) { 631 case kN32_SkColorType: 632 switch (src.alphaType()) { 633 case kOpaque_SkAlphaType: 634 return ToColor_S32_Opaque; 635 case kPremul_SkAlphaType: 636 return ToColor_S32_Alpha; 637 case kUnpremul_SkAlphaType: 638 return ToColor_S32_Raw; 639 default: 640 return NULL; 641 } 642 case kARGB_4444_SkColorType: 643 switch (src.alphaType()) { 644 case kOpaque_SkAlphaType: 645 return ToColor_S4444_Opaque; 646 case kPremul_SkAlphaType: 647 return ToColor_S4444_Alpha; 648 case kUnpremul_SkAlphaType: 649 return ToColor_S4444_Raw; 650 default: 651 return NULL; 652 } 653 case kRGB_565_SkColorType: 654 return ToColor_S565; 655 case kIndex_8_SkColorType: 656 if (src.getColorTable() == NULL) { 657 return NULL; 658 } 659 switch (src.alphaType()) { 660 case kOpaque_SkAlphaType: 661 return ToColor_SI8_Opaque; 662 case kPremul_SkAlphaType: 663 return ToColor_SI8_Alpha; 664 case kUnpremul_SkAlphaType: 665 return ToColor_SI8_Raw; 666 default: 667 return NULL; 668 } 669 case kAlpha_8_SkColorType: 670 return ToColor_SA8; 671 case kRGBA_F16_SkColorType: 672 switch (src.alphaType()) { 673 case kOpaque_SkAlphaType: 674 return ToColor_F16_Raw; 675 case kPremul_SkAlphaType: 676 return ToColor_F16_Alpha; 677 case kUnpremul_SkAlphaType: 678 return ToColor_F16_Raw; 679 default: 680 return NULL; 681 } 682 default: 683 break; 684 } 685 return NULL; 686} 687 688static void ToF16_SA8(void* dst, const void* src, int width) { 689 SkASSERT(width > 0); 690 uint64_t* d = (uint64_t*)dst; 691 const uint8_t* s = (const uint8_t*)src; 692 693 for (int i = 0; i < width; i++) { 694 uint8_t c = *s++; 695 SkPM4f a; 696 a.fVec[SkPM4f::R] = 0.0f; 697 a.fVec[SkPM4f::G] = 0.0f; 698 a.fVec[SkPM4f::B] = 0.0f; 699 a.fVec[SkPM4f::A] = c / 255.0f; 700 *d++ = a.toF16(); 701 } 702} 703 704/////////////////////////////////////////////////////////////////////////////// 705/////////////////////////////////////////////////////////////////////////////// 706 707static int getPremulBitmapCreateFlags(bool isMutable) { 708 int flags = android::bitmap::kBitmapCreateFlag_Premultiplied; 709 if (isMutable) flags |= android::bitmap::kBitmapCreateFlag_Mutable; 710 return flags; 711} 712 713static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 714 jint offset, jint stride, jint width, jint height, 715 jint configHandle, jboolean isMutable, 716 jfloatArray xyzD50, jobject transferParameters) { 717 SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); 718 if (NULL != jColors) { 719 size_t n = env->GetArrayLength(jColors); 720 if (n < SkAbs32(stride) * (size_t)height) { 721 doThrowAIOOBE(env); 722 return NULL; 723 } 724 } 725 726 // ARGB_4444 is a deprecated format, convert automatically to 8888 727 if (colorType == kARGB_4444_SkColorType) { 728 colorType = kN32_SkColorType; 729 } 730 731 SkBitmap bitmap; 732 sk_sp<SkColorSpace> colorSpace; 733 734 if (colorType != kN32_SkColorType || xyzD50 == nullptr || transferParameters == nullptr) { 735 colorSpace = GraphicsJNI::colorSpaceForType(colorType); 736 } else { 737 SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters); 738 SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50); 739 colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix); 740 } 741 742 bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, colorSpace)); 743 744 sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL); 745 if (!nativeBitmap) { 746 return NULL; 747 } 748 749 if (jColors != NULL) { 750 GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap); 751 } 752 753 return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable)); 754} 755 756static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src, 757 SkBitmap::Allocator* alloc) { 758 // Skia does not support copying from kAlpha8 to types that are not alpha only. 759 // We will handle this case here. 760 if (kAlpha_8_SkColorType == src.colorType() && kAlpha_8_SkColorType != dstCT) { 761 SkAutoPixmapUnlock srcUnlocker; 762 if (!src.requestLock(&srcUnlocker)) { 763 return false; 764 } 765 SkPixmap srcPixmap = srcUnlocker.pixmap(); 766 767 SkImageInfo dstInfo = src.info().makeColorType(dstCT); 768 if (dstCT == kRGBA_F16_SkColorType) { 769 dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear()); 770 } 771 if (!dst->setInfo(dstInfo)) { 772 return false; 773 } 774 if (!dst->tryAllocPixels(alloc, nullptr)) { 775 return false; 776 } 777 778 switch (dstCT) { 779 case kRGBA_8888_SkColorType: 780 case kBGRA_8888_SkColorType: { 781 for (int y = 0; y < src.height(); y++) { 782 const uint8_t* srcRow = srcPixmap.addr8(0, y); 783 uint32_t* dstRow = dst->getAddr32(0, y); 784 ToColor_SA8(dstRow, srcRow, src.width(), nullptr); 785 } 786 return true; 787 } 788 case kRGB_565_SkColorType: { 789 for (int y = 0; y < src.height(); y++) { 790 uint16_t* dstRow = dst->getAddr16(0, y); 791 memset(dstRow, 0, sizeof(uint16_t) * src.width()); 792 } 793 return true; 794 } 795 case kRGBA_F16_SkColorType: { 796 for (int y = 0; y < src.height(); y++) { 797 const uint8_t* srcRow = srcPixmap.addr8(0, y); 798 void* dstRow = dst->getAddr(0, y); 799 ToF16_SA8(dstRow, srcRow, src.width()); 800 } 801 return true; 802 } 803 default: 804 return false; 805 } 806 } 807 808 return src.copyTo(dst, dstCT, alloc); 809} 810 811static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, 812 jint dstConfigHandle, jboolean isMutable) { 813 SkBitmap src; 814 reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); 815 if (dstConfigHandle == GraphicsJNI::hardwareLegacyBitmapConfig()) { 816 sk_sp<Bitmap> bitmap(Bitmap::allocateHardwareBitmap(src)); 817 if (!bitmap.get()) { 818 return NULL; 819 } 820 return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(isMutable)); 821 } 822 823 SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); 824 SkBitmap result; 825 HeapAllocator allocator; 826 827 if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { 828 return NULL; 829 } 830 auto bitmap = allocator.getStorageObjAndReset(); 831 return createBitmap(env, bitmap, getPremulBitmapCreateFlags(isMutable)); 832} 833 834static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) { 835 SkBitmap result; 836 837 AshmemPixelAllocator allocator(env); 838 if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { 839 return NULL; 840 } 841 auto bitmap = allocator.getStorageObjAndReset(); 842 bitmap->setImmutable(); 843 return bitmap; 844} 845 846static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) { 847 SkBitmap src; 848 reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); 849 SkColorType dstCT = src.colorType(); 850 auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT); 851 jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false)); 852 return ret; 853} 854 855static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle) { 856 SkBitmap src; 857 reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); 858 SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); 859 auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT); 860 jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false)); 861 return ret; 862} 863 864static void Bitmap_destruct(BitmapWrapper* bitmap) { 865 delete bitmap; 866} 867 868static jlong Bitmap_getNativeFinalizer(JNIEnv*, jobject) { 869 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Bitmap_destruct)); 870} 871 872static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { 873 LocalScopedBitmap bitmap(bitmapHandle); 874 bitmap->freePixels(); 875 return JNI_TRUE; 876} 877 878static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, 879 jint width, jint height, jint configHandle, jboolean requestPremul) { 880 LocalScopedBitmap bitmap(bitmapHandle); 881 bitmap->assertValid(); 882 SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); 883 884 // ARGB_4444 is a deprecated format, convert automatically to 8888 885 if (colorType == kARGB_4444_SkColorType) { 886 colorType = kN32_SkColorType; 887 } 888 size_t requestedSize = width * height * SkColorTypeBytesPerPixel(colorType); 889 if (requestedSize > bitmap->getAllocationByteCount()) { 890 // done in native as there's no way to get BytesPerPixel in Java 891 doThrowIAE(env, "Bitmap not large enough to support new configuration"); 892 return; 893 } 894 SkAlphaType alphaType; 895 if (bitmap->info().colorType() != kRGB_565_SkColorType 896 && bitmap->info().alphaType() == kOpaque_SkAlphaType) { 897 // If the original bitmap was set to opaque, keep that setting, unless it 898 // was 565, which is required to be opaque. 899 alphaType = kOpaque_SkAlphaType; 900 } else { 901 // Otherwise respect the premultiplied request. 902 alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; 903 } 904 bitmap->bitmap().reconfigure(SkImageInfo::Make(width, height, colorType, alphaType, 905 sk_ref_sp(bitmap->info().colorSpace()))); 906} 907 908// These must match the int values in Bitmap.java 909enum JavaEncodeFormat { 910 kJPEG_JavaEncodeFormat = 0, 911 kPNG_JavaEncodeFormat = 1, 912 kWEBP_JavaEncodeFormat = 2 913}; 914 915static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, 916 jint format, jint quality, 917 jobject jstream, jbyteArray jstorage) { 918 SkEncodedImageFormat fm; 919 switch (format) { 920 case kJPEG_JavaEncodeFormat: 921 fm = SkEncodedImageFormat::kJPEG; 922 break; 923 case kPNG_JavaEncodeFormat: 924 fm = SkEncodedImageFormat::kPNG; 925 break; 926 case kWEBP_JavaEncodeFormat: 927 fm = SkEncodedImageFormat::kWEBP; 928 break; 929 default: 930 return JNI_FALSE; 931 } 932 933 LocalScopedBitmap bitmap(bitmapHandle); 934 if (!bitmap.valid()) { 935 return JNI_FALSE; 936 } 937 938 std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage)); 939 if (!strm.get()) { 940 return JNI_FALSE; 941 } 942 943 SkBitmap skbitmap; 944 bitmap->getSkBitmap(&skbitmap); 945 return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE; 946} 947 948static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { 949 LocalScopedBitmap bitmap(bitmapHandle); 950 SkBitmap skBitmap; 951 bitmap->getSkBitmap(&skBitmap); 952 skBitmap.eraseColor(color); 953} 954 955static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { 956 LocalScopedBitmap bitmap(bitmapHandle); 957 return static_cast<jint>(bitmap->rowBytes()); 958} 959 960static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { 961 LocalScopedBitmap bitmap(bitmapHandle); 962 if (bitmap->isHardware()) { 963 return GraphicsJNI::hardwareLegacyBitmapConfig(); 964 } 965 return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType()); 966} 967 968static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { 969 LocalScopedBitmap bitmap(bitmapHandle); 970 return static_cast<jint>(bitmap->getGenerationID()); 971} 972 973static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { 974 LocalScopedBitmap bitmap(bitmapHandle); 975 if (bitmap->info().alphaType() == kPremul_SkAlphaType) { 976 return JNI_TRUE; 977 } 978 return JNI_FALSE; 979} 980 981static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { 982 LocalScopedBitmap bitmap(bitmapHandle); 983 return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE; 984} 985 986static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, 987 jboolean hasAlpha, jboolean requestPremul) { 988 LocalScopedBitmap bitmap(bitmapHandle); 989 if (hasAlpha) { 990 bitmap->setAlphaType( 991 requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); 992 } else { 993 bitmap->setAlphaType(kOpaque_SkAlphaType); 994 } 995} 996 997static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, 998 jboolean isPremul) { 999 LocalScopedBitmap bitmap(bitmapHandle); 1000 if (!bitmap->info().isOpaque()) { 1001 if (isPremul) { 1002 bitmap->setAlphaType(kPremul_SkAlphaType); 1003 } else { 1004 bitmap->setAlphaType(kUnpremul_SkAlphaType); 1005 } 1006 } 1007} 1008 1009static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { 1010 LocalScopedBitmap bitmap(bitmapHandle); 1011 return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; 1012} 1013 1014static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, 1015 jboolean hasMipMap) { 1016 LocalScopedBitmap bitmap(bitmapHandle); 1017 bitmap->setHasHardwareMipMap(hasMipMap); 1018} 1019 1020/////////////////////////////////////////////////////////////////////////////// 1021 1022// This is the maximum possible size because the SkColorSpace must be 1023// representable (and therefore serializable) using a matrix and numerical 1024// transfer function. If we allow more color space representations in the 1025// framework, we may need to update this maximum size. 1026static constexpr uint32_t kMaxColorSpaceSerializedBytes = 80; 1027 1028static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { 1029 if (parcel == NULL) { 1030 SkDebugf("-------- unparcel parcel is NULL\n"); 1031 return NULL; 1032 } 1033 1034 android::Parcel* p = android::parcelForJavaObject(env, parcel); 1035 1036 const bool isMutable = p->readInt32() != 0; 1037 const SkColorType colorType = (SkColorType)p->readInt32(); 1038 const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); 1039 const uint32_t colorSpaceSize = p->readUint32(); 1040 sk_sp<SkColorSpace> colorSpace; 1041 if (kRGBA_F16_SkColorType == colorType) { 1042 colorSpace = SkColorSpace::MakeSRGBLinear(); 1043 } else if (colorSpaceSize > 0) { 1044 if (colorSpaceSize > kMaxColorSpaceSerializedBytes) { 1045 ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: " 1046 "%d bytes\n", colorSpaceSize); 1047 } 1048 1049 const void* data = p->readInplace(colorSpaceSize); 1050 if (data) { 1051 colorSpace = SkColorSpace::Deserialize(data, colorSpaceSize); 1052 } else { 1053 ALOGD("Bitmap_createFromParcel: Unable to read serialized SkColorSpace data\n"); 1054 } 1055 } 1056 const int width = p->readInt32(); 1057 const int height = p->readInt32(); 1058 const int rowBytes = p->readInt32(); 1059 const int density = p->readInt32(); 1060 1061 if (kN32_SkColorType != colorType && 1062 kRGBA_F16_SkColorType != colorType && 1063 kRGB_565_SkColorType != colorType && 1064 kARGB_4444_SkColorType != colorType && 1065 kIndex_8_SkColorType != colorType && 1066 kAlpha_8_SkColorType != colorType) { 1067 SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType); 1068 return NULL; 1069 } 1070 1071 std::unique_ptr<SkBitmap> bitmap(new SkBitmap); 1072 if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace), 1073 rowBytes)) { 1074 return NULL; 1075 } 1076 1077 SkColorTable* ctable = NULL; 1078 if (colorType == kIndex_8_SkColorType) { 1079 int count = p->readInt32(); 1080 if (count < 0 || count > 256) { 1081 // The data is corrupt, since SkColorTable enforces a value between 0 and 256, 1082 // inclusive. 1083 return NULL; 1084 } 1085 if (count > 0) { 1086 size_t size = count * sizeof(SkPMColor); 1087 const SkPMColor* src = (const SkPMColor*)p->readInplace(size); 1088 if (src == NULL) { 1089 return NULL; 1090 } 1091 ctable = new SkColorTable(src, count); 1092 } 1093 } 1094 1095 // Read the bitmap blob. 1096 size_t size = bitmap->getSize(); 1097 android::Parcel::ReadableBlob blob; 1098 android::status_t status = p->readBlob(size, &blob); 1099 if (status) { 1100 SkSafeUnref(ctable); 1101 doThrowRE(env, "Could not read bitmap blob."); 1102 return NULL; 1103 } 1104 1105 // Map the bitmap in place from the ashmem region if possible otherwise copy. 1106 sk_sp<Bitmap> nativeBitmap; 1107 if (blob.fd() >= 0 && (blob.isMutable() || !isMutable) && (size >= ASHMEM_BITMAP_MIN_SIZE)) { 1108#if DEBUG_PARCEL 1109 ALOGD("Bitmap.createFromParcel: mapped contents of %s bitmap from %s blob " 1110 "(fds %s)", 1111 isMutable ? "mutable" : "immutable", 1112 blob.isMutable() ? "mutable" : "immutable", 1113 p->allowFds() ? "allowed" : "forbidden"); 1114#endif 1115 // Dup the file descriptor so we can keep a reference to it after the Parcel 1116 // is disposed. 1117 int dupFd = dup(blob.fd()); 1118 if (dupFd < 0) { 1119 ALOGE("Error allocating dup fd. Error:%d", errno); 1120 blob.release(); 1121 SkSafeUnref(ctable); 1122 doThrowRE(env, "Could not allocate dup blob fd."); 1123 return NULL; 1124 } 1125 1126 // Map the pixels in place and take ownership of the ashmem region. 1127 nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(), 1128 ctable, dupFd, const_cast<void*>(blob.data()), size, !isMutable)); 1129 SkSafeUnref(ctable); 1130 if (!nativeBitmap) { 1131 close(dupFd); 1132 blob.release(); 1133 doThrowRE(env, "Could not allocate ashmem pixel ref."); 1134 return NULL; 1135 } 1136 1137 // Clear the blob handle, don't release it. 1138 blob.clear(); 1139 } else { 1140#if DEBUG_PARCEL 1141 if (blob.fd() >= 0) { 1142 ALOGD("Bitmap.createFromParcel: copied contents of mutable bitmap " 1143 "from immutable blob (fds %s)", 1144 p->allowFds() ? "allowed" : "forbidden"); 1145 } else { 1146 ALOGD("Bitmap.createFromParcel: copied contents from %s blob " 1147 "(fds %s)", 1148 blob.isMutable() ? "mutable" : "immutable", 1149 p->allowFds() ? "allowed" : "forbidden"); 1150 } 1151#endif 1152 1153 // Copy the pixels into a new buffer. 1154 nativeBitmap = Bitmap::allocateHeapBitmap(bitmap.get(), ctable); 1155 SkSafeUnref(ctable); 1156 if (!nativeBitmap) { 1157 blob.release(); 1158 doThrowRE(env, "Could not allocate java pixel ref."); 1159 return NULL; 1160 } 1161 bitmap->lockPixels(); 1162 memcpy(bitmap->getPixels(), blob.data(), size); 1163 bitmap->unlockPixels(); 1164 1165 // Release the blob handle. 1166 blob.release(); 1167 } 1168 1169 return createBitmap(env, nativeBitmap.release(), 1170 getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); 1171} 1172 1173static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, 1174 jlong bitmapHandle, 1175 jboolean isMutable, jint density, 1176 jobject parcel) { 1177 if (parcel == NULL) { 1178 SkDebugf("------- writeToParcel null parcel\n"); 1179 return JNI_FALSE; 1180 } 1181 1182 android::Parcel* p = android::parcelForJavaObject(env, parcel); 1183 SkBitmap bitmap; 1184 1185 auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle); 1186 bitmapWrapper->getSkBitmap(&bitmap); 1187 1188 p->writeInt32(isMutable); 1189 p->writeInt32(bitmap.colorType()); 1190 p->writeInt32(bitmap.alphaType()); 1191 SkColorSpace* colorSpace = bitmap.colorSpace(); 1192 if (colorSpace != nullptr && bitmap.colorType() != kRGBA_F16_SkColorType) { 1193 sk_sp<SkData> data = colorSpace->serialize(); 1194 size_t size = data->size(); 1195 p->writeUint32(size); 1196 if (size > 0) { 1197 if (size > kMaxColorSpaceSerializedBytes) { 1198 ALOGD("Bitmap_writeToParcel: Serialized SkColorSpace is larger than expected: " 1199 "%zu bytes\n", size); 1200 } 1201 1202 p->write(data->data(), size); 1203 } 1204 } else { 1205 p->writeUint32(0); 1206 } 1207 p->writeInt32(bitmap.width()); 1208 p->writeInt32(bitmap.height()); 1209 p->writeInt32(bitmap.rowBytes()); 1210 p->writeInt32(density); 1211 1212 if (bitmap.colorType() == kIndex_8_SkColorType) { 1213 // The bitmap needs to be locked to access its color table. 1214 SkAutoLockPixels alp(bitmap); 1215 SkColorTable* ctable = bitmap.getColorTable(); 1216 if (ctable != NULL) { 1217 int count = ctable->count(); 1218 p->writeInt32(count); 1219 memcpy(p->writeInplace(count * sizeof(SkPMColor)), 1220 ctable->readColors(), count * sizeof(SkPMColor)); 1221 } else { 1222 p->writeInt32(0); // indicate no ctable 1223 } 1224 } 1225 1226 // Transfer the underlying ashmem region if we have one and it's immutable. 1227 android::status_t status; 1228 int fd = bitmapWrapper->bitmap().getAshmemFd(); 1229 if (fd >= 0 && !isMutable && p->allowFds()) { 1230#if DEBUG_PARCEL 1231 ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as " 1232 "immutable blob (fds %s)", 1233 p->allowFds() ? "allowed" : "forbidden"); 1234#endif 1235 1236 status = p->writeDupImmutableBlobFileDescriptor(fd); 1237 if (status) { 1238 doThrowRE(env, "Could not write bitmap blob file descriptor."); 1239 return JNI_FALSE; 1240 } 1241 return JNI_TRUE; 1242 } 1243 1244 // Copy the bitmap to a new blob. 1245 bool mutableCopy = isMutable; 1246#if DEBUG_PARCEL 1247 ALOGD("Bitmap.writeToParcel: copying %s bitmap into new %s blob (fds %s)", 1248 isMutable ? "mutable" : "immutable", 1249 mutableCopy ? "mutable" : "immutable", 1250 p->allowFds() ? "allowed" : "forbidden"); 1251#endif 1252 1253 size_t size = bitmap.getSize(); 1254 android::Parcel::WritableBlob blob; 1255 status = p->writeBlob(size, mutableCopy, &blob); 1256 if (status) { 1257 doThrowRE(env, "Could not copy bitmap to parcel blob."); 1258 return JNI_FALSE; 1259 } 1260 1261 bitmap.lockPixels(); 1262 const void* pSrc = bitmap.getPixels(); 1263 if (pSrc == NULL) { 1264 memset(blob.data(), 0, size); 1265 } else { 1266 memcpy(blob.data(), pSrc, size); 1267 } 1268 bitmap.unlockPixels(); 1269 1270 blob.release(); 1271 return JNI_TRUE; 1272} 1273 1274static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, 1275 jlong srcHandle, jlong paintHandle, 1276 jintArray offsetXY) { 1277 SkBitmap src; 1278 reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); 1279 const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle); 1280 SkIPoint offset; 1281 SkBitmap dst; 1282 HeapAllocator allocator; 1283 1284 src.extractAlpha(&dst, paint, &allocator, &offset); 1285 // If Skia can't allocate pixels for destination bitmap, it resets 1286 // it, that is set its pixels buffer to NULL, and zero width and height. 1287 if (dst.getPixels() == NULL && src.getPixels() != NULL) { 1288 doThrowOOME(env, "failed to allocate pixels for alpha"); 1289 return NULL; 1290 } 1291 if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { 1292 int* array = env->GetIntArrayElements(offsetXY, NULL); 1293 array[0] = offset.fX; 1294 array[1] = offset.fY; 1295 env->ReleaseIntArrayElements(offsetXY, array, 0); 1296 } 1297 1298 return createBitmap(env, allocator.getStorageObjAndReset(), 1299 getPremulBitmapCreateFlags(true)); 1300} 1301 1302/////////////////////////////////////////////////////////////////////////////// 1303 1304static jboolean Bitmap_isSRGB(JNIEnv* env, jobject, jlong bitmapHandle) { 1305 LocalScopedBitmap bitmapHolder(bitmapHandle); 1306 if (!bitmapHolder.valid()) return JNI_TRUE; 1307 1308 SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); 1309 return GraphicsJNI::isColorSpaceSRGB(colorSpace); 1310} 1311 1312static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, 1313 jfloatArray xyzArray, jfloatArray paramsArray) { 1314 1315 LocalScopedBitmap bitmapHolder(bitmapHandle); 1316 if (!bitmapHolder.valid()) return JNI_FALSE; 1317 1318 SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); 1319 if (colorSpace == nullptr) return JNI_FALSE; 1320 1321 SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); 1322 if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE; 1323 1324 jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL); 1325 xyz[0] = xyzMatrix.getFloat(0, 0); 1326 xyz[1] = xyzMatrix.getFloat(1, 0); 1327 xyz[2] = xyzMatrix.getFloat(2, 0); 1328 xyz[3] = xyzMatrix.getFloat(0, 1); 1329 xyz[4] = xyzMatrix.getFloat(1, 1); 1330 xyz[5] = xyzMatrix.getFloat(2, 1); 1331 xyz[6] = xyzMatrix.getFloat(0, 2); 1332 xyz[7] = xyzMatrix.getFloat(1, 2); 1333 xyz[8] = xyzMatrix.getFloat(2, 2); 1334 env->ReleaseFloatArrayElements(xyzArray, xyz, 0); 1335 1336 SkColorSpaceTransferFn transferParams; 1337 if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE; 1338 1339 jfloat* params = env->GetFloatArrayElements(paramsArray, NULL); 1340 params[0] = transferParams.fA; 1341 params[1] = transferParams.fB; 1342 params[2] = transferParams.fC; 1343 params[3] = transferParams.fD; 1344 params[4] = transferParams.fE; 1345 params[5] = transferParams.fF; 1346 params[6] = transferParams.fG; 1347 env->ReleaseFloatArrayElements(paramsArray, params, 0); 1348 1349 return JNI_TRUE; 1350} 1351 1352/////////////////////////////////////////////////////////////////////////////// 1353 1354static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, 1355 jint x, jint y) { 1356 SkBitmap bitmap; 1357 reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); 1358 SkAutoLockPixels alp(bitmap); 1359 1360 ToColorProc proc = ChooseToColorProc(bitmap); 1361 if (NULL == proc) { 1362 return 0; 1363 } 1364 const void* src = bitmap.getAddr(x, y); 1365 if (NULL == src) { 1366 return 0; 1367 } 1368 1369 SkColor dst[1]; 1370 proc(dst, src, 1, bitmap.getColorTable()); 1371 1372 SkColorSpace* colorSpace = bitmap.colorSpace(); 1373 if (bitmap.colorType() != kRGBA_F16_SkColorType && 1374 !GraphicsJNI::isColorSpaceSRGB(colorSpace)) { 1375 auto sRGB = SkColorSpace::MakeSRGB(); 1376 auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get()); 1377 xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0], 1378 SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0], 1, 1379 SkAlphaType::kUnpremul_SkAlphaType); 1380 } 1381 1382 return static_cast<jint>(dst[0]); 1383} 1384 1385static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, 1386 jintArray pixelArray, jint offset, jint stride, 1387 jint x, jint y, jint width, jint height) { 1388 SkBitmap bitmap; 1389 reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); 1390 SkAutoLockPixels alp(bitmap); 1391 1392 ToColorProc proc = ChooseToColorProc(bitmap); 1393 if (NULL == proc) { 1394 return; 1395 } 1396 const void* src = bitmap.getAddr(x, y); 1397 if (NULL == src) { 1398 return; 1399 } 1400 1401 SkColorTable* ctable = bitmap.getColorTable(); 1402 jint* dst = env->GetIntArrayElements(pixelArray, NULL); 1403 SkColor* d = (SkColor*)dst + offset; 1404 1405 SkColorSpace* colorSpace = bitmap.colorSpace(); 1406 if (bitmap.colorType() == kRGBA_F16_SkColorType || 1407 GraphicsJNI::isColorSpaceSRGB(colorSpace)) { 1408 while (--height >= 0) { 1409 proc(d, src, width, ctable); 1410 d += stride; 1411 src = (void*)((const char*)src + bitmap.rowBytes()); 1412 } 1413 } else { 1414 auto sRGB = SkColorSpace::MakeSRGB(); 1415 auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get()); 1416 1417 while (--height >= 0) { 1418 proc(d, src, width, ctable); 1419 1420 xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, d, 1421 SkColorSpaceXform::kBGRA_8888_ColorFormat, d, width, 1422 SkAlphaType::kUnpremul_SkAlphaType); 1423 1424 d += stride; 1425 src = (void*)((const char*)src + bitmap.rowBytes()); 1426 } 1427 } 1428 1429 env->ReleaseIntArrayElements(pixelArray, dst, 0); 1430} 1431 1432/////////////////////////////////////////////////////////////////////////////// 1433 1434static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, 1435 jint x, jint y, jint colorHandle) { 1436 SkBitmap bitmap; 1437 reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); 1438 SkColor color = static_cast<SkColor>(colorHandle); 1439 SkAutoLockPixels alp(bitmap); 1440 if (NULL == bitmap.getPixels()) { 1441 return; 1442 } 1443 1444 FromColorProc proc = ChooseFromColorProc(bitmap); 1445 if (NULL == proc) { 1446 return; 1447 } 1448 1449 SkColorSpace* colorSpace = bitmap.colorSpace(); 1450 if (bitmap.colorType() != kRGBA_F16_SkColorType && 1451 !GraphicsJNI::isColorSpaceSRGB(colorSpace)) { 1452 auto sRGB = SkColorSpace::MakeSRGB(); 1453 auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace); 1454 xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &color, 1455 SkColorSpaceXform::kBGRA_8888_ColorFormat, &color, 1, 1456 SkAlphaType::kUnpremul_SkAlphaType); 1457 } 1458 1459 proc(bitmap.getAddr(x, y), &color, 1, x, y); 1460 bitmap.notifyPixelsChanged(); 1461} 1462 1463static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, 1464 jintArray pixelArray, jint offset, jint stride, 1465 jint x, jint y, jint width, jint height) { 1466 SkBitmap bitmap; 1467 reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); 1468 GraphicsJNI::SetPixels(env, pixelArray, offset, stride, 1469 x, y, width, height, bitmap); 1470} 1471 1472static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, 1473 jlong bitmapHandle, jobject jbuffer) { 1474 SkBitmap bitmap; 1475 reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); 1476 SkAutoLockPixels alp(bitmap); 1477 const void* src = bitmap.getPixels(); 1478 1479 if (NULL != src) { 1480 android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); 1481 1482 // the java side has already checked that buffer is large enough 1483 memcpy(abp.pointer(), src, bitmap.getSize()); 1484 } 1485} 1486 1487static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, 1488 jlong bitmapHandle, jobject jbuffer) { 1489 SkBitmap bitmap; 1490 reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); 1491 SkAutoLockPixels alp(bitmap); 1492 void* dst = bitmap.getPixels(); 1493 1494 if (NULL != dst) { 1495 android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); 1496 // the java side has already checked that buffer is large enough 1497 memcpy(dst, abp.pointer(), bitmap.getSize()); 1498 bitmap.notifyPixelsChanged(); 1499 } 1500} 1501 1502static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) { 1503 SkBitmap bm0; 1504 SkBitmap bm1; 1505 1506 LocalScopedBitmap bitmap0(bm0Handle); 1507 LocalScopedBitmap bitmap1(bm1Handle); 1508 1509 // Paying the price for making Hardware Bitmap as Config: 1510 // later check for colorType will pass successfully, 1511 // because Hardware Config internally may be RGBA8888 or smth like that. 1512 if (bitmap0->isHardware() != bitmap1->isHardware()) { 1513 return JNI_FALSE; 1514 } 1515 1516 bitmap0->bitmap().getSkBitmap(&bm0); 1517 bitmap1->bitmap().getSkBitmap(&bm1); 1518 if (bm0.width() != bm1.width() 1519 || bm0.height() != bm1.height() 1520 || bm0.colorType() != bm1.colorType() 1521 || bm0.alphaType() != bm1.alphaType() 1522 || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) { 1523 return JNI_FALSE; 1524 } 1525 1526 SkAutoLockPixels alp0(bm0); 1527 SkAutoLockPixels alp1(bm1); 1528 1529 // if we can't load the pixels, return false 1530 if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) { 1531 return JNI_FALSE; 1532 } 1533 1534 if (bm0.colorType() == kIndex_8_SkColorType) { 1535 SkColorTable* ct0 = bm0.getColorTable(); 1536 SkColorTable* ct1 = bm1.getColorTable(); 1537 if (NULL == ct0 || NULL == ct1) { 1538 return JNI_FALSE; 1539 } 1540 if (ct0->count() != ct1->count()) { 1541 return JNI_FALSE; 1542 } 1543 1544 const size_t size = ct0->count() * sizeof(SkPMColor); 1545 if (memcmp(ct0->readColors(), ct1->readColors(), size) != 0) { 1546 return JNI_FALSE; 1547 } 1548 } 1549 1550 // now compare each scanline. We can't do the entire buffer at once, 1551 // since we don't care about the pixel values that might extend beyond 1552 // the width (since the scanline might be larger than the logical width) 1553 const int h = bm0.height(); 1554 const size_t size = bm0.width() * bm0.bytesPerPixel(); 1555 for (int y = 0; y < h; y++) { 1556 // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config 1557 // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 1558 // and bm1 both have pixel data() (have passed NULL == getPixels() check), 1559 // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE 1560 // to warn user those 2 unrecognized config bitmaps may be different. 1561 void *bm0Addr = bm0.getAddr(0, y); 1562 void *bm1Addr = bm1.getAddr(0, y); 1563 1564 if(bm0Addr == NULL || bm1Addr == NULL) { 1565 return JNI_FALSE; 1566 } 1567 1568 if (memcmp(bm0Addr, bm1Addr, size) != 0) { 1569 return JNI_FALSE; 1570 } 1571 } 1572 return JNI_TRUE; 1573} 1574 1575static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) { 1576 LocalScopedBitmap bitmapHandle(bitmapPtr); 1577 if (!bitmapHandle.valid()) return; 1578 android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmapHandle->bitmap()); 1579} 1580 1581static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) { 1582 LocalScopedBitmap bitmapHandle(bitmapPtr); 1583 return static_cast<jint>(bitmapHandle->getAllocationByteCount()); 1584} 1585 1586static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bitmapPtr) { 1587 LocalScopedBitmap bitmapHandle(bitmapPtr); 1588 LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), 1589 "Hardware config is only supported config in Bitmap_nativeCopyPreserveInternalConfig"); 1590 Bitmap& hwuiBitmap = bitmapHandle->bitmap(); 1591 SkBitmap src; 1592 hwuiBitmap.getSkBitmap(&src); 1593 1594 SkBitmap result; 1595 HeapAllocator allocator; 1596 if (!src.copyTo(&result, hwuiBitmap.info().colorType(), &allocator)) { 1597 doThrowRE(env, "Could not copy a hardware bitmap."); 1598 return NULL; 1599 } 1600 return createBitmap(env, allocator.getStorageObjAndReset(), getPremulBitmapCreateFlags(false)); 1601} 1602 1603static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) { 1604 sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer)); 1605 sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer); 1606 if (!bitmap.get()) { 1607 ALOGW("failed to create hardware bitmap from graphic buffer"); 1608 return NULL; 1609 } 1610 return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); 1611} 1612 1613static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitmapPtr) { 1614 LocalScopedBitmap bitmapHandle(bitmapPtr); 1615 LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), 1616 "Hardware config is only supported config in Bitmap_getGraphicBuffer"); 1617 1618 Bitmap& hwuiBitmap = bitmapHandle->bitmap(); 1619 sp<GraphicBuffer> buffer(hwuiBitmap.graphicBuffer()); 1620 return createJavaGraphicBuffer(env, buffer); 1621} 1622 1623static void Bitmap_copyColorSpace(JNIEnv* env, jobject, jlong srcBitmapPtr, jlong dstBitmapPtr) { 1624 LocalScopedBitmap srcBitmapHandle(srcBitmapPtr); 1625 LocalScopedBitmap dstBitmapHandle(dstBitmapPtr); 1626 1627 dstBitmapHandle->bitmap().setColorSpace(srcBitmapHandle->bitmap().info().refColorSpace()); 1628} 1629 1630/////////////////////////////////////////////////////////////////////////////// 1631 1632static const JNINativeMethod gBitmapMethods[] = { 1633 { "nativeCreate", "([IIIIIIZ[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;", 1634 (void*)Bitmap_creator }, 1635 { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", 1636 (void*)Bitmap_copy }, 1637 { "nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;", 1638 (void*)Bitmap_copyAshmem }, 1639 { "nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;", 1640 (void*)Bitmap_copyAshmemConfig }, 1641 { "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer }, 1642 { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle }, 1643 { "nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure }, 1644 { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", 1645 (void*)Bitmap_compress }, 1646 { "nativeErase", "(JI)V", (void*)Bitmap_erase }, 1647 { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, 1648 { "nativeConfig", "(J)I", (void*)Bitmap_config }, 1649 { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, 1650 { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, 1651 { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, 1652 { "nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied}, 1653 { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, 1654 { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, 1655 { "nativeCreateFromParcel", 1656 "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", 1657 (void*)Bitmap_createFromParcel }, 1658 { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z", 1659 (void*)Bitmap_writeToParcel }, 1660 { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", 1661 (void*)Bitmap_extractAlpha }, 1662 { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId }, 1663 { "nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel }, 1664 { "nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels }, 1665 { "nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel }, 1666 { "nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels }, 1667 { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", 1668 (void*)Bitmap_copyPixelsToBuffer }, 1669 { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", 1670 (void*)Bitmap_copyPixelsFromBuffer }, 1671 { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, 1672 { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, 1673 { "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount }, 1674 { "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;", 1675 (void*)Bitmap_copyPreserveInternalConfig }, 1676 { "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;", 1677 (void*) Bitmap_createHardwareBitmap }, 1678 { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", 1679 (void*) Bitmap_createGraphicBufferHandle }, 1680 { "nativeGetColorSpace", "(J[F[F)Z", (void*)Bitmap_getColorSpace }, 1681 { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, 1682 { "nativeCopyColorSpace", "(JJ)V", 1683 (void*)Bitmap_copyColorSpace }, 1684}; 1685 1686int register_android_graphics_Bitmap(JNIEnv* env) 1687{ 1688 gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap")); 1689 gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J"); 1690 gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V"); 1691 gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); 1692 gBitmap_getAllocationByteCountMethodID = GetMethodIDOrDie(env, gBitmap_class, "getAllocationByteCount", "()I"); 1693 return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, 1694 NELEM(gBitmapMethods)); 1695} 1696