BitmapFactory.cpp revision 288471d8a57e1c318742cbfc28697877436fdb87
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 if (optionsPurgeable(env, options)) { 469 // if we could "ref/reopen" the asset, we may not need to copy it here 470 // and we could assume optionsShareable, since assets are always RO 471 stream = copyAssetToStream(asset); 472 if (NULL == stream) { 473 return NULL; 474 } 475 } else { 476 // since we know we'll be done with the asset when we return, we can 477 // just use a simple wrapper 478 stream = new AssetStreamAdaptor(asset); 479 } 480 SkAutoUnref aur(stream); 481 return doDecode(env, stream, padding, options, true, forcePurgeable); 482} 483 484static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 485 int offset, int length, jobject options) { 486 /* If optionsShareable() we could decide to just wrap the java array and 487 share it, but that means adding a globalref to the java array object 488 and managing its lifetime. For now we just always copy the array's data 489 if optionsPurgeable(). 490 */ 491 AutoJavaByteArray ar(env, byteArray); 492 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, 493 optionsPurgeable(env, options)); 494 SkAutoUnref aur(stream); 495 return doDecode(env, stream, NULL, options, true); 496} 497 498static void nativeRequestCancel(JNIEnv*, jobject joptions) { 499 (void)AutoDecoderCancel::RequestCancel(joptions); 500} 501 502static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 503 jobject padding) { 504 505 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 506 if (array != NULL) { 507 size_t chunkSize = env->GetArrayLength(chunkObject); 508 void* storage = alloca(chunkSize); 509 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 510 memcpy(chunk, array, chunkSize); 511 android::Res_png_9patch::deserialize(chunk); 512 513 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 514 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 515 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 516 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 517 518 for (int i = 0; i < chunk->numXDivs; i++) { 519 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 520 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 521 chunk->xDivs[i]++; 522 } 523 } 524 525 for (int i = 0; i < chunk->numYDivs; i++) { 526 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 527 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 528 chunk->yDivs[i]++; 529 } 530 } 531 532 memcpy(array, chunk, chunkSize); 533 534 if (padding) { 535 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 536 chunk->paddingRight, chunk->paddingBottom); 537 } 538 539 env->ReleaseByteArrayElements(chunkObject, array, 0); 540 } 541 return chunkObject; 542} 543 544static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) { 545 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig); 546 547 // these are the only default configs that make sense for codecs right now 548 static const SkBitmap::Config gValidDefConfig[] = { 549 SkBitmap::kRGB_565_Config, 550 SkBitmap::kARGB_8888_Config, 551 }; 552 553 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) { 554 if (config == gValidDefConfig[i]) { 555 SkImageDecoder::SetDeviceConfig(config); 556 break; 557 } 558 } 559} 560 561static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) { 562 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 563 int width, height; 564 if (NULL == decoder) { 565 doThrowIOE(env, "Image format not supported"); 566 return nullObjectReturn("SkImageDecoder::Factory returned null"); 567 } 568 569 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); 570 decoder->setAllocator(javaAllocator); 571 JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); 572 decoder->setReporter(javaMemoryReporter); 573 javaAllocator->unref(); 574 javaMemoryReporter->unref(); 575 576 if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) { 577 char msg[1024]; 578 snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName()); 579 doThrowIOE(env, msg); 580 return nullObjectReturn("decoder->buildTileIndex returned false"); 581 } 582 583 SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height); 584 585 return GraphicsJNI::createLargeBitmap(env, bm); 586} 587 588static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 589 int offset, int length, jboolean isShareable) { 590 AutoJavaByteArray ar(env, byteArray); 591 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false); 592 SkAutoUnref aur(stream); 593 if (isShareable) { 594 aur.detach(); 595 } 596 return doBuildTileIndex(env, stream, isShareable); 597} 598 599static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz, 600 jobject fileDescriptor, jboolean isShareable) { 601 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 602 603 jint descriptor = env->GetIntField(fileDescriptor, 604 gFileDescriptor_descriptor); 605 bool weOwnTheFD = false; 606 607 if (isShareable) { 608 int newFD = ::dup(descriptor); 609 if (-1 != newFD) { 610 weOwnTheFD = true; 611 descriptor = newFD; 612 } 613 } 614 615 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); 616 SkAutoUnref aur(stream); 617 if (!stream->isValid()) { 618 return NULL; 619 } 620 621 if (isShareable) { 622 aur.detach(); 623 } 624 625 /* Restore our offset when we leave, so we can be called more than once 626 with the same descriptor. This is only required if we didn't dup the 627 file descriptor, but it is OK to do it all the time. 628 */ 629 AutoFDSeek as(descriptor); 630 631 return doBuildTileIndex(env, stream, isShareable); 632} 633 634static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz, 635 jobject is, // InputStream 636 jbyteArray storage, // byte[] 637 jboolean isShareable) { 638 jobject largeBitmap = NULL; 639 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024); 640 641 if (stream) { 642 // for now we don't allow shareable with java inputstreams 643 largeBitmap = doBuildTileIndex(env, stream, false); 644 stream->unref(); 645 } 646 return largeBitmap; 647} 648 649static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz, 650 jint native_asset, // Asset 651 jboolean isShareable) { 652 SkStream* stream; 653 Asset* asset = reinterpret_cast<Asset*>(native_asset); 654 stream = new AssetStreamAdaptor(asset); 655 SkAutoUnref aur(stream); 656 if (isShareable) { 657 aur.detach(); 658 } 659 return doBuildTileIndex(env, stream, isShareable); 660} 661 662/////////////////////////////////////////////////////////////////////////////// 663 664static JNINativeMethod gMethods[] = { 665 { "nativeDecodeStream", 666 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 667 (void*)nativeDecodeStream 668 }, 669 670 { "nativeDecodeFileDescriptor", 671 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 672 (void*)nativeDecodeFileDescriptor 673 }, 674 675 { "nativeDecodeAsset", 676 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 677 (void*)nativeDecodeAsset 678 }, 679 680 { "nativeDecodeByteArray", 681 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 682 (void*)nativeDecodeByteArray 683 }, 684 685 { "nativeScaleNinePatch", 686 "([BFLandroid/graphics/Rect;)[B", 687 (void*)nativeScaleNinePatch 688 }, 689 690 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig }, 691 692 { "nativeCreateLargeBitmap", 693 "([BIIZ)Landroid/graphics/LargeBitmap;", 694 (void*)nativeCreateLargeBitmapFromByteArray 695 }, 696 697 { "nativeCreateLargeBitmap", 698 "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;", 699 (void*)nativeCreateLargeBitmapFromStream 700 }, 701 702 { "nativeCreateLargeBitmap", 703 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;", 704 (void*)nativeCreateLargeBitmapFromFileDescriptor 705 }, 706 707 { "nativeCreateLargeBitmap", 708 "(IZ)Landroid/graphics/LargeBitmap;", 709 (void*)nativeCreateLargeBitmapFromAsset 710 }, 711}; 712 713static JNINativeMethod gOptionsMethods[] = { 714 { "requestCancel", "()V", (void*)nativeRequestCancel } 715}; 716 717static jclass make_globalref(JNIEnv* env, const char classname[]) { 718 jclass c = env->FindClass(classname); 719 SkASSERT(c); 720 return (jclass)env->NewGlobalRef(c); 721} 722 723static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 724 const char fieldname[], const char type[]) { 725 jfieldID id = env->GetFieldID(clazz, fieldname, type); 726 SkASSERT(id); 727 return id; 728} 729 730#define kClassPathName "android/graphics/BitmapFactory" 731 732#define RETURN_ERR_IF_NULL(value) \ 733 do { if (!(value)) { assert(0); return -1; } } while (false) 734 735int register_android_graphics_BitmapFactory(JNIEnv* env); 736int register_android_graphics_BitmapFactory(JNIEnv* env) { 737 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); 738 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); 739 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); 740 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", 741 "Landroid/graphics/Bitmap$Config;"); 742 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); 743 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); 744 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); 745 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z"); 746 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); 747 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); 748 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); 749 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z"); 750 751 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 752 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 753 754 int ret = AndroidRuntime::registerNativeMethods(env, 755 "android/graphics/BitmapFactory$Options", 756 gOptionsMethods, 757 SK_ARRAY_COUNT(gOptionsMethods)); 758 if (ret) { 759 return ret; 760 } 761 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 762 gMethods, SK_ARRAY_COUNT(gMethods)); 763} 764