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