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