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