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