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