BitmapFactory.cpp revision 77b5cad3efedd20f2b7cc14d87ccce1b0261960a
1#define LOG_TAG "BitmapFactory" 2 3#include "BitmapFactory.h" 4#include "NinePatchPeeker.h" 5#include "SkFrontBufferedStream.h" 6#include "SkImageDecoder.h" 7#include "SkMath.h" 8#include "SkPixelRef.h" 9#include "SkStream.h" 10#include "SkTemplates.h" 11#include "SkUtils.h" 12#include "CreateJavaOutputStreamAdaptor.h" 13#include "AutoDecodeCancel.h" 14#include "Utils.h" 15#include "JNIHelp.h" 16#include "GraphicsJNI.h" 17 18#include <android_runtime/AndroidRuntime.h> 19#include <androidfw/Asset.h> 20#include <androidfw/ResourceTypes.h> 21#include <netinet/in.h> 22#include <stdio.h> 23#include <sys/mman.h> 24#include <sys/stat.h> 25 26jfieldID gOptions_justBoundsFieldID; 27jfieldID gOptions_sampleSizeFieldID; 28jfieldID gOptions_configFieldID; 29jfieldID gOptions_premultipliedFieldID; 30jfieldID gOptions_mutableFieldID; 31jfieldID gOptions_ditherFieldID; 32jfieldID gOptions_preferQualityOverSpeedFieldID; 33jfieldID gOptions_scaledFieldID; 34jfieldID gOptions_densityFieldID; 35jfieldID gOptions_screenDensityFieldID; 36jfieldID gOptions_targetDensityFieldID; 37jfieldID gOptions_widthFieldID; 38jfieldID gOptions_heightFieldID; 39jfieldID gOptions_mimeFieldID; 40jfieldID gOptions_mCancelID; 41jfieldID gOptions_bitmapFieldID; 42 43jfieldID gBitmap_nativeBitmapFieldID; 44jfieldID gBitmap_ninePatchInsetsFieldID; 45 46jclass gInsetStruct_class; 47jmethodID gInsetStruct_constructorMethodID; 48 49using namespace android; 50 51static inline int32_t validOrNeg1(bool isValid, int32_t value) { 52// return isValid ? value : -1; 53 SkASSERT((int)isValid == 0 || (int)isValid == 1); 54 return ((int32_t)isValid - 1) | value; 55} 56 57jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { 58 static const struct { 59 SkImageDecoder::Format fFormat; 60 const char* fMimeType; 61 } gMimeTypes[] = { 62 { SkImageDecoder::kBMP_Format, "image/bmp" }, 63 { SkImageDecoder::kGIF_Format, "image/gif" }, 64 { SkImageDecoder::kICO_Format, "image/x-ico" }, 65 { SkImageDecoder::kJPEG_Format, "image/jpeg" }, 66 { SkImageDecoder::kPNG_Format, "image/png" }, 67 { SkImageDecoder::kWEBP_Format, "image/webp" }, 68 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" } 69 }; 70 71 const char* cstr = NULL; 72 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) { 73 if (gMimeTypes[i].fFormat == format) { 74 cstr = gMimeTypes[i].fMimeType; 75 break; 76 } 77 } 78 79 jstring jstr = 0; 80 if (NULL != cstr) { 81 jstr = env->NewStringUTF(cstr); 82 } 83 return jstr; 84} 85 86static bool optionsJustBounds(JNIEnv* env, jobject options) { 87 return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID); 88} 89 90static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { 91 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 92 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 93 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 94 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 95 96 int32_t* xDivs = chunk->getXDivs(); 97 for (int i = 0; i < chunk->numXDivs; i++) { 98 xDivs[i] = int32_t(xDivs[i] * scale + 0.5f); 99 if (i > 0 && xDivs[i] == xDivs[i - 1]) { 100 xDivs[i]++; 101 } 102 } 103 104 int32_t* yDivs = chunk->getYDivs(); 105 for (int i = 0; i < chunk->numYDivs; i++) { 106 yDivs[i] = int32_t(yDivs[i] * scale + 0.5f); 107 if (i > 0 && yDivs[i] == yDivs[i - 1]) { 108 yDivs[i]++; 109 } 110 } 111} 112 113static SkColorType colorTypeForScaledOutput(SkColorType colorType) { 114 switch (colorType) { 115 case kUnknown_SkColorType: 116 case kIndex_8_SkColorType: 117 return kN32_SkColorType; 118 default: 119 break; 120 } 121 return colorType; 122} 123 124class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { 125public: 126 ScaleCheckingAllocator(float scale, int size) 127 : mScale(scale), mSize(size) { 128 } 129 130 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 131 // accounts for scale in final allocation, using eventual size and config 132 const int bytesPerPixel = SkColorTypeBytesPerPixel( 133 colorTypeForScaledOutput(bitmap->colorType())); 134 const int requestedSize = bytesPerPixel * 135 int(bitmap->width() * mScale + 0.5f) * 136 int(bitmap->height() * mScale + 0.5f); 137 if (requestedSize > mSize) { 138 ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)", 139 mSize, requestedSize); 140 return false; 141 } 142 return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable); 143 } 144private: 145 const float mScale; 146 const int mSize; 147}; 148 149class RecyclingPixelAllocator : public SkBitmap::Allocator { 150public: 151 RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size) 152 : mPixelRef(pixelRef), mSize(size) { 153 SkSafeRef(mPixelRef); 154 } 155 156 ~RecyclingPixelAllocator() { 157 SkSafeUnref(mPixelRef); 158 } 159 160 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 161 const SkImageInfo& info = bitmap->info(); 162 if (info.fColorType == kUnknown_SkColorType) { 163 ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration"); 164 return false; 165 } 166 167 const int64_t size64 = info.getSafeSize64(bitmap->rowBytes()); 168 if (!sk_64_isS32(size64)) { 169 ALOGW("bitmap is too large"); 170 return false; 171 } 172 173 const size_t size = sk_64_asS32(size64); 174 if (size > mSize) { 175 ALOGW("bitmap marked for reuse (%d bytes) can't fit new bitmap (%d bytes)", 176 mSize, size); 177 return false; 178 } 179 180 // Create a new pixelref with the new ctable that wraps the previous pixelref 181 SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef), 182 info, bitmap->rowBytes(), ctable); 183 184 bitmap->setPixelRef(pr)->unref(); 185 // since we're already allocated, we lockPixels right away 186 // HeapAllocator/JavaPixelAllocator behaves this way too 187 bitmap->lockPixels(); 188 return true; 189 } 190 191private: 192 SkPixelRef* const mPixelRef; 193 const unsigned int mSize; 194}; 195 196static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { 197 198 int sampleSize = 1; 199 200 SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode; 201 SkColorType prefColorType = kN32_SkColorType; 202 203 bool doDither = true; 204 bool isMutable = false; 205 float scale = 1.0f; 206 bool preferQualityOverSpeed = false; 207 bool requireUnpremultiplied = false; 208 209 jobject javaBitmap = NULL; 210 211 if (options != NULL) { 212 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 213 if (optionsJustBounds(env, options)) { 214 decodeMode = SkImageDecoder::kDecodeBounds_Mode; 215 } 216 217 // initialize these, in case we fail later on 218 env->SetIntField(options, gOptions_widthFieldID, -1); 219 env->SetIntField(options, gOptions_heightFieldID, -1); 220 env->SetObjectField(options, gOptions_mimeFieldID, 0); 221 222 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 223 prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); 224 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); 225 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 226 preferQualityOverSpeed = env->GetBooleanField(options, 227 gOptions_preferQualityOverSpeedFieldID); 228 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); 229 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 230 231 if (env->GetBooleanField(options, gOptions_scaledFieldID)) { 232 const int density = env->GetIntField(options, gOptions_densityFieldID); 233 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID); 234 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); 235 if (density != 0 && targetDensity != 0 && density != screenDensity) { 236 scale = (float) targetDensity / density; 237 } 238 } 239 } 240 241 const bool willScale = scale != 1.0f; 242 243 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 244 if (decoder == NULL) { 245 return nullObjectReturn("SkImageDecoder::Factory returned null"); 246 } 247 248 decoder->setSampleSize(sampleSize); 249 decoder->setDitherImage(doDither); 250 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 251 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); 252 253 SkBitmap* outputBitmap = NULL; 254 unsigned int existingBufferSize = 0; 255 if (javaBitmap != NULL) { 256 outputBitmap = (SkBitmap*) env->GetLongField(javaBitmap, gBitmap_nativeBitmapFieldID); 257 if (outputBitmap->isImmutable()) { 258 ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); 259 javaBitmap = NULL; 260 outputBitmap = NULL; 261 } else { 262 existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); 263 } 264 } 265 266 SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL); 267 if (outputBitmap == NULL) outputBitmap = adb.get(); 268 269 NinePatchPeeker peeker(decoder); 270 decoder->setPeeker(&peeker); 271 272 JavaPixelAllocator javaAllocator(env); 273 RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize); 274 ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); 275 SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ? 276 (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator; 277 if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) { 278 if (!willScale) { 279 // If the java allocator is being used to allocate the pixel memory, the decoder 280 // need not write zeroes, since the memory is initialized to 0. 281 decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator); 282 decoder->setAllocator(outputAllocator); 283 } else if (javaBitmap != NULL) { 284 // check for eventual scaled bounds at allocation time, so we don't decode the bitmap 285 // only to find the scaled result too large to fit in the allocation 286 decoder->setAllocator(&scaleCheckingAllocator); 287 } 288 } 289 290 // Only setup the decoder to be deleted after its stack-based, refcounted 291 // components (allocators, peekers, etc) are declared. This prevents RefCnt 292 // asserts from firing due to the order objects are deleted from the stack. 293 SkAutoTDelete<SkImageDecoder> add(decoder); 294 295 AutoDecoderCancel adc(options, decoder); 296 297 // To fix the race condition in case "requestCancelDecode" 298 // happens earlier than AutoDecoderCancel object is added 299 // to the gAutoDecoderCancelMutex linked list. 300 if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) { 301 return nullObjectReturn("gOptions_mCancelID"); 302 } 303 304 SkBitmap decodingBitmap; 305 if (!decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)) { 306 return nullObjectReturn("decoder->decode returned false"); 307 } 308 309 int scaledWidth = decodingBitmap.width(); 310 int scaledHeight = decodingBitmap.height(); 311 312 if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) { 313 scaledWidth = int(scaledWidth * scale + 0.5f); 314 scaledHeight = int(scaledHeight * scale + 0.5f); 315 } 316 317 // update options (if any) 318 if (options != NULL) { 319 env->SetIntField(options, gOptions_widthFieldID, scaledWidth); 320 env->SetIntField(options, gOptions_heightFieldID, scaledHeight); 321 env->SetObjectField(options, gOptions_mimeFieldID, 322 getMimeTypeString(env, decoder->getFormat())); 323 } 324 325 // if we're in justBounds mode, return now (skip the java bitmap) 326 if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) { 327 return NULL; 328 } 329 330 jbyteArray ninePatchChunk = NULL; 331 if (peeker.mPatch != NULL) { 332 if (willScale) { 333 scaleNinePatchChunk(peeker.mPatch, scale); 334 } 335 336 size_t ninePatchArraySize = peeker.mPatch->serializedSize(); 337 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 338 if (ninePatchChunk == NULL) { 339 return nullObjectReturn("ninePatchChunk == null"); 340 } 341 342 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); 343 if (array == NULL) { 344 return nullObjectReturn("primitive array == null"); 345 } 346 347 memcpy(array, peeker.mPatch, peeker.mPatchSize); 348 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 349 } 350 351 jobject ninePatchInsets = NULL; 352 if (peeker.mHasInsets) { 353 ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, 354 peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], 355 peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], 356 peeker.mOutlineRadius, peeker.mOutlineAlpha, scale); 357 if (javaBitmap != NULL) { 358 env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets); 359 } 360 } 361 362 if (willScale) { 363 // This is weird so let me explain: we could use the scale parameter 364 // directly, but for historical reasons this is how the corresponding 365 // Dalvik code has always behaved. We simply recreate the behavior here. 366 // The result is slightly different from simply using scale because of 367 // the 0.5f rounding bias applied when computing the target image size 368 const float sx = scaledWidth / float(decodingBitmap.width()); 369 const float sy = scaledHeight / float(decodingBitmap.height()); 370 371 // TODO: avoid copying when scaled size equals decodingBitmap size 372 SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType()); 373 // FIXME: If the alphaType is kUnpremul and the image has alpha, the 374 // colors may not be correct, since Skia does not yet support drawing 375 // to/from unpremultiplied bitmaps. 376 outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, 377 colorType, decodingBitmap.alphaType())); 378 if (!outputBitmap->allocPixels(outputAllocator, NULL)) { 379 return nullObjectReturn("allocation failed for scaled bitmap"); 380 } 381 382 // If outputBitmap's pixels are newly allocated by Java, there is no need 383 // to erase to 0, since the pixels were initialized to 0. 384 if (outputAllocator != &javaAllocator) { 385 outputBitmap->eraseColor(0); 386 } 387 388 SkPaint paint; 389 paint.setFilterLevel(SkPaint::kLow_FilterLevel); 390 391 SkCanvas canvas(*outputBitmap); 392 canvas.scale(sx, sy); 393 canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); 394 } else { 395 outputBitmap->swap(decodingBitmap); 396 } 397 398 if (padding) { 399 if (peeker.mPatch != NULL) { 400 GraphicsJNI::set_jrect(env, padding, 401 peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop, 402 peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom); 403 } else { 404 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 405 } 406 } 407 408 // if we get here, we're in kDecodePixels_Mode and will therefore 409 // already have a pixelref installed. 410 if (outputBitmap->pixelRef() == NULL) { 411 return nullObjectReturn("Got null SkPixelRef"); 412 } 413 414 if (!isMutable && javaBitmap == NULL) { 415 // promise we will never change our pixels (great for sharing and pictures) 416 outputBitmap->setImmutable(); 417 } 418 419 // detach bitmap from its autodeleter, since we want to own it now 420 adb.detach(); 421 422 if (javaBitmap != NULL) { 423 bool isPremultiplied = !requireUnpremultiplied; 424 GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied); 425 outputBitmap->notifyPixelsChanged(); 426 // If a java bitmap was passed in for reuse, pass it back 427 return javaBitmap; 428 } 429 430 int bitmapCreateFlags = 0x0; 431 if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable; 432 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; 433 434 // now create the java bitmap 435 return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(), 436 bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); 437} 438 439// Need to buffer enough input to be able to rewind as much as might be read by a decoder 440// trying to determine the stream's format. Currently the most is 64, read by 441// SkImageDecoder_libwebp. 442// FIXME: Get this number from SkImageDecoder 443#define BYTES_TO_BUFFER 64 444 445static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, 446 jobject padding, jobject options) { 447 448 jobject bitmap = NULL; 449 SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage)); 450 451 if (stream.get()) { 452 SkAutoTUnref<SkStreamRewindable> bufferedStream( 453 SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER)); 454 SkASSERT(bufferedStream.get() != NULL); 455 bitmap = doDecode(env, bufferedStream, padding, options); 456 } 457 return bitmap; 458} 459 460static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, 461 jobject padding, jobject bitmapFactoryOptions) { 462 463 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 464 465 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 466 467 struct stat fdStat; 468 if (fstat(descriptor, &fdStat) == -1) { 469 doThrowIOE(env, "broken file descriptor"); 470 return nullObjectReturn("fstat return -1"); 471 } 472 473 // Restore the descriptor's offset on exiting this function. 474 AutoFDSeek autoRestore(descriptor); 475 476 FILE* file = fdopen(descriptor, "r"); 477 if (file == NULL) { 478 return nullObjectReturn("Could not open file"); 479 } 480 481 SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file, 482 SkFILEStream::kCallerRetains_Ownership)); 483 484 // Use a buffered stream. Although an SkFILEStream can be rewound, this 485 // ensures that SkImageDecoder::Factory never rewinds beyond the 486 // current position of the file descriptor. 487 SkAutoTUnref<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream, 488 BYTES_TO_BUFFER)); 489 490 return doDecode(env, stream, padding, bitmapFactoryOptions); 491} 492 493static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, 494 jobject padding, jobject options) { 495 496 Asset* asset = reinterpret_cast<Asset*>(native_asset); 497 // since we know we'll be done with the asset when we return, we can 498 // just use a simple wrapper 499 SkAutoTUnref<SkStreamRewindable> stream(new AssetStreamAdaptor(asset, 500 AssetStreamAdaptor::kNo_OwnAsset, AssetStreamAdaptor::kNo_HasMemoryBase)); 501 return doDecode(env, stream, padding, options); 502} 503 504static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 505 jint offset, jint length, jobject options) { 506 507 AutoJavaByteArray ar(env, byteArray); 508 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false); 509 SkAutoUnref aur(stream); 510 return doDecode(env, stream, NULL, options); 511} 512 513static void nativeRequestCancel(JNIEnv*, jobject joptions) { 514 (void)AutoDecoderCancel::RequestCancel(joptions); 515} 516 517static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { 518 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 519 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE; 520} 521 522/////////////////////////////////////////////////////////////////////////////// 523 524static JNINativeMethod gMethods[] = { 525 { "nativeDecodeStream", 526 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 527 (void*)nativeDecodeStream 528 }, 529 530 { "nativeDecodeFileDescriptor", 531 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 532 (void*)nativeDecodeFileDescriptor 533 }, 534 535 { "nativeDecodeAsset", 536 "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 537 (void*)nativeDecodeAsset 538 }, 539 540 { "nativeDecodeByteArray", 541 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 542 (void*)nativeDecodeByteArray 543 }, 544 545 { "nativeIsSeekable", 546 "(Ljava/io/FileDescriptor;)Z", 547 (void*)nativeIsSeekable 548 }, 549}; 550 551static JNINativeMethod gOptionsMethods[] = { 552 { "requestCancel", "()V", (void*)nativeRequestCancel } 553}; 554 555static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 556 const char fieldname[], const char type[]) { 557 jfieldID id = env->GetFieldID(clazz, fieldname, type); 558 SkASSERT(id); 559 return id; 560} 561 562int register_android_graphics_BitmapFactory(JNIEnv* env) { 563 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options"); 564 SkASSERT(options_class); 565 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap", 566 "Landroid/graphics/Bitmap;"); 567 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z"); 568 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I"); 569 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig", 570 "Landroid/graphics/Bitmap$Config;"); 571 gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z"); 572 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z"); 573 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z"); 574 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class, 575 "inPreferQualityOverSpeed", "Z"); 576 gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z"); 577 gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I"); 578 gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I"); 579 gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I"); 580 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I"); 581 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I"); 582 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;"); 583 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z"); 584 585 jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); 586 SkASSERT(bitmap_class); 587 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "J"); 588 gBitmap_ninePatchInsetsFieldID = getFieldIDCheck(env, bitmap_class, "mNinePatchInsets", 589 "Landroid/graphics/NinePatch$InsetStruct;"); 590 591 gInsetStruct_class = (jclass) env->NewGlobalRef(env->FindClass("android/graphics/NinePatch$InsetStruct")); 592 gInsetStruct_constructorMethodID = env->GetMethodID(gInsetStruct_class, "<init>", "(IIIIIIIIFIF)V"); 593 594 int ret = AndroidRuntime::registerNativeMethods(env, 595 "android/graphics/BitmapFactory$Options", 596 gOptionsMethods, 597 SK_ARRAY_COUNT(gOptionsMethods)); 598 if (ret) { 599 return ret; 600 } 601 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory", 602 gMethods, SK_ARRAY_COUNT(gMethods)); 603} 604