BitmapFactory.cpp revision d24b8183b93e781080b2c16c487e60d51c12da31
1#define LOG_TAG "BitmapFactory" 2 3#include "SkImageDecoder.h" 4#include "SkPixelRef.h" 5#include "SkStream.h" 6#include "GraphicsJNI.h" 7#include "SkTemplates.h" 8#include "SkUtils.h" 9#include "CreateJavaOutputStreamAdaptor.h" 10 11#include <android_runtime/AndroidRuntime.h> 12#include <utils/Asset.h> 13#include <utils/ResourceTypes.h> 14#include <netinet/in.h> 15#include <sys/mman.h> 16 17static jclass gOptions_class; 18static jfieldID gOptions_justBoundsFieldID; 19static jfieldID gOptions_sampleSizeFieldID; 20static jfieldID gOptions_configFieldID; 21static jfieldID gOptions_ditherFieldID; 22static jfieldID gOptions_widthFieldID; 23static jfieldID gOptions_heightFieldID; 24static jfieldID gOptions_mimeFieldID; 25 26static jclass gFileDescriptor_class; 27static jfieldID gFileDescriptor_descriptor; 28 29#if 0 30 #define TRACE_BITMAP(code) code 31#else 32 #define TRACE_BITMAP(code) 33#endif 34 35//#define MIN_SIZE_TO_USE_MMAP (4*1024) 36 37/////////////////////////////////////////////////////////////////////////////// 38 39class AutoDecoderCancel { 40public: 41 AutoDecoderCancel(jobject options, SkImageDecoder* decoder); 42 ~AutoDecoderCancel(); 43 44 static bool RequestCancel(jobject options); 45 46private: 47 AutoDecoderCancel* fNext; 48 AutoDecoderCancel* fPrev; 49 jobject fJOptions; // java options object 50 SkImageDecoder* fDecoder; 51 52#ifdef SK_DEBUG 53 static void Validate(); 54#else 55 static void Validate() {} 56#endif 57}; 58 59static SkMutex gAutoDecoderCancelMutex; 60static AutoDecoderCancel* gAutoDecoderCancel; 61#ifdef SK_DEBUG 62 static int gAutoDecoderCancelCount; 63#endif 64 65AutoDecoderCancel::AutoDecoderCancel(jobject joptions, 66 SkImageDecoder* decoder) { 67 fJOptions = joptions; 68 fDecoder = decoder; 69 70 if (NULL != joptions) { 71 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); 72 73 // Add us as the head of the list 74 fPrev = NULL; 75 fNext = gAutoDecoderCancel; 76 if (gAutoDecoderCancel) { 77 gAutoDecoderCancel->fPrev = this; 78 } 79 gAutoDecoderCancel = this; 80 81 SkDEBUGCODE(gAutoDecoderCancelCount += 1;) 82 Validate(); 83 } 84} 85 86AutoDecoderCancel::~AutoDecoderCancel() { 87 if (NULL != fJOptions) { 88 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); 89 90 // take us out of the dllist 91 AutoDecoderCancel* prev = fPrev; 92 AutoDecoderCancel* next = fNext; 93 94 if (prev) { 95 SkASSERT(prev->fNext == this); 96 prev->fNext = next; 97 } else { 98 SkASSERT(gAutoDecoderCancel == this); 99 gAutoDecoderCancel = next; 100 } 101 if (next) { 102 SkASSERT(next->fPrev == this); 103 next->fPrev = prev; 104 } 105 106 SkDEBUGCODE(gAutoDecoderCancelCount -= 1;) 107 Validate(); 108 } 109} 110 111bool AutoDecoderCancel::RequestCancel(jobject joptions) { 112 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex); 113 114 Validate(); 115 116 AutoDecoderCancel* pair = gAutoDecoderCancel; 117 while (pair != NULL) { 118 if (pair->fJOptions == joptions) { 119 pair->fDecoder->cancelDecode(); 120 return true; 121 } 122 pair = pair->fNext; 123 } 124 return false; 125} 126 127#ifdef SK_DEBUG 128// can only call this inside a lock on gAutoDecoderCancelMutex 129void AutoDecoderCancel::Validate() { 130 const int gCount = gAutoDecoderCancelCount; 131 132 if (gCount == 0) { 133 SkASSERT(gAutoDecoderCancel == NULL); 134 } else { 135 SkASSERT(gCount > 0); 136 137 AutoDecoderCancel* curr = gAutoDecoderCancel; 138 SkASSERT(curr); 139 SkASSERT(curr->fPrev == NULL); 140 141 int count = 0; 142 while (curr) { 143 count += 1; 144 SkASSERT(count <= gCount); 145 if (curr->fPrev) { 146 SkASSERT(curr->fPrev->fNext == curr); 147 } 148 if (curr->fNext) { 149 SkASSERT(curr->fNext->fPrev == curr); 150 } 151 curr = curr->fNext; 152 } 153 SkASSERT(count == gCount); 154 } 155} 156#endif 157 158/////////////////////////////////////////////////////////////////////////////// 159 160using namespace android; 161 162class NinePatchPeeker : public SkImageDecoder::Peeker { 163public: 164 NinePatchPeeker() { 165 fPatchIsValid = false; 166 } 167 168 ~NinePatchPeeker() { 169 if (fPatchIsValid) { 170 free(fPatch); 171 } 172 } 173 174 bool fPatchIsValid; 175 Res_png_9patch* fPatch; 176 177 virtual bool peek(const char tag[], const void* data, size_t length) { 178 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) { 179 Res_png_9patch* patch = (Res_png_9patch*) data; 180 size_t patchSize = patch->serializedSize(); 181 assert(length == patchSize); 182 // You have to copy the data because it is owned by the png reader 183 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize); 184 memcpy(patchNew, patch, patchSize); 185 // this relies on deserialization being done in place 186 Res_png_9patch::deserialize(patchNew); 187 patchNew->fileToDevice(); 188 if (fPatchIsValid) { 189 free(fPatch); 190 } 191 fPatch = patchNew; 192 //printf("9patch: (%d,%d)-(%d,%d)\n", 193 // fPatch.sizeLeft, fPatch.sizeTop, 194 // fPatch.sizeRight, fPatch.sizeBottom); 195 fPatchIsValid = true; 196 } else { 197 fPatch = NULL; 198 } 199 return true; // keep on decoding 200 } 201}; 202 203class AssetStreamAdaptor : public SkStream { 204public: 205 AssetStreamAdaptor(Asset* a) : fAsset(a) {} 206 207 virtual bool rewind() { 208 off_t pos = fAsset->seek(0, SEEK_SET); 209 return pos != (off_t)-1; 210 } 211 212 virtual size_t read(void* buffer, size_t size) { 213 ssize_t amount; 214 215 if (NULL == buffer) { 216 if (0 == size) { // caller is asking us for our total length 217 return fAsset->getLength(); 218 } 219 // asset->seek returns new total offset 220 // we want to return amount that was skipped 221 222 off_t oldOffset = fAsset->seek(0, SEEK_CUR); 223 if (-1 == oldOffset) { 224 return 0; 225 } 226 off_t newOffset = fAsset->seek(size, SEEK_CUR); 227 if (-1 == newOffset) { 228 return 0; 229 } 230 amount = newOffset - oldOffset; 231 } else { 232 amount = fAsset->read(buffer, size); 233 } 234 235 if (amount < 0) { 236 amount = 0; 237 } 238 return amount; 239 } 240 241private: 242 Asset* fAsset; 243}; 244 245/////////////////////////////////////////////////////////////////////////////// 246 247static inline int32_t validOrNeg1(bool isValid, int32_t value) { 248// return isValid ? value : -1; 249 SkASSERT((int)isValid == 0 || (int)isValid == 1); 250 return ((int32_t)isValid - 1) | value; 251} 252 253static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { 254 static const struct { 255 SkImageDecoder::Format fFormat; 256 const char* fMimeType; 257 } gMimeTypes[] = { 258 { SkImageDecoder::kBMP_Format, "image/bmp" }, 259 { SkImageDecoder::kGIF_Format, "image/gif" }, 260 { SkImageDecoder::kICO_Format, "image/x-ico" }, 261 { SkImageDecoder::kJPEG_Format, "image/jpeg" }, 262 { SkImageDecoder::kPNG_Format, "image/png" }, 263 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" } 264 }; 265 266 const char* cstr = NULL; 267 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) { 268 if (gMimeTypes[i].fFormat == format) { 269 cstr = gMimeTypes[i].fMimeType; 270 break; 271 } 272 } 273 274 jstring jstr = 0; 275 if (NULL != cstr) { 276 jstr = env->NewStringUTF(cstr); 277 } 278 return jstr; 279} 280 281static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, 282 jobject options) { 283 284 int sampleSize = 1; 285 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; 286 SkBitmap::Config prefConfig = SkBitmap::kNo_Config; 287 bool doDither = true; 288 289 if (NULL != options) { 290 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 291 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { 292 mode = SkImageDecoder::kDecodeBounds_Mode; 293 } 294 // initialize these, in case we fail later on 295 env->SetIntField(options, gOptions_widthFieldID, -1); 296 env->SetIntField(options, gOptions_heightFieldID, -1); 297 env->SetObjectField(options, gOptions_mimeFieldID, 0); 298 299 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 300 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 301 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 302 } 303 304 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 305 if (NULL == decoder) { 306 return NULL; 307 } 308 309 decoder->setSampleSize(sampleSize); 310 decoder->setDitherImage(doDither); 311 312 NinePatchPeeker peeker; 313 JavaPixelAllocator allocator(env); 314 SkBitmap* bitmap = new SkBitmap; 315 Res_png_9patch dummy9Patch; 316 317 SkAutoTDelete<SkImageDecoder> add(decoder); 318 SkAutoTDelete<SkBitmap> adb(bitmap); 319 320 decoder->setPeeker(&peeker); 321 decoder->setAllocator(&allocator); 322 323 AutoDecoderCancel adc(options, decoder); 324 325 if (!decoder->decode(stream, bitmap, prefConfig, mode)) { 326 return NULL; 327 } 328 329 // update options (if any) 330 if (NULL != options) { 331 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 332 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 333 // TODO: set the mimeType field with the data from the codec. 334 // but how to reuse a set of strings, rather than allocating new one 335 // each time? 336 env->SetObjectField(options, gOptions_mimeFieldID, 337 getMimeTypeString(env, decoder->getFormat())); 338 } 339 340 // if we're in justBounds mode, return now (skip the java bitmap) 341 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 342 return NULL; 343 } 344 345 jbyteArray ninePatchChunk = NULL; 346 if (peeker.fPatchIsValid) { 347 size_t ninePatchArraySize = peeker.fPatch->serializedSize(); 348 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 349 if (NULL == ninePatchChunk) { 350 return NULL; 351 } 352 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, 353 NULL); 354 if (NULL == array) { 355 return NULL; 356 } 357 peeker.fPatch->serialize(array); 358 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 359 } 360 361 // detach bitmap from its autotdeleter, since we want to own it now 362 adb.detach(); 363 364 if (padding) { 365 if (peeker.fPatchIsValid) { 366 GraphicsJNI::set_jrect(env, padding, 367 peeker.fPatch->paddingLeft, 368 peeker.fPatch->paddingTop, 369 peeker.fPatch->paddingRight, 370 peeker.fPatch->paddingBottom); 371 } else { 372 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 373 } 374 } 375 376 // promise we will never change our pixels (great for sharing and pictures) 377 SkPixelRef* ref = bitmap->pixelRef(); 378 SkASSERT(ref); 379 ref->setImmutable(); 380 381 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); 382} 383 384static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, 385 jobject is, // InputStream 386 jbyteArray storage, // byte[] 387 jobject padding, 388 jobject options) { // BitmapFactory$Options 389 jobject bitmap = NULL; 390 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage); 391 392 if (stream) { 393 bitmap = doDecode(env, stream, padding, options); 394 stream->unref(); 395 } 396 return bitmap; 397} 398 399static ssize_t getFDSize(int fd) { 400 off_t curr = ::lseek(fd, 0, SEEK_CUR); 401 if (curr < 0) { 402 return 0; 403 } 404 size_t size = ::lseek(fd, 0, SEEK_END); 405 ::lseek(fd, curr, SEEK_SET); 406 return size; 407} 408 409/** Restore the file descriptor's offset in our destructor 410 */ 411class AutoFDSeek { 412public: 413 AutoFDSeek(int fd) : fFD(fd) { 414 fCurr = ::lseek(fd, 0, SEEK_CUR); 415 } 416 ~AutoFDSeek() { 417 if (fCurr >= 0) { 418 ::lseek(fFD, fCurr, SEEK_SET); 419 } 420 } 421private: 422 int fFD; 423 off_t fCurr; 424}; 425 426static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, 427 jobject fileDescriptor, 428 jobject padding, 429 jobject bitmapFactoryOptions) { 430 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 431 432 jint descriptor = env->GetIntField(fileDescriptor, 433 gFileDescriptor_descriptor); 434 435#ifdef MIN_SIZE_TO_USE_MMAP 436 // First try to use mmap 437 size_t size = getFDSize(descriptor); 438 if (size >= MIN_SIZE_TO_USE_MMAP) { 439 void* addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, 0); 440// SkDebugf("-------- mmap returned %p %d\n", addr, size); 441 if (MAP_FAILED != addr) { 442 SkMemoryStream strm(addr, size); 443 jobject obj = doDecode(env, &strm, padding, bitmapFactoryOptions); 444 munmap(addr, size); 445 return obj; 446 } 447 } 448#endif 449 450 // we pass false for closeWhenDone, since the caller owns the descriptor 451 SkFDStream file(descriptor, false); 452 if (!file.isValid()) { 453 return NULL; 454 } 455 456 /* Restore our offset when we leave, so the caller doesn't have to. 457 This is a real feature, so we can be called more than once with the 458 same descriptor. 459 */ 460 AutoFDSeek as(descriptor); 461 462 return doDecode(env, &file, padding, bitmapFactoryOptions); 463} 464 465static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, 466 jint native_asset, // Asset 467 jobject padding, // Rect 468 jobject options) { // BitmapFactory$Options 469 AssetStreamAdaptor mystream((Asset*)native_asset); 470 471 return doDecode(env, &mystream, padding, options); 472} 473 474static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 475 int offset, int length, jobject options) { 476 AutoJavaByteArray ar(env, byteArray); 477 SkMemoryStream stream(ar.ptr() + offset, length); 478 479 return doDecode(env, &stream, NULL, options); 480} 481 482static void nativeRequestCancel(JNIEnv*, jobject joptions) { 483 (void)AutoDecoderCancel::RequestCancel(joptions); 484} 485 486static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 487 jobject padding) { 488 489 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 490 if (array != NULL) { 491 size_t chunkSize = env->GetArrayLength(chunkObject); 492 void* storage = alloca(chunkSize); 493 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 494 memcpy(chunk, array, chunkSize); 495 android::Res_png_9patch::deserialize(chunk); 496 497 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 498 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 499 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 500 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 501 502 for (int i = 0; i < chunk->numXDivs; i++) { 503 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 504 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 505 chunk->xDivs[i]++; 506 } 507 } 508 509 for (int i = 0; i < chunk->numYDivs; i++) { 510 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 511 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 512 chunk->yDivs[i]++; 513 } 514 } 515 516 memcpy(array, chunk, chunkSize); 517 518 if (padding) { 519 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 520 chunk->paddingRight, chunk->paddingBottom); 521 } 522 523 env->ReleaseByteArrayElements(chunkObject, array, 0); 524 } 525 return chunkObject; 526} 527 528/////////////////////////////////////////////////////////////////////////////// 529 530static JNINativeMethod gMethods[] = { 531 { "nativeDecodeStream", 532 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 533 (void*)nativeDecodeStream 534 }, 535 536 { "nativeDecodeFileDescriptor", 537 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 538 (void*)nativeDecodeFileDescriptor 539 }, 540 541 { "nativeDecodeAsset", 542 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 543 (void*)nativeDecodeAsset 544 }, 545 546 { "nativeDecodeByteArray", 547 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 548 (void*)nativeDecodeByteArray 549 }, 550 551 { "nativeScaleNinePatch", 552 "([BFLandroid/graphics/Rect;)[B", 553 (void*)nativeScaleNinePatch 554 } 555 556}; 557 558static JNINativeMethod gOptionsMethods[] = { 559 { "requestCancel", "()V", (void*)nativeRequestCancel } 560}; 561 562static jclass make_globalref(JNIEnv* env, const char classname[]) { 563 jclass c = env->FindClass(classname); 564 SkASSERT(c); 565 return (jclass)env->NewGlobalRef(c); 566} 567 568static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 569 const char fieldname[], const char type[]) { 570 jfieldID id = env->GetFieldID(clazz, fieldname, type); 571 SkASSERT(id); 572 return id; 573} 574 575#define kClassPathName "android/graphics/BitmapFactory" 576 577#define RETURN_ERR_IF_NULL(value) \ 578 do { if (!(value)) { assert(0); return -1; } } while (false) 579 580int register_android_graphics_BitmapFactory(JNIEnv* env); 581int register_android_graphics_BitmapFactory(JNIEnv* env) { 582 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); 583 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); 584 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); 585 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", 586 "Landroid/graphics/Bitmap$Config;"); 587 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); 588 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); 589 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); 590 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); 591 592 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 593 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 594 595 int ret = AndroidRuntime::registerNativeMethods(env, 596 "android/graphics/BitmapFactory$Options", 597 gOptionsMethods, 598 SK_ARRAY_COUNT(gOptionsMethods)); 599 if (ret) { 600 return ret; 601 } 602 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 603 gMethods, SK_ARRAY_COUNT(gMethods)); 604} 605