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