BitmapFactory.cpp revision 4e5ec34e98faa8338ca04282a160ef2b0d13bc9d
1#define LOG_TAG "BitmapFactory" 2 3#include "BitmapFactory.h" 4#include "CreateJavaOutputStreamAdaptor.h" 5#include "GraphicsJNI.h" 6#include "NinePatchPeeker.h" 7#include "SkAndroidCodec.h" 8#include "SkBRDAllocator.h" 9#include "SkFrontBufferedStream.h" 10#include "SkImageDecoder.h" 11#include "SkMath.h" 12#include "SkPixelRef.h" 13#include "SkStream.h" 14#include "SkUtils.h" 15#include "Utils.h" 16#include "core_jni_helpers.h" 17 18#include <JNIHelp.h> 19#include <androidfw/Asset.h> 20#include <androidfw/ResourceTypes.h> 21#include <cutils/compiler.h> 22#include <memory> 23#include <netinet/in.h> 24#include <stdio.h> 25#include <sys/mman.h> 26#include <sys/stat.h> 27 28jfieldID gOptions_justBoundsFieldID; 29jfieldID gOptions_sampleSizeFieldID; 30jfieldID gOptions_configFieldID; 31jfieldID gOptions_premultipliedFieldID; 32jfieldID gOptions_mutableFieldID; 33jfieldID gOptions_ditherFieldID; 34jfieldID gOptions_preferQualityOverSpeedFieldID; 35jfieldID gOptions_scaledFieldID; 36jfieldID gOptions_densityFieldID; 37jfieldID gOptions_screenDensityFieldID; 38jfieldID gOptions_targetDensityFieldID; 39jfieldID gOptions_widthFieldID; 40jfieldID gOptions_heightFieldID; 41jfieldID gOptions_mimeFieldID; 42jfieldID gOptions_mCancelID; 43jfieldID gOptions_bitmapFieldID; 44 45jfieldID gBitmap_ninePatchInsetsFieldID; 46 47jclass gInsetStruct_class; 48jmethodID gInsetStruct_constructorMethodID; 49 50using namespace android; 51 52jstring encodedFormatToString(JNIEnv* env, SkEncodedFormat format) { 53 const char* mimeType; 54 switch (format) { 55 case SkEncodedFormat::kBMP_SkEncodedFormat: 56 mimeType = "image/bmp"; 57 break; 58 case SkEncodedFormat::kGIF_SkEncodedFormat: 59 mimeType = "image/gif"; 60 break; 61 case SkEncodedFormat::kICO_SkEncodedFormat: 62 mimeType = "image/x-ico"; 63 break; 64 case SkEncodedFormat::kJPEG_SkEncodedFormat: 65 mimeType = "image/jpeg"; 66 break; 67 case SkEncodedFormat::kPNG_SkEncodedFormat: 68 mimeType = "image/png"; 69 break; 70 case SkEncodedFormat::kWEBP_SkEncodedFormat: 71 mimeType = "image/webp"; 72 break; 73 case SkEncodedFormat::kWBMP_SkEncodedFormat: 74 mimeType = "image/vnd.wap.wbmp"; 75 break; 76 default: 77 mimeType = nullptr; 78 break; 79 } 80 81 jstring jstr = nullptr; 82 if (mimeType) { 83 // NOTE: Caller should env->ExceptionCheck() for OOM 84 // (can't check for nullptr as it's a valid return value) 85 jstr = env->NewStringUTF(mimeType); 86 } 87 return jstr; 88} 89 90static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) { 91 for (int i = 0; i < count; i++) { 92 divs[i] = int32_t(divs[i] * scale + 0.5f); 93 if (i > 0 && divs[i] == divs[i - 1]) { 94 divs[i]++; // avoid collisions 95 } 96 } 97 98 if (CC_UNLIKELY(divs[count - 1] > maxValue)) { 99 // if the collision avoidance above put some divs outside the bounds of the bitmap, 100 // slide outer stretchable divs inward to stay within bounds 101 int highestAvailable = maxValue; 102 for (int i = count - 1; i >= 0; i--) { 103 divs[i] = highestAvailable; 104 if (i > 0 && divs[i] <= divs[i-1]){ 105 // keep shifting 106 highestAvailable = divs[i] - 1; 107 } else { 108 break; 109 } 110 } 111 } 112} 113 114static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale, 115 int scaledWidth, int scaledHeight) { 116 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 117 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 118 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 119 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 120 121 scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth); 122 scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight); 123} 124 125static SkColorType colorTypeForScaledOutput(SkColorType colorType) { 126 switch (colorType) { 127 case kUnknown_SkColorType: 128 case kIndex_8_SkColorType: 129 return kN32_SkColorType; 130 default: 131 break; 132 } 133 return colorType; 134} 135 136class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { 137public: 138 ScaleCheckingAllocator(float scale, int size) 139 : mScale(scale), mSize(size) { 140 } 141 142 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 143 // accounts for scale in final allocation, using eventual size and config 144 const int bytesPerPixel = SkColorTypeBytesPerPixel( 145 colorTypeForScaledOutput(bitmap->colorType())); 146 const int requestedSize = bytesPerPixel * 147 int(bitmap->width() * mScale + 0.5f) * 148 int(bitmap->height() * mScale + 0.5f); 149 if (requestedSize > mSize) { 150 ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)", 151 mSize, requestedSize); 152 return false; 153 } 154 return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable); 155 } 156private: 157 const float mScale; 158 const int mSize; 159}; 160 161class RecyclingPixelAllocator : public SkBitmap::Allocator { 162public: 163 RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size) 164 : mBitmap(bitmap), mSize(size) { 165 } 166 167 ~RecyclingPixelAllocator() { 168 } 169 170 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 171 const SkImageInfo& info = bitmap->info(); 172 if (info.colorType() == kUnknown_SkColorType) { 173 ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration"); 174 return false; 175 } 176 177 const int64_t size64 = info.getSafeSize64(bitmap->rowBytes()); 178 if (!sk_64_isS32(size64)) { 179 ALOGW("bitmap is too large"); 180 return false; 181 } 182 183 const size_t size = sk_64_asS32(size64); 184 if (size > mSize) { 185 ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap " 186 "(%zu bytes)", mSize, size); 187 return false; 188 } 189 190 mBitmap->reconfigure(info, bitmap->rowBytes(), ctable); 191 bitmap->setPixelRef(mBitmap->refPixelRef())->unref(); 192 193 // since we're already allocated, we lockPixels right away 194 // HeapAllocator/JavaPixelAllocator behaves this way too 195 bitmap->lockPixels(); 196 return true; 197 } 198 199private: 200 android::Bitmap* const mBitmap; 201 const unsigned int mSize; 202}; 203 204// Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize 205// (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the 206// scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how 207// best to round. 208static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) { 209 if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) { 210 return true; 211 } else if ((fullSize / sampleSize + 1) != decodedSize && 212 (fullSize / sampleSize) != decodedSize) { 213 return true; 214 } 215 return false; 216} 217 218static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, 219 const int sampleSize) { 220 return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) || 221 needsFineScale(fullSize.height(), decodedSize.height(), sampleSize); 222} 223 224static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { 225 // This function takes ownership of the input stream. Since the SkAndroidCodec 226 // will take ownership of the stream, we don't necessarily need to take ownership 227 // here. This is a precaution - if we were to return before creating the codec, 228 // we need to make sure that we delete the stream. 229 std::unique_ptr<SkStreamRewindable> streamDeleter(stream); 230 231 // Set default values for the options parameters. 232 int sampleSize = 1; 233 bool onlyDecodeSize = false; 234 SkColorType prefColorType = kN32_SkColorType; 235 bool isMutable = false; 236 float scale = 1.0f; 237 bool requireUnpremultiplied = false; 238 jobject javaBitmap = NULL; 239 240 // Update with options supplied by the client. 241 if (options != NULL) { 242 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 243 // Correct a non-positive sampleSize. sampleSize defaults to zero within the 244 // options object, which is strange. 245 if (sampleSize <= 0) { 246 sampleSize = 1; 247 } 248 249 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { 250 onlyDecodeSize = true; 251 } 252 253 // initialize these, in case we fail later on 254 env->SetIntField(options, gOptions_widthFieldID, -1); 255 env->SetIntField(options, gOptions_heightFieldID, -1); 256 env->SetObjectField(options, gOptions_mimeFieldID, 0); 257 258 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 259 prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); 260 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); 261 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); 262 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 263 264 if (env->GetBooleanField(options, gOptions_scaledFieldID)) { 265 const int density = env->GetIntField(options, gOptions_densityFieldID); 266 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID); 267 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); 268 if (density != 0 && targetDensity != 0 && density != screenDensity) { 269 scale = (float) targetDensity / density; 270 } 271 } 272 } 273 274 // Create the codec. 275 NinePatchPeeker peeker; 276 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(), 277 &peeker)); 278 if (!codec.get()) { 279 return nullObjectReturn("SkAndroidCodec::NewFromStream returned null"); 280 } 281 282 // Do not allow ninepatch decodes to 565. In the past, decodes to 565 283 // would dither, and we do not want to pre-dither ninepatches, since we 284 // know that they will be stretched. We no longer dither 565 decodes, 285 // but we continue to prevent ninepatches from decoding to 565, in order 286 // to maintain the old behavior. 287 if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) { 288 prefColorType = kN32_SkColorType; 289 } 290 291 // Determine the output size. 292 SkISize size = codec->getSampledDimensions(sampleSize); 293 294 int scaledWidth = size.width(); 295 int scaledHeight = size.height(); 296 bool willScale = false; 297 298 // Apply a fine scaling step if necessary. 299 if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) { 300 willScale = true; 301 scaledWidth = codec->getInfo().width() / sampleSize; 302 scaledHeight = codec->getInfo().height() / sampleSize; 303 } 304 305 // Set the options and return if the client only wants the size. 306 if (options != NULL) { 307 jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat()); 308 if (env->ExceptionCheck()) { 309 return nullObjectReturn("OOM in encodedFormatToString()"); 310 } 311 env->SetIntField(options, gOptions_widthFieldID, scaledWidth); 312 env->SetIntField(options, gOptions_heightFieldID, scaledHeight); 313 env->SetObjectField(options, gOptions_mimeFieldID, mimeType); 314 315 if (onlyDecodeSize) { 316 return nullptr; 317 } 318 } 319 320 // Scale is necessary due to density differences. 321 if (scale != 1.0f) { 322 willScale = true; 323 scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f); 324 scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f); 325 } 326 327 android::Bitmap* reuseBitmap = nullptr; 328 unsigned int existingBufferSize = 0; 329 if (javaBitmap != NULL) { 330 reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap); 331 if (reuseBitmap->peekAtPixelRef()->isImmutable()) { 332 ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); 333 javaBitmap = NULL; 334 reuseBitmap = nullptr; 335 } else { 336 existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); 337 } 338 } 339 340 JavaPixelAllocator javaAllocator(env); 341 RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize); 342 ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); 343 SkBitmap::HeapAllocator heapAllocator; 344 SkBitmap::Allocator* decodeAllocator; 345 if (javaBitmap != nullptr && willScale) { 346 // This will allocate pixels using a HeapAllocator, since there will be an extra 347 // scaling step that copies these pixels into Java memory. This allocator 348 // also checks that the recycled javaBitmap is large enough. 349 decodeAllocator = &scaleCheckingAllocator; 350 } else if (javaBitmap != nullptr) { 351 decodeAllocator = &recyclingAllocator; 352 } else if (willScale) { 353 // This will allocate pixels using a HeapAllocator, since there will be an extra 354 // scaling step that copies these pixels into Java memory. 355 decodeAllocator = &heapAllocator; 356 } else { 357 decodeAllocator = &javaAllocator; 358 } 359 360 // Set the decode colorType. This is necessary because we can't always support 361 // the requested colorType. 362 SkColorType decodeColorType = codec->computeOutputColorType(prefColorType); 363 364 // Construct a color table for the decode if necessary 365 SkAutoTUnref<SkColorTable> colorTable(nullptr); 366 SkPMColor* colorPtr = nullptr; 367 int* colorCount = nullptr; 368 int maxColors = 256; 369 SkPMColor colors[256]; 370 if (kIndex_8_SkColorType == decodeColorType) { 371 colorTable.reset(new SkColorTable(colors, maxColors)); 372 373 // SkColorTable expects us to initialize all of the colors before creating an 374 // SkColorTable. However, we are using SkBitmap with an Allocator to allocate 375 // memory for the decode, so we need to create the SkColorTable before decoding. 376 // It is safe for SkAndroidCodec to modify the colors because this SkBitmap is 377 // not being used elsewhere. 378 colorPtr = const_cast<SkPMColor*>(colorTable->readColors()); 379 colorCount = &maxColors; 380 } 381 382 // Set the alpha type for the decode. 383 SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); 384 385 const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType, 386 alphaType); 387 SkImageInfo bitmapInfo = decodeInfo; 388 if (decodeColorType == kGray_8_SkColorType) { 389 // The legacy implementation of BitmapFactory used kAlpha8 for 390 // grayscale images (before kGray8 existed). While the codec 391 // recognizes kGray8, we need to decode into a kAlpha8 bitmap 392 // in order to avoid a behavior change. 393 bitmapInfo = SkImageInfo::MakeA8(size.width(), size.height()); 394 } 395 SkBitmap decodingBitmap; 396 if (!decodingBitmap.setInfo(bitmapInfo) || 397 !decodingBitmap.tryAllocPixels(decodeAllocator, colorTable)) { 398 // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo() 399 // should only only fail if the calculated value for rowBytes is too 400 // large. 401 // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the 402 // native heap, or the recycled javaBitmap being too small to reuse. 403 return nullptr; 404 } 405 406 // Use SkAndroidCodec to perform the decode. 407 SkAndroidCodec::AndroidOptions codecOptions; 408 codecOptions.fZeroInitialized = (decodeAllocator == &javaAllocator) ? 409 SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized; 410 codecOptions.fColorPtr = colorPtr; 411 codecOptions.fColorCount = colorCount; 412 codecOptions.fSampleSize = sampleSize; 413 SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(), 414 decodingBitmap.rowBytes(), &codecOptions); 415 switch (result) { 416 case SkCodec::kSuccess: 417 case SkCodec::kIncompleteInput: 418 break; 419 default: 420 return nullObjectReturn("codec->getAndroidPixels() failed."); 421 } 422 423 jbyteArray ninePatchChunk = NULL; 424 if (peeker.mPatch != NULL) { 425 if (willScale) { 426 scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight); 427 } 428 429 size_t ninePatchArraySize = peeker.mPatch->serializedSize(); 430 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 431 if (ninePatchChunk == NULL) { 432 return nullObjectReturn("ninePatchChunk == null"); 433 } 434 435 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); 436 if (array == NULL) { 437 return nullObjectReturn("primitive array == null"); 438 } 439 440 memcpy(array, peeker.mPatch, peeker.mPatchSize); 441 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 442 } 443 444 jobject ninePatchInsets = NULL; 445 if (peeker.mHasInsets) { 446 ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, 447 peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], 448 peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], 449 peeker.mOutlineRadius, peeker.mOutlineAlpha, scale); 450 if (ninePatchInsets == NULL) { 451 return nullObjectReturn("nine patch insets == null"); 452 } 453 if (javaBitmap != NULL) { 454 env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets); 455 } 456 } 457 458 SkBitmap outputBitmap; 459 if (willScale) { 460 // This is weird so let me explain: we could use the scale parameter 461 // directly, but for historical reasons this is how the corresponding 462 // Dalvik code has always behaved. We simply recreate the behavior here. 463 // The result is slightly different from simply using scale because of 464 // the 0.5f rounding bias applied when computing the target image size 465 const float sx = scaledWidth / float(decodingBitmap.width()); 466 const float sy = scaledHeight / float(decodingBitmap.height()); 467 468 // Set the allocator for the outputBitmap. 469 SkBitmap::Allocator* outputAllocator; 470 if (javaBitmap != nullptr) { 471 outputAllocator = &recyclingAllocator; 472 } else { 473 outputAllocator = &javaAllocator; 474 } 475 476 SkColorType scaledColorType = colorTypeForScaledOutput(decodingBitmap.colorType()); 477 // FIXME: If the alphaType is kUnpremul and the image has alpha, the 478 // colors may not be correct, since Skia does not yet support drawing 479 // to/from unpremultiplied bitmaps. 480 outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, 481 scaledColorType, decodingBitmap.alphaType())); 482 if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) { 483 // This should only fail on OOM. The recyclingAllocator should have 484 // enough memory since we check this before decoding using the 485 // scaleCheckingAllocator. 486 return nullObjectReturn("allocation failed for scaled bitmap"); 487 } 488 489 SkPaint paint; 490 // kSrc_Mode instructs us to overwrite the unininitialized pixels in 491 // outputBitmap. Otherwise we would blend by default, which is not 492 // what we want. 493 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 494 paint.setFilterQuality(kLow_SkFilterQuality); 495 496 SkCanvas canvas(outputBitmap); 497 canvas.scale(sx, sy); 498 canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); 499 } else { 500 outputBitmap.swap(decodingBitmap); 501 } 502 503 if (padding) { 504 if (peeker.mPatch != NULL) { 505 GraphicsJNI::set_jrect(env, padding, 506 peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop, 507 peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom); 508 } else { 509 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 510 } 511 } 512 513 // If we get here, the outputBitmap should have an installed pixelref. 514 if (outputBitmap.pixelRef() == NULL) { 515 return nullObjectReturn("Got null SkPixelRef"); 516 } 517 518 if (!isMutable && javaBitmap == NULL) { 519 // promise we will never change our pixels (great for sharing and pictures) 520 outputBitmap.setImmutable(); 521 } 522 523 bool isPremultiplied = !requireUnpremultiplied; 524 if (javaBitmap != nullptr) { 525 GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied); 526 outputBitmap.notifyPixelsChanged(); 527 // If a java bitmap was passed in for reuse, pass it back 528 return javaBitmap; 529 } 530 531 int bitmapCreateFlags = 0x0; 532 if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable; 533 if (isPremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; 534 535 // now create the java bitmap 536 return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(), 537 bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); 538} 539 540static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, 541 jobject padding, jobject options) { 542 543 jobject bitmap = NULL; 544 std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage)); 545 546 if (stream.get()) { 547 std::unique_ptr<SkStreamRewindable> bufferedStream( 548 SkFrontBufferedStream::Create(stream.release(), SkCodec::MinBufferedBytesNeeded())); 549 SkASSERT(bufferedStream.get() != NULL); 550 bitmap = doDecode(env, bufferedStream.release(), padding, options); 551 } 552 return bitmap; 553} 554 555static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, 556 jobject padding, jobject bitmapFactoryOptions) { 557 558 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 559 560 int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 561 562 struct stat fdStat; 563 if (fstat(descriptor, &fdStat) == -1) { 564 doThrowIOE(env, "broken file descriptor"); 565 return nullObjectReturn("fstat return -1"); 566 } 567 568 // Restore the descriptor's offset on exiting this function. Even though 569 // we dup the descriptor, both the original and dup refer to the same open 570 // file description and changes to the file offset in one impact the other. 571 AutoFDSeek autoRestore(descriptor); 572 573 // Duplicate the descriptor here to prevent leaking memory. A leak occurs 574 // if we only close the file descriptor and not the file object it is used to 575 // create. If we don't explicitly clean up the file (which in turn closes the 576 // descriptor) the buffers allocated internally by fseek will be leaked. 577 int dupDescriptor = dup(descriptor); 578 579 FILE* file = fdopen(dupDescriptor, "r"); 580 if (file == NULL) { 581 // cleanup the duplicated descriptor since it will not be closed when the 582 // file is cleaned up (fclose). 583 close(dupDescriptor); 584 return nullObjectReturn("Could not open file"); 585 } 586 587 std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file, 588 SkFILEStream::kCallerPasses_Ownership)); 589 590 // If there is no offset for the file descriptor, we use SkFILEStream directly. 591 if (::lseek(descriptor, 0, SEEK_CUR) == 0) { 592 assert(isSeekable(dupDescriptor)); 593 return doDecode(env, fileStream.release(), padding, bitmapFactoryOptions); 594 } 595 596 // Use a buffered stream. Although an SkFILEStream can be rewound, this 597 // ensures that SkImageDecoder::Factory never rewinds beyond the 598 // current position of the file descriptor. 599 std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream.release(), 600 SkCodec::MinBufferedBytesNeeded())); 601 602 return doDecode(env, stream.release(), padding, bitmapFactoryOptions); 603} 604 605static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, 606 jobject padding, jobject options) { 607 608 Asset* asset = reinterpret_cast<Asset*>(native_asset); 609 // since we know we'll be done with the asset when we return, we can 610 // just use a simple wrapper 611 std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset)); 612 return doDecode(env, stream.release(), padding, options); 613} 614 615static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 616 jint offset, jint length, jobject options) { 617 618 AutoJavaByteArray ar(env, byteArray); 619 std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, false)); 620 return doDecode(env, stream.release(), NULL, options); 621} 622 623static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { 624 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 625 return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE; 626} 627 628jobject decodeBitmap(JNIEnv* env, void* data, size_t size) { 629 SkMemoryStream stream(data, size); 630 return doDecode(env, &stream, NULL, NULL); 631} 632 633/////////////////////////////////////////////////////////////////////////////// 634 635static const JNINativeMethod gMethods[] = { 636 { "nativeDecodeStream", 637 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 638 (void*)nativeDecodeStream 639 }, 640 641 { "nativeDecodeFileDescriptor", 642 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 643 (void*)nativeDecodeFileDescriptor 644 }, 645 646 { "nativeDecodeAsset", 647 "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 648 (void*)nativeDecodeAsset 649 }, 650 651 { "nativeDecodeByteArray", 652 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 653 (void*)nativeDecodeByteArray 654 }, 655 656 { "nativeIsSeekable", 657 "(Ljava/io/FileDescriptor;)Z", 658 (void*)nativeIsSeekable 659 }, 660}; 661 662int register_android_graphics_BitmapFactory(JNIEnv* env) { 663 jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options"); 664 gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap", 665 "Landroid/graphics/Bitmap;"); 666 gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z"); 667 gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I"); 668 gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig", 669 "Landroid/graphics/Bitmap$Config;"); 670 gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z"); 671 gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z"); 672 gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z"); 673 gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class, 674 "inPreferQualityOverSpeed", "Z"); 675 gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z"); 676 gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I"); 677 gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I"); 678 gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I"); 679 gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I"); 680 gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I"); 681 gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;"); 682 gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z"); 683 684 jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap"); 685 gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", 686 "Landroid/graphics/NinePatch$InsetStruct;"); 687 688 gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, 689 "android/graphics/NinePatch$InsetStruct")); 690 gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>", 691 "(IIIIIIIIFIF)V"); 692 693 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory", 694 gMethods, NELEM(gMethods)); 695} 696