BitmapFactory.cpp revision 17154417e8ad488d18d9133bf802f598e7506483
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, bool ditherImage) { 318 SkImageRef* 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 pr->setDitherImage(ditherImage); 326 bitmap->setPixelRef(pr)->unref(); 327 return pr; 328} 329 330// since we "may" create a purgeable imageref, we require the stream be ref'able 331// i.e. dynamically allocated, since its lifetime may exceed the current stack 332// frame. 333static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, 334 jobject options, bool allowPurgeable) { 335 int sampleSize = 1; 336 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; 337 SkBitmap::Config prefConfig = SkBitmap::kNo_Config; 338 bool doDither = true; 339 bool isPurgeable = allowPurgeable && optionsPurgeable(env, options); 340 bool reportSizeToVM = optionsReportSizeToVM(env, options); 341 342 if (NULL != options) { 343 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 344 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { 345 mode = SkImageDecoder::kDecodeBounds_Mode; 346 } 347 // initialize these, in case we fail later on 348 env->SetIntField(options, gOptions_widthFieldID, -1); 349 env->SetIntField(options, gOptions_heightFieldID, -1); 350 env->SetObjectField(options, gOptions_mimeFieldID, 0); 351 352 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 353 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 354 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 355 } 356 357 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 358 if (NULL == decoder) { 359 return nullObjectReturn("SkImageDecoder::Factory returned null"); 360 } 361 362 decoder->setSampleSize(sampleSize); 363 decoder->setDitherImage(doDither); 364 365 NinePatchPeeker peeker; 366 JavaPixelAllocator javaAllocator(env, reportSizeToVM); 367 SkBitmap* bitmap = new SkBitmap; 368 Res_png_9patch dummy9Patch; 369 370 SkAutoTDelete<SkImageDecoder> add(decoder); 371 SkAutoTDelete<SkBitmap> adb(bitmap); 372 373 decoder->setPeeker(&peeker); 374 if (!isPurgeable) { 375 decoder->setAllocator(&javaAllocator); 376 } 377 378 AutoDecoderCancel adc(options, decoder); 379 380 // To fix the race condition in case "requestCancelDecode" 381 // happens earlier than AutoDecoderCancel object is added 382 // to the gAutoDecoderCancelMutex linked list. 383 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 384 return nullObjectReturn("gOptions_mCancelID");; 385 } 386 387 SkImageDecoder::Mode decodeMode = mode; 388 if (isPurgeable) { 389 decodeMode = SkImageDecoder::kDecodeBounds_Mode; 390 } 391 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) { 392 return nullObjectReturn("decoder->decode returned false"); 393 } 394 395 // update options (if any) 396 if (NULL != options) { 397 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 398 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 399 // TODO: set the mimeType field with the data from the codec. 400 // but how to reuse a set of strings, rather than allocating new one 401 // each time? 402 env->SetObjectField(options, gOptions_mimeFieldID, 403 getMimeTypeString(env, decoder->getFormat())); 404 } 405 406 // if we're in justBounds mode, return now (skip the java bitmap) 407 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 408 return NULL; 409 } 410 411 jbyteArray ninePatchChunk = NULL; 412 if (peeker.fPatchIsValid) { 413 size_t ninePatchArraySize = peeker.fPatch->serializedSize(); 414 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 415 if (NULL == ninePatchChunk) { 416 return nullObjectReturn("ninePatchChunk == null"); 417 } 418 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, 419 NULL); 420 if (NULL == array) { 421 return nullObjectReturn("primitive array == null"); 422 } 423 peeker.fPatch->serialize(array); 424 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 425 } 426 427 // detach bitmap from its autotdeleter, since we want to own it now 428 adb.detach(); 429 430 if (padding) { 431 if (peeker.fPatchIsValid) { 432 GraphicsJNI::set_jrect(env, padding, 433 peeker.fPatch->paddingLeft, 434 peeker.fPatch->paddingTop, 435 peeker.fPatch->paddingRight, 436 peeker.fPatch->paddingBottom); 437 } else { 438 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 439 } 440 } 441 442 SkPixelRef* pr; 443 if (isPurgeable) { 444 pr = installPixelRef(bitmap, stream, sampleSize, doDither); 445 } else { 446 // if we get here, we're in kDecodePixels_Mode and will therefore 447 // already have a pixelref installed. 448 pr = bitmap->pixelRef(); 449 } 450 // promise we will never change our pixels (great for sharing and pictures) 451 pr->setImmutable(); 452 // now create the java bitmap 453 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); 454} 455 456static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, 457 jobject is, // InputStream 458 jbyteArray storage, // byte[] 459 jobject padding, 460 jobject options) { // BitmapFactory$Options 461 jobject bitmap = NULL; 462 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage); 463 464 if (stream) { 465 // for now we don't allow purgeable with java inputstreams 466 bitmap = doDecode(env, stream, padding, options, false); 467 stream->unref(); 468 } 469 return bitmap; 470} 471 472static ssize_t getFDSize(int fd) { 473 off_t curr = ::lseek(fd, 0, SEEK_CUR); 474 if (curr < 0) { 475 return 0; 476 } 477 size_t size = ::lseek(fd, 0, SEEK_END); 478 ::lseek(fd, curr, SEEK_SET); 479 return size; 480} 481 482/** Restore the file descriptor's offset in our destructor 483 */ 484class AutoFDSeek { 485public: 486 AutoFDSeek(int fd) : fFD(fd) { 487 fCurr = ::lseek(fd, 0, SEEK_CUR); 488 } 489 ~AutoFDSeek() { 490 if (fCurr >= 0) { 491 ::lseek(fFD, fCurr, SEEK_SET); 492 } 493 } 494private: 495 int fFD; 496 off_t fCurr; 497}; 498 499static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, 500 jobject fileDescriptor, 501 jobject padding, 502 jobject bitmapFactoryOptions) { 503 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 504 505 jint descriptor = env->GetIntField(fileDescriptor, 506 gFileDescriptor_descriptor); 507 508 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); 509 bool isShareable = optionsShareable(env, bitmapFactoryOptions); 510 bool weOwnTheFD = false; 511 if (isPurgeable && isShareable) { 512 int newFD = ::dup(descriptor); 513 if (-1 != newFD) { 514 weOwnTheFD = true; 515 descriptor = newFD; 516 } 517 } 518 519 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); 520 SkAutoUnref aur(stream); 521 if (!stream->isValid()) { 522 return NULL; 523 } 524 525 /* Restore our offset when we leave, so we can be called more than once 526 with the same descriptor. This is only required if we didn't dup the 527 file descriptor, but it is OK to do it all the time. 528 */ 529 AutoFDSeek as(descriptor); 530 531 /* Allow purgeable iff we own the FD, i.e., in the puregeable and 532 shareable case. 533 */ 534 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); 535} 536 537/* make a deep copy of the asset, and return it as a stream, or NULL if there 538 was an error. 539 */ 540static SkStream* copyAssetToStream(Asset* asset) { 541 // if we could "ref/reopen" the asset, we may not need to copy it here 542 off_t size = asset->seek(0, SEEK_SET); 543 if ((off_t)-1 == size) { 544 SkDebugf("---- copyAsset: asset rewind failed\n"); 545 return NULL; 546 } 547 548 size = asset->getLength(); 549 if (size <= 0) { 550 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); 551 return NULL; 552 } 553 554 SkStream* stream = new SkMemoryStream(size); 555 void* data = const_cast<void*>(stream->getMemoryBase()); 556 off_t len = asset->read(data, size); 557 if (len != size) { 558 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); 559 delete stream; 560 stream = NULL; 561 } 562 return stream; 563} 564 565static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, 566 jint native_asset, // Asset 567 jobject padding, // Rect 568 jobject options) { // BitmapFactory$Options 569 SkStream* stream; 570 Asset* asset = reinterpret_cast<Asset*>(native_asset); 571 572 if (optionsPurgeable(env, options)) { 573 // if we could "ref/reopen" the asset, we may not need to copy it here 574 // and we could assume optionsShareable, since assets are always RO 575 stream = copyAssetToStream(asset); 576 if (NULL == stream) { 577 return NULL; 578 } 579 } else { 580 // since we know we'll be done with the asset when we return, we can 581 // just use a simple wrapper 582 stream = new AssetStreamAdaptor(asset); 583 } 584 SkAutoUnref aur(stream); 585 return doDecode(env, stream, padding, options, true); 586} 587 588static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 589 int offset, int length, jobject options) { 590 /* If optionsShareable() we could decide to just wrap the java array and 591 share it, but that means adding a globalref to the java array object 592 and managing its lifetime. For now we just always copy the array's data 593 if optionsPurgeable(). 594 */ 595 AutoJavaByteArray ar(env, byteArray); 596 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, 597 optionsPurgeable(env, options)); 598 SkAutoUnref aur(stream); 599 return doDecode(env, stream, NULL, options, true); 600} 601 602static void nativeRequestCancel(JNIEnv*, jobject joptions) { 603 (void)AutoDecoderCancel::RequestCancel(joptions); 604} 605 606static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 607 jobject padding) { 608 609 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 610 if (array != NULL) { 611 size_t chunkSize = env->GetArrayLength(chunkObject); 612 void* storage = alloca(chunkSize); 613 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 614 memcpy(chunk, array, chunkSize); 615 android::Res_png_9patch::deserialize(chunk); 616 617 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 618 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 619 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 620 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 621 622 for (int i = 0; i < chunk->numXDivs; i++) { 623 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 624 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 625 chunk->xDivs[i]++; 626 } 627 } 628 629 for (int i = 0; i < chunk->numYDivs; i++) { 630 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 631 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 632 chunk->yDivs[i]++; 633 } 634 } 635 636 memcpy(array, chunk, chunkSize); 637 638 if (padding) { 639 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 640 chunk->paddingRight, chunk->paddingBottom); 641 } 642 643 env->ReleaseByteArrayElements(chunkObject, array, 0); 644 } 645 return chunkObject; 646} 647 648/////////////////////////////////////////////////////////////////////////////// 649 650static JNINativeMethod gMethods[] = { 651 { "nativeDecodeStream", 652 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 653 (void*)nativeDecodeStream 654 }, 655 656 { "nativeDecodeFileDescriptor", 657 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 658 (void*)nativeDecodeFileDescriptor 659 }, 660 661 { "nativeDecodeAsset", 662 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 663 (void*)nativeDecodeAsset 664 }, 665 666 { "nativeDecodeByteArray", 667 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 668 (void*)nativeDecodeByteArray 669 }, 670 671 { "nativeScaleNinePatch", 672 "([BFLandroid/graphics/Rect;)[B", 673 (void*)nativeScaleNinePatch 674 } 675 676}; 677 678static JNINativeMethod gOptionsMethods[] = { 679 { "requestCancel", "()V", (void*)nativeRequestCancel } 680}; 681 682static jclass make_globalref(JNIEnv* env, const char classname[]) { 683 jclass c = env->FindClass(classname); 684 SkASSERT(c); 685 return (jclass)env->NewGlobalRef(c); 686} 687 688static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 689 const char fieldname[], const char type[]) { 690 jfieldID id = env->GetFieldID(clazz, fieldname, type); 691 SkASSERT(id); 692 return id; 693} 694 695#define kClassPathName "android/graphics/BitmapFactory" 696 697#define RETURN_ERR_IF_NULL(value) \ 698 do { if (!(value)) { assert(0); return -1; } } while (false) 699 700int register_android_graphics_BitmapFactory(JNIEnv* env); 701int register_android_graphics_BitmapFactory(JNIEnv* env) { 702 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); 703 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); 704 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); 705 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", 706 "Landroid/graphics/Bitmap$Config;"); 707 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); 708 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); 709 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); 710 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z"); 711 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); 712 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); 713 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); 714 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z"); 715 716 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 717 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 718 719 int ret = AndroidRuntime::registerNativeMethods(env, 720 "android/graphics/BitmapFactory$Options", 721 gOptionsMethods, 722 SK_ARRAY_COUNT(gOptionsMethods)); 723 if (ret) { 724 return ret; 725 } 726 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 727 gMethods, SK_ARRAY_COUNT(gMethods)); 728} 729