BitmapFactory.cpp revision a06d86ab8177ee9e631e0ee4e39688bf42179bde
1#define LOG_TAG "BitmapFactory" 2 3#include "BitmapFactory.h" 4#include "NinePatchPeeker.h" 5#include "SkImageDecoder.h" 6#include "SkImageRef_ashmem.h" 7#include "SkImageRef_GlobalPool.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 16#include <android_runtime/AndroidRuntime.h> 17#include <utils/Asset.h> 18#include <utils/ResourceTypes.h> 19#include <netinet/in.h> 20#include <sys/mman.h> 21#include <sys/stat.h> 22 23jclass gOptions_class; 24jfieldID gOptions_justBoundsFieldID; 25jfieldID gOptions_sampleSizeFieldID; 26jfieldID gOptions_configFieldID; 27jfieldID gOptions_mutableFieldID; 28jfieldID gOptions_ditherFieldID; 29jfieldID gOptions_purgeableFieldID; 30jfieldID gOptions_shareableFieldID; 31jfieldID gOptions_preferQualityOverSpeedFieldID; 32jfieldID gOptions_widthFieldID; 33jfieldID gOptions_heightFieldID; 34jfieldID gOptions_mimeFieldID; 35jfieldID gOptions_mCancelID; 36jfieldID gOptions_bitmapFieldID; 37jclass gBitmap_class; 38jfieldID gBitmap_nativeBitmapFieldID; 39 40static jclass gFileDescriptor_class; 41static jfieldID gFileDescriptor_descriptor; 42 43#if 0 44 #define TRACE_BITMAP(code) code 45#else 46 #define TRACE_BITMAP(code) 47#endif 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::kWBMP_Format, "image/vnd.wap.wbmp" } 68 }; 69 70 const char* cstr = NULL; 71 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) { 72 if (gMimeTypes[i].fFormat == format) { 73 cstr = gMimeTypes[i].fMimeType; 74 break; 75 } 76 } 77 78 jstring jstr = 0; 79 if (NULL != cstr) { 80 jstr = env->NewStringUTF(cstr); 81 } 82 return jstr; 83} 84 85static bool optionsPurgeable(JNIEnv* env, jobject options) { 86 return options != NULL && 87 env->GetBooleanField(options, gOptions_purgeableFieldID); 88} 89 90static bool optionsShareable(JNIEnv* env, jobject options) { 91 return options != NULL && 92 env->GetBooleanField(options, gOptions_shareableFieldID); 93} 94 95static bool optionsJustBounds(JNIEnv* env, jobject options) { 96 return options != NULL && 97 env->GetBooleanField(options, gOptions_justBoundsFieldID); 98} 99 100static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, 101 int sampleSize, bool ditherImage) { 102 SkImageRef* pr; 103 // only use ashmem for large images, since mmaps come at a price 104 if (bitmap->getSize() >= 32 * 1024) { 105 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); 106 } else { 107 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); 108 } 109 pr->setDitherImage(ditherImage); 110 bitmap->setPixelRef(pr)->unref(); 111 pr->isOpaque(bitmap); 112 return pr; 113} 114 115// since we "may" create a purgeable imageref, we require the stream be ref'able 116// i.e. dynamically allocated, since its lifetime may exceed the current stack 117// frame. 118static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, 119 jobject options, bool allowPurgeable, 120 bool forcePurgeable = false) { 121 int sampleSize = 1; 122 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; 123 SkBitmap::Config prefConfig = SkBitmap::kNo_Config; 124 bool doDither = true; 125 bool isMutable = false; 126 bool isPurgeable = forcePurgeable || 127 (allowPurgeable && optionsPurgeable(env, options)); 128 bool preferQualityOverSpeed = false; 129 jobject javaBitmap = NULL; 130 131 if (NULL != options) { 132 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 133 if (optionsJustBounds(env, options)) { 134 mode = SkImageDecoder::kDecodeBounds_Mode; 135 } 136 // initialize these, in case we fail later on 137 env->SetIntField(options, gOptions_widthFieldID, -1); 138 env->SetIntField(options, gOptions_heightFieldID, -1); 139 env->SetObjectField(options, gOptions_mimeFieldID, 0); 140 141 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 142 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 143 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); 144 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 145 preferQualityOverSpeed = env->GetBooleanField(options, 146 gOptions_preferQualityOverSpeedFieldID); 147 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 148 } 149 150 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 151 if (NULL == decoder) { 152 return nullObjectReturn("SkImageDecoder::Factory returned null"); 153 } 154 155 decoder->setSampleSize(sampleSize); 156 decoder->setDitherImage(doDither); 157 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 158 159 NinePatchPeeker peeker(decoder); 160 JavaPixelAllocator javaAllocator(env); 161 SkBitmap* bitmap; 162 if (javaBitmap == NULL) { 163 bitmap = new SkBitmap; 164 } else { 165 if (sampleSize != 1) { 166 return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1"); 167 } 168 bitmap = (SkBitmap *) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID); 169 // config of supplied bitmap overrules config set in options 170 prefConfig = bitmap->getConfig(); 171 } 172 Res_png_9patch dummy9Patch; 173 174 SkAutoTDelete<SkImageDecoder> add(decoder); 175 SkAutoTDelete<SkBitmap> adb(bitmap, (javaBitmap == NULL)); 176 177 decoder->setPeeker(&peeker); 178 if (!isPurgeable) { 179 decoder->setAllocator(&javaAllocator); 180 } 181 182 AutoDecoderCancel adc(options, decoder); 183 184 // To fix the race condition in case "requestCancelDecode" 185 // happens earlier than AutoDecoderCancel object is added 186 // to the gAutoDecoderCancelMutex linked list. 187 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 188 return nullObjectReturn("gOptions_mCancelID"); 189 } 190 191 SkImageDecoder::Mode decodeMode = mode; 192 if (isPurgeable) { 193 decodeMode = SkImageDecoder::kDecodeBounds_Mode; 194 } 195 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode, javaBitmap != NULL)) { 196 return nullObjectReturn("decoder->decode returned false"); 197 } 198 199 // update options (if any) 200 if (NULL != options) { 201 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 202 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 203 // TODO: set the mimeType field with the data from the codec. 204 // but how to reuse a set of strings, rather than allocating new one 205 // each time? 206 env->SetObjectField(options, gOptions_mimeFieldID, 207 getMimeTypeString(env, decoder->getFormat())); 208 } 209 210 // if we're in justBounds mode, return now (skip the java bitmap) 211 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 212 return NULL; 213 } 214 215 jbyteArray ninePatchChunk = NULL; 216 if (peeker.fPatchIsValid) { 217 size_t ninePatchArraySize = peeker.fPatch->serializedSize(); 218 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 219 if (NULL == ninePatchChunk) { 220 return nullObjectReturn("ninePatchChunk == null"); 221 } 222 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, 223 NULL); 224 if (NULL == array) { 225 return nullObjectReturn("primitive array == null"); 226 } 227 peeker.fPatch->serialize(array); 228 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 229 } 230 231 // detach bitmap from its autodeleter, since we want to own it now 232 adb.detach(); 233 234 if (padding) { 235 if (peeker.fPatchIsValid) { 236 GraphicsJNI::set_jrect(env, padding, 237 peeker.fPatch->paddingLeft, 238 peeker.fPatch->paddingTop, 239 peeker.fPatch->paddingRight, 240 peeker.fPatch->paddingBottom); 241 } else { 242 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 243 } 244 } 245 246 SkPixelRef* pr; 247 if (isPurgeable) { 248 pr = installPixelRef(bitmap, stream, sampleSize, doDither); 249 } else { 250 // if we get here, we're in kDecodePixels_Mode and will therefore 251 // already have a pixelref installed. 252 pr = bitmap->pixelRef(); 253 } 254 255 if (!isMutable) { 256 // promise we will never change our pixels (great for sharing and pictures) 257 pr->setImmutable(); 258 } 259 260 if (javaBitmap != NULL) { 261 // If a java bitmap was passed in for reuse, pass it back 262 return javaBitmap; 263 } 264 // now create the java bitmap 265 return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), 266 isMutable, ninePatchChunk); 267} 268 269static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, 270 jobject is, // InputStream 271 jbyteArray storage, // byte[] 272 jobject padding, 273 jobject options) { // BitmapFactory$Options 274 jobject bitmap = NULL; 275 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0); 276 277 if (stream) { 278 // for now we don't allow purgeable with java inputstreams 279 bitmap = doDecode(env, stream, padding, options, false); 280 stream->unref(); 281 } 282 return bitmap; 283} 284 285static ssize_t getFDSize(int fd) { 286 off64_t curr = ::lseek64(fd, 0, SEEK_CUR); 287 if (curr < 0) { 288 return 0; 289 } 290 size_t size = ::lseek(fd, 0, SEEK_END); 291 ::lseek64(fd, curr, SEEK_SET); 292 return size; 293} 294 295static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, 296 jobject fileDescriptor, 297 jobject padding, 298 jobject bitmapFactoryOptions) { 299 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 300 301 jint descriptor = env->GetIntField(fileDescriptor, 302 gFileDescriptor_descriptor); 303 304 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); 305 bool isShareable = optionsShareable(env, bitmapFactoryOptions); 306 bool weOwnTheFD = false; 307 if (isPurgeable && isShareable) { 308 int newFD = ::dup(descriptor); 309 if (-1 != newFD) { 310 weOwnTheFD = true; 311 descriptor = newFD; 312 } 313 } 314 315 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); 316 SkAutoUnref aur(stream); 317 if (!stream->isValid()) { 318 return NULL; 319 } 320 321 /* Restore our offset when we leave, so we can be called more than once 322 with the same descriptor. This is only required if we didn't dup the 323 file descriptor, but it is OK to do it all the time. 324 */ 325 AutoFDSeek as(descriptor); 326 327 /* Allow purgeable iff we own the FD, i.e., in the puregeable and 328 shareable case. 329 */ 330 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); 331} 332 333/* make a deep copy of the asset, and return it as a stream, or NULL if there 334 was an error. 335 */ 336static SkStream* copyAssetToStream(Asset* asset) { 337 // if we could "ref/reopen" the asset, we may not need to copy it here 338 off64_t size = asset->seek(0, SEEK_SET); 339 if ((off64_t)-1 == size) { 340 SkDebugf("---- copyAsset: asset rewind failed\n"); 341 return NULL; 342 } 343 344 size = asset->getLength(); 345 if (size <= 0) { 346 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); 347 return NULL; 348 } 349 350 SkStream* stream = new SkMemoryStream(size); 351 void* data = const_cast<void*>(stream->getMemoryBase()); 352 off64_t len = asset->read(data, size); 353 if (len != size) { 354 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); 355 delete stream; 356 stream = NULL; 357 } 358 return stream; 359} 360 361static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, 362 jint native_asset, // Asset 363 jobject padding, // Rect 364 jobject options) { // BitmapFactory$Options 365 SkStream* stream; 366 Asset* asset = reinterpret_cast<Asset*>(native_asset); 367 bool forcePurgeable = optionsPurgeable(env, options); 368 if (forcePurgeable) { 369 // if we could "ref/reopen" the asset, we may not need to copy it here 370 // and we could assume optionsShareable, since assets are always RO 371 stream = copyAssetToStream(asset); 372 if (NULL == stream) { 373 return NULL; 374 } 375 } else { 376 // since we know we'll be done with the asset when we return, we can 377 // just use a simple wrapper 378 stream = new AssetStreamAdaptor(asset); 379 } 380 SkAutoUnref aur(stream); 381 return doDecode(env, stream, padding, options, true, forcePurgeable); 382} 383 384static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 385 int offset, int length, jobject options) { 386 /* If optionsShareable() we could decide to just wrap the java array and 387 share it, but that means adding a globalref to the java array object 388 and managing its lifetime. For now we just always copy the array's data 389 if optionsPurgeable(), unless we're just decoding bounds. 390 */ 391 bool purgeable = optionsPurgeable(env, options) 392 && !optionsJustBounds(env, options); 393 AutoJavaByteArray ar(env, byteArray); 394 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable); 395 SkAutoUnref aur(stream); 396 return doDecode(env, stream, NULL, options, purgeable); 397} 398 399static void nativeRequestCancel(JNIEnv*, jobject joptions) { 400 (void)AutoDecoderCancel::RequestCancel(joptions); 401} 402 403static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 404 jobject padding) { 405 406 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 407 if (array != NULL) { 408 size_t chunkSize = env->GetArrayLength(chunkObject); 409 void* storage = alloca(chunkSize); 410 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 411 memcpy(chunk, array, chunkSize); 412 android::Res_png_9patch::deserialize(chunk); 413 414 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 415 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 416 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 417 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 418 419 for (int i = 0; i < chunk->numXDivs; i++) { 420 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 421 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 422 chunk->xDivs[i]++; 423 } 424 } 425 426 for (int i = 0; i < chunk->numYDivs; i++) { 427 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 428 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 429 chunk->yDivs[i]++; 430 } 431 } 432 433 memcpy(array, chunk, chunkSize); 434 435 if (padding) { 436 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 437 chunk->paddingRight, chunk->paddingBottom); 438 } 439 440 env->ReleaseByteArrayElements(chunkObject, array, 0); 441 } 442 return chunkObject; 443} 444 445static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) { 446 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig); 447 448 // these are the only default configs that make sense for codecs right now 449 static const SkBitmap::Config gValidDefConfig[] = { 450 SkBitmap::kRGB_565_Config, 451 SkBitmap::kARGB_8888_Config, 452 }; 453 454 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) { 455 if (config == gValidDefConfig[i]) { 456 SkImageDecoder::SetDeviceConfig(config); 457 break; 458 } 459 } 460} 461 462static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { 463 jint descriptor = env->GetIntField(fileDescriptor, gFileDescriptor_descriptor); 464 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE; 465} 466 467/////////////////////////////////////////////////////////////////////////////// 468 469static JNINativeMethod gMethods[] = { 470 { "nativeDecodeStream", 471 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 472 (void*)nativeDecodeStream 473 }, 474 475 { "nativeDecodeFileDescriptor", 476 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 477 (void*)nativeDecodeFileDescriptor 478 }, 479 480 { "nativeDecodeAsset", 481 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 482 (void*)nativeDecodeAsset 483 }, 484 485 { "nativeDecodeByteArray", 486 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 487 (void*)nativeDecodeByteArray 488 }, 489 490 { "nativeScaleNinePatch", 491 "([BFLandroid/graphics/Rect;)[B", 492 (void*)nativeScaleNinePatch 493 }, 494 495 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig }, 496 497 { "nativeIsSeekable", 498 "(Ljava/io/FileDescriptor;)Z", 499 (void*)nativeIsSeekable 500 }, 501}; 502 503static JNINativeMethod gOptionsMethods[] = { 504 { "requestCancel", "()V", (void*)nativeRequestCancel } 505}; 506 507static jclass make_globalref(JNIEnv* env, const char classname[]) { 508 jclass c = env->FindClass(classname); 509 SkASSERT(c); 510 return (jclass)env->NewGlobalRef(c); 511} 512 513static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 514 const char fieldname[], const char type[]) { 515 jfieldID id = env->GetFieldID(clazz, fieldname, type); 516 SkASSERT(id); 517 return id; 518} 519 520#define kClassPathName "android/graphics/BitmapFactory" 521 522#define RETURN_ERR_IF_NULL(value) \ 523 do { if (!(value)) { assert(0); return -1; } } while (false) 524 525int register_android_graphics_BitmapFactory(JNIEnv* env); 526int register_android_graphics_BitmapFactory(JNIEnv* env) { 527 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); 528 gOptions_bitmapFieldID = getFieldIDCheck(env, gOptions_class, "inBitmap", 529 "Landroid/graphics/Bitmap;"); 530 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); 531 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); 532 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", 533 "Landroid/graphics/Bitmap$Config;"); 534 gOptions_mutableFieldID = getFieldIDCheck(env, gOptions_class, "inMutable", "Z"); 535 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); 536 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); 537 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); 538 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class, 539 "inPreferQualityOverSpeed", "Z"); 540 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); 541 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); 542 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); 543 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z"); 544 545 gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 546 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); 547 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 548 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 549 550 int ret = AndroidRuntime::registerNativeMethods(env, 551 "android/graphics/BitmapFactory$Options", 552 gOptionsMethods, 553 SK_ARRAY_COUNT(gOptionsMethods)); 554 if (ret) { 555 return ret; 556 } 557 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 558 gMethods, SK_ARRAY_COUNT(gMethods)); 559} 560