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