BitmapFactory.cpp revision c70e06bbfac0d92ec218a32e35d9d7fa80f23cc9
1#define LOG_TAG "BitmapFactory" 2 3#include "SkImageDecoder.h" 4#include "SkImageRef_ashmem.h" 5#include "SkImageRef_GlobalPool.h" 6#include "SkPixelRef.h" 7#include "SkStream.h" 8#include "GraphicsJNI.h" 9#include "SkTemplates.h" 10#include "SkUtils.h" 11#include "CreateJavaOutputStreamAdaptor.h" 12 13#include <android_runtime/AndroidRuntime.h> 14#include <utils/Asset.h> 15#include <utils/ResourceTypes.h> 16#include <netinet/in.h> 17#include <sys/mman.h> 18 19static jclass gOptions_class; 20static jfieldID gOptions_justBoundsFieldID; 21static jfieldID gOptions_sampleSizeFieldID; 22static jfieldID gOptions_configFieldID; 23static jfieldID gOptions_ditherFieldID; 24static jfieldID gOptions_purgeableFieldID; 25static jfieldID gOptions_shareableFieldID; 26static jfieldID gOptions_widthFieldID; 27static jfieldID gOptions_heightFieldID; 28static jfieldID gOptions_mimeFieldID; 29static jfieldID gOptions_mCancelID; 30 31static jclass gFileDescriptor_class; 32static jfieldID gFileDescriptor_descriptor; 33 34#if 0 35 #define TRACE_BITMAP(code) code 36#else 37 #define TRACE_BITMAP(code) 38#endif 39 40/////////////////////////////////////////////////////////////////////////////// 41 42class AutoDecoderCancel { 43public: 44 AutoDecoderCancel(jobject options, SkImageDecoder* decoder); 45 ~AutoDecoderCancel(); 46 47 static bool RequestCancel(jobject options); 48 49private: 50 AutoDecoderCancel* fNext; 51 AutoDecoderCancel* fPrev; 52 jobject fJOptions; // java options object 53 SkImageDecoder* fDecoder; 54 55#ifdef SK_DEBUG 56 static void Validate(); 57#else 58 static void Validate() {} 59#endif 60}; 61 62static SkMutex gAutoDecoderCancelMutex; 63static AutoDecoderCancel* gAutoDecoderCancel; 64#ifdef SK_DEBUG 65 static int gAutoDecoderCancelCount; 66#endif 67 68AutoDecoderCancel::AutoDecoderCancel(jobject joptions, 69 SkImageDecoder* decoder) { 70 fJOptions = joptions; 71 fDecoder = decoder; 72 73 if (NULL != joptions) { 74 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); 75 76 // Add us as the head of the list 77 fPrev = NULL; 78 fNext = gAutoDecoderCancel; 79 if (gAutoDecoderCancel) { 80 gAutoDecoderCancel->fPrev = this; 81 } 82 gAutoDecoderCancel = this; 83 84 SkDEBUGCODE(gAutoDecoderCancelCount += 1;) 85 Validate(); 86 } 87} 88 89AutoDecoderCancel::~AutoDecoderCancel() { 90 if (NULL != fJOptions) { 91 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); 92 93 // take us out of the dllist 94 AutoDecoderCancel* prev = fPrev; 95 AutoDecoderCancel* next = fNext; 96 97 if (prev) { 98 SkASSERT(prev->fNext == this); 99 prev->fNext = next; 100 } else { 101 SkASSERT(gAutoDecoderCancel == this); 102 gAutoDecoderCancel = next; 103 } 104 if (next) { 105 SkASSERT(next->fPrev == this); 106 next->fPrev = prev; 107 } 108 109 SkDEBUGCODE(gAutoDecoderCancelCount -= 1;) 110 Validate(); 111 } 112} 113 114bool AutoDecoderCancel::RequestCancel(jobject joptions) { 115 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); 116 117 Validate(); 118 119 AutoDecoderCancel* pair = gAutoDecoderCancel; 120 while (pair != NULL) { 121 if (pair->fJOptions == joptions) { 122 pair->fDecoder->cancelDecode(); 123 return true; 124 } 125 pair = pair->fNext; 126 } 127 return false; 128} 129 130#ifdef SK_DEBUG 131// can only call this inside a lock on gAutoDecoderCancelMutex 132void AutoDecoderCancel::Validate() { 133 const int gCount = gAutoDecoderCancelCount; 134 135 if (gCount == 0) { 136 SkASSERT(gAutoDecoderCancel == NULL); 137 } else { 138 SkASSERT(gCount > 0); 139 140 AutoDecoderCancel* curr = gAutoDecoderCancel; 141 SkASSERT(curr); 142 SkASSERT(curr->fPrev == NULL); 143 144 int count = 0; 145 while (curr) { 146 count += 1; 147 SkASSERT(count <= gCount); 148 if (curr->fPrev) { 149 SkASSERT(curr->fPrev->fNext == curr); 150 } 151 if (curr->fNext) { 152 SkASSERT(curr->fNext->fPrev == curr); 153 } 154 curr = curr->fNext; 155 } 156 SkASSERT(count == gCount); 157 } 158} 159#endif 160 161/////////////////////////////////////////////////////////////////////////////// 162 163using namespace android; 164 165class NinePatchPeeker : public SkImageDecoder::Peeker { 166public: 167 NinePatchPeeker() { 168 fPatchIsValid = false; 169 } 170 171 ~NinePatchPeeker() { 172 if (fPatchIsValid) { 173 free(fPatch); 174 } 175 } 176 177 bool fPatchIsValid; 178 Res_png_9patch* fPatch; 179 180 virtual bool peek(const char tag[], const void* data, size_t length) { 181 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) { 182 Res_png_9patch* patch = (Res_png_9patch*) data; 183 size_t patchSize = patch->serializedSize(); 184 assert(length == patchSize); 185 // You have to copy the data because it is owned by the png reader 186 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize); 187 memcpy(patchNew, patch, patchSize); 188 // this relies on deserialization being done in place 189 Res_png_9patch::deserialize(patchNew); 190 patchNew->fileToDevice(); 191 if (fPatchIsValid) { 192 free(fPatch); 193 } 194 fPatch = patchNew; 195 //printf("9patch: (%d,%d)-(%d,%d)\n", 196 // fPatch.sizeLeft, fPatch.sizeTop, 197 // fPatch.sizeRight, fPatch.sizeBottom); 198 fPatchIsValid = true; 199 } else { 200 fPatch = NULL; 201 } 202 return true; // keep on decoding 203 } 204}; 205 206class AssetStreamAdaptor : public SkStream { 207public: 208 AssetStreamAdaptor(Asset* a) : fAsset(a) {} 209 210 virtual bool rewind() { 211 off_t pos = fAsset->seek(0, SEEK_SET); 212 if (pos == (off_t)-1) { 213 SkDebugf("----- fAsset->seek(rewind) failed\n"); 214 return false; 215 } 216 return true; 217 } 218 219 virtual size_t read(void* buffer, size_t size) { 220 ssize_t amount; 221 222 if (NULL == buffer) { 223 if (0 == size) { // caller is asking us for our total length 224 return fAsset->getLength(); 225 } 226 // asset->seek returns new total offset 227 // we want to return amount that was skipped 228 229 off_t oldOffset = fAsset->seek(0, SEEK_CUR); 230 if (-1 == oldOffset) { 231 SkDebugf("---- fAsset->seek(oldOffset) failed\n"); 232 return 0; 233 } 234 off_t newOffset = fAsset->seek(size, SEEK_CUR); 235 if (-1 == newOffset) { 236 SkDebugf("---- fAsset->seek(%d) failed\n", size); 237 return 0; 238 } 239 amount = newOffset - oldOffset; 240 } else { 241 amount = fAsset->read(buffer, size); 242 if (amount <= 0) { 243 SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount); 244 } 245 } 246 247 if (amount < 0) { 248 amount = 0; 249 } 250 return amount; 251 } 252 253private: 254 Asset* fAsset; 255}; 256 257/////////////////////////////////////////////////////////////////////////////// 258 259static inline int32_t validOrNeg1(bool isValid, int32_t value) { 260// return isValid ? value : -1; 261 SkASSERT((int)isValid == 0 || (int)isValid == 1); 262 return ((int32_t)isValid - 1) | value; 263} 264 265static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { 266 static const struct { 267 SkImageDecoder::Format fFormat; 268 const char* fMimeType; 269 } gMimeTypes[] = { 270 { SkImageDecoder::kBMP_Format, "image/bmp" }, 271 { SkImageDecoder::kGIF_Format, "image/gif" }, 272 { SkImageDecoder::kICO_Format, "image/x-ico" }, 273 { SkImageDecoder::kJPEG_Format, "image/jpeg" }, 274 { SkImageDecoder::kPNG_Format, "image/png" }, 275 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" } 276 }; 277 278 const char* cstr = NULL; 279 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) { 280 if (gMimeTypes[i].fFormat == format) { 281 cstr = gMimeTypes[i].fMimeType; 282 break; 283 } 284 } 285 286 jstring jstr = 0; 287 if (NULL != cstr) { 288 jstr = env->NewStringUTF(cstr); 289 } 290 return jstr; 291} 292 293static bool optionsPurgeable(JNIEnv* env, jobject options) { 294 return options != NULL && 295 env->GetBooleanField(options, gOptions_purgeableFieldID); 296} 297 298static bool optionsShareable(JNIEnv* env, jobject options) { 299 return options != NULL && 300 env->GetBooleanField(options, gOptions_shareableFieldID); 301} 302 303static jobject nullObjectReturn(const char msg[]) { 304 if (msg) { 305 SkDebugf("--- %s\n", msg); 306 } 307 return NULL; 308} 309 310static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, 311 int sampleSize) { 312 SkPixelRef* pr; 313 // only use ashmem for large images, since mmaps come at a price 314 if (bitmap->getSize() >= 32 * 65536) { 315 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); 316 } else { 317 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); 318 } 319 bitmap->setPixelRef(pr)->unref(); 320 return pr; 321} 322 323// since we "may" create a purgeable imageref, we require the stream be ref'able 324// i.e. dynamically allocated, since its lifetime may exceed the current stack 325// frame. 326static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, 327 jobject options, bool allowPurgeable) { 328 int sampleSize = 1; 329 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; 330 SkBitmap::Config prefConfig = SkBitmap::kNo_Config; 331 bool doDither = true; 332 bool isPurgeable = allowPurgeable && optionsPurgeable(env, options); 333 334 if (NULL != options) { 335 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 336 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { 337 mode = SkImageDecoder::kDecodeBounds_Mode; 338 } 339 // initialize these, in case we fail later on 340 env->SetIntField(options, gOptions_widthFieldID, -1); 341 env->SetIntField(options, gOptions_heightFieldID, -1); 342 env->SetObjectField(options, gOptions_mimeFieldID, 0); 343 344 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 345 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 346 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 347 } 348 349 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 350 if (NULL == decoder) { 351 return nullObjectReturn("SkImageDecoder::Factory returned null"); 352 } 353 354 decoder->setSampleSize(sampleSize); 355 decoder->setDitherImage(doDither); 356 357 NinePatchPeeker peeker; 358 JavaPixelAllocator javaAllocator(env); 359 SkBitmap* bitmap = new SkBitmap; 360 Res_png_9patch dummy9Patch; 361 362 SkAutoTDelete<SkImageDecoder> add(decoder); 363 SkAutoTDelete<SkBitmap> adb(bitmap); 364 365 decoder->setPeeker(&peeker); 366 if (!isPurgeable) { 367 decoder->setAllocator(&javaAllocator); 368 } 369 370 AutoDecoderCancel adc(options, decoder); 371 372 // To fix the race condition in case "requestCancelDecode" 373 // happens earlier than AutoDecoderCancel object is added 374 // to the gAutoDecoderCancelMutex linked list. 375 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 376 return nullObjectReturn("gOptions_mCancelID");; 377 } 378 379 SkImageDecoder::Mode decodeMode = mode; 380 if (isPurgeable) { 381 decodeMode = SkImageDecoder::kDecodeBounds_Mode; 382 } 383 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) { 384 return nullObjectReturn("decoder->decode returned false"); 385 } 386 387 // update options (if any) 388 if (NULL != options) { 389 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 390 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 391 // TODO: set the mimeType field with the data from the codec. 392 // but how to reuse a set of strings, rather than allocating new one 393 // each time? 394 env->SetObjectField(options, gOptions_mimeFieldID, 395 getMimeTypeString(env, decoder->getFormat())); 396 } 397 398 // if we're in justBounds mode, return now (skip the java bitmap) 399 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 400 return NULL; 401 } 402 403 jbyteArray ninePatchChunk = NULL; 404 if (peeker.fPatchIsValid) { 405 size_t ninePatchArraySize = peeker.fPatch->serializedSize(); 406 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 407 if (NULL == ninePatchChunk) { 408 return nullObjectReturn("ninePatchChunk == null"); 409 } 410 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, 411 NULL); 412 if (NULL == array) { 413 return nullObjectReturn("primitive array == null"); 414 } 415 peeker.fPatch->serialize(array); 416 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 417 } 418 419 // detach bitmap from its autotdeleter, since we want to own it now 420 adb.detach(); 421 422 if (padding) { 423 if (peeker.fPatchIsValid) { 424 GraphicsJNI::set_jrect(env, padding, 425 peeker.fPatch->paddingLeft, 426 peeker.fPatch->paddingTop, 427 peeker.fPatch->paddingRight, 428 peeker.fPatch->paddingBottom); 429 } else { 430 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 431 } 432 } 433 434 SkPixelRef* pr; 435 if (isPurgeable) { 436 pr = installPixelRef(bitmap, stream, sampleSize); 437 } else { 438 // if we get here, we're in kDecodePixels_Mode and will therefore 439 // already have a pixelref installed. 440 pr = bitmap->pixelRef(); 441 } 442 // promise we will never change our pixels (great for sharing and pictures) 443 pr->setImmutable(); 444 // now create the java bitmap 445 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); 446} 447 448static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, 449 jobject is, // InputStream 450 jbyteArray storage, // byte[] 451 jobject padding, 452 jobject options) { // BitmapFactory$Options 453 jobject bitmap = NULL; 454 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage); 455 456 if (stream) { 457 // for now we don't allow purgeable with java inputstreams 458 bitmap = doDecode(env, stream, padding, options, false); 459 stream->unref(); 460 } 461 return bitmap; 462} 463 464static ssize_t getFDSize(int fd) { 465 off_t curr = ::lseek(fd, 0, SEEK_CUR); 466 if (curr < 0) { 467 return 0; 468 } 469 size_t size = ::lseek(fd, 0, SEEK_END); 470 ::lseek(fd, curr, SEEK_SET); 471 return size; 472} 473 474/** Restore the file descriptor's offset in our destructor 475 */ 476class AutoFDSeek { 477public: 478 AutoFDSeek(int fd) : fFD(fd) { 479 fCurr = ::lseek(fd, 0, SEEK_CUR); 480 } 481 ~AutoFDSeek() { 482 if (fCurr >= 0) { 483 ::lseek(fFD, fCurr, SEEK_SET); 484 } 485 } 486private: 487 int fFD; 488 off_t fCurr; 489}; 490 491static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, 492 jobject fileDescriptor, 493 jobject padding, 494 jobject bitmapFactoryOptions) { 495 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 496 497 jint descriptor = env->GetIntField(fileDescriptor, 498 gFileDescriptor_descriptor); 499 500 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); 501 bool isShareable = optionsShareable(env, bitmapFactoryOptions); 502 bool weOwnTheFD = false; 503 if (isPurgeable && isShareable) { 504 int newFD = ::dup(descriptor); 505 if (-1 != newFD) { 506 weOwnTheFD = true; 507 descriptor = newFD; 508 } 509 } 510 511 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); 512 SkAutoUnref aur(stream); 513 if (!stream->isValid()) { 514 return NULL; 515 } 516 517 /* Restore our offset when we leave, so we can be called more than once 518 with the same descriptor. This is only required if we didn't dup the 519 file descriptor, but it is OK to do it all the time. 520 */ 521 AutoFDSeek as(descriptor); 522 523 return doDecode(env, stream, padding, bitmapFactoryOptions, true); 524} 525 526/* make a deep copy of the asset, and return it as a stream, or NULL if there 527 was an error. 528 */ 529static SkStream* copyAssetToStream(Asset* asset) { 530 // if we could "ref/reopen" the asset, we may not need to copy it here 531 off_t size = asset->seek(0, SEEK_SET); 532 if ((off_t)-1 == size) { 533 SkDebugf("---- copyAsset: asset rewind failed\n"); 534 return NULL; 535 } 536 537 size = asset->getLength(); 538 if (size <= 0) { 539 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); 540 return NULL; 541 } 542 543 SkStream* stream = new SkMemoryStream(size); 544 void* data = const_cast<void*>(stream->getMemoryBase()); 545 off_t len = asset->read(data, size); 546 if (len != size) { 547 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); 548 delete stream; 549 stream = NULL; 550 } 551 return stream; 552} 553 554static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, 555 jint native_asset, // Asset 556 jobject padding, // Rect 557 jobject options) { // BitmapFactory$Options 558 SkStream* stream; 559 Asset* asset = reinterpret_cast<Asset*>(native_asset); 560 561 if (optionsPurgeable(env, options)) { 562 // if we could "ref/reopen" the asset, we may not need to copy it here 563 // and we could assume optionsShareable, since assets are always RO 564 stream = copyAssetToStream(asset); 565 if (NULL == stream) { 566 return NULL; 567 } 568 } else { 569 // since we know we'll be done with the asset when we return, we can 570 // just use a simple wrapper 571 stream = new AssetStreamAdaptor(asset); 572 } 573 SkAutoUnref aur(stream); 574 return doDecode(env, stream, padding, options, true); 575} 576 577static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 578 int offset, int length, jobject options) { 579 /* If optionsShareable() we could decide to just wrap the java array and 580 share it, but that means adding a globalref to the java array object 581 and managing its lifetime. For now we just always copy the array's data 582 if optionsPurgeable(). 583 */ 584 AutoJavaByteArray ar(env, byteArray); 585 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, 586 optionsPurgeable(env, options)); 587 SkAutoUnref aur(stream); 588 return doDecode(env, stream, NULL, options, true); 589} 590 591static void nativeRequestCancel(JNIEnv*, jobject joptions) { 592 (void)AutoDecoderCancel::RequestCancel(joptions); 593} 594 595static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 596 jobject padding) { 597 598 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 599 if (array != NULL) { 600 size_t chunkSize = env->GetArrayLength(chunkObject); 601 void* storage = alloca(chunkSize); 602 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 603 memcpy(chunk, array, chunkSize); 604 android::Res_png_9patch::deserialize(chunk); 605 606 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 607 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 608 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 609 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 610 611 for (int i = 0; i < chunk->numXDivs; i++) { 612 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 613 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 614 chunk->xDivs[i]++; 615 } 616 } 617 618 for (int i = 0; i < chunk->numYDivs; i++) { 619 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 620 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 621 chunk->yDivs[i]++; 622 } 623 } 624 625 memcpy(array, chunk, chunkSize); 626 627 if (padding) { 628 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 629 chunk->paddingRight, chunk->paddingBottom); 630 } 631 632 env->ReleaseByteArrayElements(chunkObject, array, 0); 633 } 634 return chunkObject; 635} 636 637/////////////////////////////////////////////////////////////////////////////// 638 639static JNINativeMethod gMethods[] = { 640 { "nativeDecodeStream", 641 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 642 (void*)nativeDecodeStream 643 }, 644 645 { "nativeDecodeFileDescriptor", 646 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 647 (void*)nativeDecodeFileDescriptor 648 }, 649 650 { "nativeDecodeAsset", 651 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 652 (void*)nativeDecodeAsset 653 }, 654 655 { "nativeDecodeByteArray", 656 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 657 (void*)nativeDecodeByteArray 658 }, 659 660 { "nativeScaleNinePatch", 661 "([BFLandroid/graphics/Rect;)[B", 662 (void*)nativeScaleNinePatch 663 } 664 665}; 666 667static JNINativeMethod gOptionsMethods[] = { 668 { "requestCancel", "()V", (void*)nativeRequestCancel } 669}; 670 671static jclass make_globalref(JNIEnv* env, const char classname[]) { 672 jclass c = env->FindClass(classname); 673 SkASSERT(c); 674 return (jclass)env->NewGlobalRef(c); 675} 676 677static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 678 const char fieldname[], const char type[]) { 679 jfieldID id = env->GetFieldID(clazz, fieldname, type); 680 SkASSERT(id); 681 return id; 682} 683 684#define kClassPathName "android/graphics/BitmapFactory" 685 686#define RETURN_ERR_IF_NULL(value) \ 687 do { if (!(value)) { assert(0); return -1; } } while (false) 688 689int register_android_graphics_BitmapFactory(JNIEnv* env); 690int register_android_graphics_BitmapFactory(JNIEnv* env) { 691 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); 692 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); 693 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); 694 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", 695 "Landroid/graphics/Bitmap$Config;"); 696 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); 697 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); 698 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); 699 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); 700 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); 701 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); 702 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z"); 703 704 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 705 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 706 707 int ret = AndroidRuntime::registerNativeMethods(env, 708 "android/graphics/BitmapFactory$Options", 709 gOptionsMethods, 710 SK_ARRAY_COUNT(gOptionsMethods)); 711 if (ret) { 712 return ret; 713 } 714 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 715 gMethods, SK_ARRAY_COUNT(gMethods)); 716} 717