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