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