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