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