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