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