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