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