BitmapFactory.cpp revision 96ffbdc9ddd8e8fd6582a907b1c5e916d21e44fa
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 204static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { 205 // This function takes ownership of the input stream. Since the SkAndroidCodec 206 // will take ownership of the stream, we don't necessarily need to take ownership 207 // here. This is a precaution - if we were to return before creating the codec, 208 // we need to make sure that we delete the stream. 209 std::unique_ptr<SkStreamRewindable> streamDeleter(stream); 210 211 // Set default values for the options parameters. 212 int sampleSize = 1; 213 bool onlyDecodeSize = false; 214 SkColorType prefColorType = kN32_SkColorType; 215 bool isMutable = false; 216 float scale = 1.0f; 217 bool requireUnpremultiplied = false; 218 jobject javaBitmap = NULL; 219 220 // Update with options supplied by the client. 221 if (options != NULL) { 222 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 223 // Correct a non-positive sampleSize. sampleSize defaults to zero within the 224 // options object, which is strange. 225 if (sampleSize <= 0) { 226 sampleSize = 1; 227 } 228 229 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { 230 onlyDecodeSize = true; 231 } 232 233 // initialize these, in case we fail later on 234 env->SetIntField(options, gOptions_widthFieldID, -1); 235 env->SetIntField(options, gOptions_heightFieldID, -1); 236 env->SetObjectField(options, gOptions_mimeFieldID, 0); 237 238 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 239 prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); 240 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); 241 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); 242 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 243 244 if (env->GetBooleanField(options, gOptions_scaledFieldID)) { 245 const int density = env->GetIntField(options, gOptions_densityFieldID); 246 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID); 247 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); 248 if (density != 0 && targetDensity != 0 && density != screenDensity) { 249 scale = (float) targetDensity / density; 250 } 251 } 252 } 253 const bool willScale = scale != 1.0f; 254 255 // Create the codec. 256 NinePatchPeeker peeker; 257 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(), 258 &peeker)); 259 if (!codec.get()) { 260 return nullObjectReturn("SkAndroidCodec::NewFromStream returned null"); 261 } 262 263 // Do not allow ninepatch decodes to 565. In the past, decodes to 565 264 // would dither, and we do not want to pre-dither ninepatches, since we 265 // know that they will be stretched. We no longer dither 565 decodes, 266 // but we continue to prevent ninepatches from decoding to 565, in order 267 // to maintain the old behavior. 268 if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) { 269 prefColorType = kN32_SkColorType; 270 } 271 272 // Determine the output size and return if the client only wants the size. 273 SkISize size = codec->getSampledDimensions(sampleSize); 274 if (options != NULL) { 275 jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat()); 276 if (env->ExceptionCheck()) { 277 return nullObjectReturn("OOM in encodedFormatToString()"); 278 } 279 env->SetIntField(options, gOptions_widthFieldID, size.width()); 280 env->SetIntField(options, gOptions_heightFieldID, size.height()); 281 env->SetObjectField(options, gOptions_mimeFieldID, mimeType); 282 283 if (onlyDecodeSize) { 284 return nullptr; 285 } 286 } 287 288 android::Bitmap* reuseBitmap = nullptr; 289 unsigned int existingBufferSize = 0; 290 if (javaBitmap != NULL) { 291 reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap); 292 if (reuseBitmap->peekAtPixelRef()->isImmutable()) { 293 ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); 294 javaBitmap = NULL; 295 reuseBitmap = nullptr; 296 } else { 297 existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); 298 } 299 } 300 301 JavaPixelAllocator javaAllocator(env); 302 RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize); 303 ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); 304 SkBitmap::HeapAllocator heapAllocator; 305 SkBitmap::Allocator* decodeAllocator; 306 if (javaBitmap != nullptr && willScale) { 307 // This will allocate pixels using a HeapAllocator, since there will be an extra 308 // scaling step that copies these pixels into Java memory. This allocator 309 // also checks that the recycled javaBitmap is large enough. 310 decodeAllocator = &scaleCheckingAllocator; 311 } else if (javaBitmap != nullptr) { 312 decodeAllocator = &recyclingAllocator; 313 } else if (willScale) { 314 // This will allocate pixels using a HeapAllocator, since there will be an extra 315 // scaling step that copies these pixels into Java memory. 316 decodeAllocator = &heapAllocator; 317 } else { 318 decodeAllocator = &javaAllocator; 319 } 320 321 // Set the decode colorType. This is necessary because we can't always support 322 // the requested colorType. 323 SkColorType decodeColorType = codec->computeOutputColorType(prefColorType); 324 325 // Construct a color table for the decode if necessary 326 SkAutoTUnref<SkColorTable> colorTable(nullptr); 327 SkPMColor* colorPtr = nullptr; 328 int* colorCount = nullptr; 329 int maxColors = 256; 330 SkPMColor colors[256]; 331 if (kIndex_8_SkColorType == decodeColorType) { 332 colorTable.reset(new SkColorTable(colors, maxColors)); 333 334 // SkColorTable expects us to initialize all of the colors before creating an 335 // SkColorTable. However, we are using SkBitmap with an Allocator to allocate 336 // memory for the decode, so we need to create the SkColorTable before decoding. 337 // It is safe for SkAndroidCodec to modify the colors because this SkBitmap is 338 // not being used elsewhere. 339 colorPtr = const_cast<SkPMColor*>(colorTable->readColors()); 340 colorCount = &maxColors; 341 } 342 343 // Set the alpha type for the decode. 344 SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); 345 346 const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType, 347 alphaType); 348 SkImageInfo bitmapInfo = decodeInfo; 349 if (decodeColorType == kGray_8_SkColorType) { 350 // The legacy implementation of BitmapFactory used kAlpha8 for 351 // grayscale images (before kGray8 existed). While the codec 352 // recognizes kGray8, we need to decode into a kAlpha8 bitmap 353 // in order to avoid a behavior change. 354 bitmapInfo = SkImageInfo::MakeA8(size.width(), size.height()); 355 } 356 SkBitmap decodingBitmap; 357 if (!decodingBitmap.setInfo(bitmapInfo) || 358 !decodingBitmap.tryAllocPixels(decodeAllocator, colorTable)) { 359 // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo() 360 // should only only fail if the calculated value for rowBytes is too 361 // large. 362 // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the 363 // native heap, or the recycled javaBitmap being too small to reuse. 364 return nullptr; 365 } 366 367 // Use SkAndroidCodec to perform the decode. 368 SkAndroidCodec::AndroidOptions codecOptions; 369 codecOptions.fZeroInitialized = (decodeAllocator == &javaAllocator) ? 370 SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized; 371 codecOptions.fColorPtr = colorPtr; 372 codecOptions.fColorCount = colorCount; 373 codecOptions.fSampleSize = sampleSize; 374 SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(), 375 decodingBitmap.rowBytes(), &codecOptions); 376 switch (result) { 377 case SkCodec::kSuccess: 378 case SkCodec::kIncompleteInput: 379 break; 380 default: 381 return nullObjectReturn("codec->getAndroidPixels() failed."); 382 } 383 384 int scaledWidth = size.width(); 385 int scaledHeight = size.height(); 386 if (willScale) { 387 scaledWidth = int(scaledWidth * scale + 0.5f); 388 scaledHeight = int(scaledHeight * scale + 0.5f); 389 } 390 391 jbyteArray ninePatchChunk = NULL; 392 if (peeker.mPatch != NULL) { 393 if (willScale) { 394 scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight); 395 } 396 397 size_t ninePatchArraySize = peeker.mPatch->serializedSize(); 398 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 399 if (ninePatchChunk == NULL) { 400 return nullObjectReturn("ninePatchChunk == null"); 401 } 402 403 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); 404 if (array == NULL) { 405 return nullObjectReturn("primitive array == null"); 406 } 407 408 memcpy(array, peeker.mPatch, peeker.mPatchSize); 409 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 410 } 411 412 jobject ninePatchInsets = NULL; 413 if (peeker.mHasInsets) { 414 ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, 415 peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], 416 peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], 417 peeker.mOutlineRadius, peeker.mOutlineAlpha, scale); 418 if (ninePatchInsets == NULL) { 419 return nullObjectReturn("nine patch insets == null"); 420 } 421 if (javaBitmap != NULL) { 422 env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets); 423 } 424 } 425 426 SkBitmap outputBitmap; 427 if (willScale) { 428 // This is weird so let me explain: we could use the scale parameter 429 // directly, but for historical reasons this is how the corresponding 430 // Dalvik code has always behaved. We simply recreate the behavior here. 431 // The result is slightly different from simply using scale because of 432 // the 0.5f rounding bias applied when computing the target image size 433 const float sx = scaledWidth / float(decodingBitmap.width()); 434 const float sy = scaledHeight / float(decodingBitmap.height()); 435 436 // Set the allocator for the outputBitmap. 437 SkBitmap::Allocator* outputAllocator; 438 if (javaBitmap != nullptr) { 439 outputAllocator = &recyclingAllocator; 440 } else { 441 outputAllocator = &javaAllocator; 442 } 443 444 SkColorType scaledColorType = colorTypeForScaledOutput(decodingBitmap.colorType()); 445 // FIXME: If the alphaType is kUnpremul and the image has alpha, the 446 // colors may not be correct, since Skia does not yet support drawing 447 // to/from unpremultiplied bitmaps. 448 outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, 449 scaledColorType, decodingBitmap.alphaType())); 450 if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) { 451 // This should only fail on OOM. The recyclingAllocator should have 452 // enough memory since we check this before decoding using the 453 // scaleCheckingAllocator. 454 return nullObjectReturn("allocation failed for scaled bitmap"); 455 } 456 457 SkPaint paint; 458 // kSrc_Mode instructs us to overwrite the unininitialized pixels in 459 // outputBitmap. Otherwise we would blend by default, which is not 460 // what we want. 461 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 462 paint.setFilterQuality(kLow_SkFilterQuality); 463 464 SkCanvas canvas(outputBitmap); 465 canvas.scale(sx, sy); 466 canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); 467 } else { 468 outputBitmap.swap(decodingBitmap); 469 } 470 471 if (padding) { 472 if (peeker.mPatch != NULL) { 473 GraphicsJNI::set_jrect(env, padding, 474 peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop, 475 peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom); 476 } else { 477 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 478 } 479 } 480 481 // If we get here, the outputBitmap should have an installed pixelref. 482 if (outputBitmap.pixelRef() == NULL) { 483 return nullObjectReturn("Got null SkPixelRef"); 484 } 485 486 if (!isMutable && javaBitmap == NULL) { 487 // promise we will never change our pixels (great for sharing and pictures) 488 outputBitmap.setImmutable(); 489 } 490 491 bool isPremultiplied = !requireUnpremultiplied; 492 if (javaBitmap != nullptr) { 493 GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied); 494 outputBitmap.notifyPixelsChanged(); 495 // If a java bitmap was passed in for reuse, pass it back 496 return javaBitmap; 497 } 498 499 int bitmapCreateFlags = 0x0; 500 if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable; 501 if (isPremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; 502 503 // now create the java bitmap 504 return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(), 505 bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); 506} 507 508static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, 509 jobject padding, jobject options) { 510 511 jobject bitmap = NULL; 512 std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage)); 513 514 if (stream.get()) { 515 std::unique_ptr<SkStreamRewindable> bufferedStream( 516 SkFrontBufferedStream::Create(stream.release(), SkCodec::MinBufferedBytesNeeded())); 517 SkASSERT(bufferedStream.get() != NULL); 518 bitmap = doDecode(env, bufferedStream.release(), padding, options); 519 } 520 return bitmap; 521} 522 523static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, 524 jobject padding, jobject bitmapFactoryOptions) { 525 526 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 527 528 int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 529 530 struct stat fdStat; 531 if (fstat(descriptor, &fdStat) == -1) { 532 doThrowIOE(env, "broken file descriptor"); 533 return nullObjectReturn("fstat return -1"); 534 } 535 536 // Restore the descriptor's offset on exiting this function. Even though 537 // we dup the descriptor, both the original and dup refer to the same open 538 // file description and changes to the file offset in one impact the other. 539 AutoFDSeek autoRestore(descriptor); 540 541 // Duplicate the descriptor here to prevent leaking memory. A leak occurs 542 // if we only close the file descriptor and not the file object it is used to 543 // create. If we don't explicitly clean up the file (which in turn closes the 544 // descriptor) the buffers allocated internally by fseek will be leaked. 545 int dupDescriptor = dup(descriptor); 546 547 FILE* file = fdopen(dupDescriptor, "r"); 548 if (file == NULL) { 549 // cleanup the duplicated descriptor since it will not be closed when the 550 // file is cleaned up (fclose). 551 close(dupDescriptor); 552 return nullObjectReturn("Could not open file"); 553 } 554 555 std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file, 556 SkFILEStream::kCallerPasses_Ownership)); 557 558 // Use a buffered stream. Although an SkFILEStream can be rewound, this 559 // ensures that SkImageDecoder::Factory never rewinds beyond the 560 // current position of the file descriptor. 561 std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream.release(), 562 SkCodec::MinBufferedBytesNeeded())); 563 564 return doDecode(env, stream.release(), padding, bitmapFactoryOptions); 565} 566 567static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, 568 jobject padding, jobject options) { 569 570 Asset* asset = reinterpret_cast<Asset*>(native_asset); 571 // since we know we'll be done with the asset when we return, we can 572 // just use a simple wrapper 573 std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset)); 574 return doDecode(env, stream.release(), padding, options); 575} 576 577static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 578 jint offset, jint length, jobject options) { 579 580 AutoJavaByteArray ar(env, byteArray); 581 std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, false)); 582 return doDecode(env, stream.release(), NULL, options); 583} 584 585static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { 586 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 587 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE; 588} 589 590jobject decodeBitmap(JNIEnv* env, void* data, size_t size) { 591 SkMemoryStream stream(data, size); 592 return doDecode(env, &stream, NULL, NULL); 593} 594 595/////////////////////////////////////////////////////////////////////////////// 596 597static const JNINativeMethod gMethods[] = { 598 { "nativeDecodeStream", 599 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 600 (void*)nativeDecodeStream 601 }, 602 603 { "nativeDecodeFileDescriptor", 604 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 605 (void*)nativeDecodeFileDescriptor 606 }, 607 608 { "nativeDecodeAsset", 609 "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 610 (void*)nativeDecodeAsset 611 }, 612 613 { "nativeDecodeByteArray", 614 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 615 (void*)nativeDecodeByteArray 616 }, 617 618 { "nativeIsSeekable", 619 "(Ljava/io/FileDescriptor;)Z", 620 (void*)nativeIsSeekable 621 }, 622}; 623 624int register_android_graphics_BitmapFactory(JNIEnv* env) { 625 jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options"); 626 gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap", 627 "Landroid/graphics/Bitmap;"); 628 gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z"); 629 gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I"); 630 gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig", 631 "Landroid/graphics/Bitmap$Config;"); 632 gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z"); 633 gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z"); 634 gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z"); 635 gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class, 636 "inPreferQualityOverSpeed", "Z"); 637 gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z"); 638 gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I"); 639 gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I"); 640 gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I"); 641 gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I"); 642 gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I"); 643 gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;"); 644 gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z"); 645 646 jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap"); 647 gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", 648 "Landroid/graphics/NinePatch$InsetStruct;"); 649 650 gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, 651 "android/graphics/NinePatch$InsetStruct")); 652 gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>", 653 "(IIIIIIIIFIF)V"); 654 655 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory", 656 gMethods, NELEM(gMethods)); 657} 658