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