BitmapFactory.cpp revision afce5a4008c494f6384e1b6b2fb4f13d33e917cb
1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define LOG_TAG "BitmapFactory" 2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "BitmapFactory.h" 4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "NinePatchPeeker.h" 5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkImageDecoder.h" 6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkImageRef_ashmem.h" 7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkImageRef_GlobalPool.h" 8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkPixelRef.h" 9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkStream.h" 10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkTemplates.h" 11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkUtils.h" 12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "CreateJavaOutputStreamAdaptor.h" 13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "AutoDecodeCancel.h" 14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "Utils.h" 15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "JNIHelp.h" 16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <android_runtime/AndroidRuntime.h> 18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <androidfw/Asset.h> 19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <androidfw/ResourceTypes.h> 20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <netinet/in.h> 21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <sys/mman.h> 22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <sys/stat.h> 23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 24282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_justBoundsFieldID; 25282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_sampleSizeFieldID; 26282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_configFieldID; 27282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_mutableFieldID; 28282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_ditherFieldID; 29282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_purgeableFieldID; 30282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_shareableFieldID; 31282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_preferQualityOverSpeedFieldID; 32282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_widthFieldID; 33282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_heightFieldID; 34282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_mimeFieldID; 35282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_mCancelID; 36282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_bitmapFieldID; 37282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gBitmap_nativeBitmapFieldID; 38282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gBitmap_layoutBoundsFieldID; 39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#if 0 41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski #define TRACE_BITMAP(code) code 42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#else 43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski #define TRACE_BITMAP(code) 44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#endif 45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiusing namespace android; 47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic inline int32_t validOrNeg1(bool isValid, int32_t value) { 49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski// return isValid ? value : -1; 50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski SkASSERT((int)isValid == 0 || (int)isValid == 1); 51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return ((int32_t)isValid - 1) | value; 52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski} 53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskijstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { 55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski static const struct { 56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski SkImageDecoder::Format fFormat; 57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski const char* fMimeType; 58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } gMimeTypes[] = { 59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski { SkImageDecoder::kBMP_Format, "image/bmp" }, 60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski { SkImageDecoder::kGIF_Format, "image/gif" }, 61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski { SkImageDecoder::kICO_Format, "image/x-ico" }, 62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski { SkImageDecoder::kJPEG_Format, "image/jpeg" }, 63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski { SkImageDecoder::kPNG_Format, "image/png" }, 64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" } 65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski }; 66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski const char* cstr = NULL; 68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) { 69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (gMimeTypes[i].fFormat == format) { 70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski cstr = gMimeTypes[i].fMimeType; 71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski break; 72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski jstring jstr = 0; 76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (NULL != cstr) { 77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski jstr = env->NewStringUTF(cstr); 78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return jstr; 80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski} 81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic bool optionsPurgeable(JNIEnv* env, jobject options) { 83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID); 84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski} 85 86static bool optionsShareable(JNIEnv* env, jobject options) { 87 return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID); 88} 89 90static bool optionsJustBounds(JNIEnv* env, jobject options) { 91 return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID); 92} 93 94static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { 95 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 96 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 97 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 98 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 99 100 for (int i = 0; i < chunk->numXDivs; i++) { 101 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 102 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 103 chunk->xDivs[i]++; 104 } 105 } 106 107 for (int i = 0; i < chunk->numYDivs; i++) { 108 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 109 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 110 chunk->yDivs[i]++; 111 } 112 } 113} 114 115static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 116 jobject padding) { 117 118 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 119 if (array != NULL) { 120 size_t chunkSize = env->GetArrayLength(chunkObject); 121 void* storage = alloca(chunkSize); 122 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 123 memcpy(chunk, array, chunkSize); 124 android::Res_png_9patch::deserialize(chunk); 125 126 scaleNinePatchChunk(chunk, scale); 127 memcpy(array, chunk, chunkSize); 128 129 if (padding) { 130 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 131 chunk->paddingRight, chunk->paddingBottom); 132 } 133 134 env->ReleaseByteArrayElements(chunkObject, array, 0); 135 } 136 return chunkObject; 137} 138 139static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, 140 int sampleSize, bool ditherImage) { 141 142 SkImageRef* pr; 143 // only use ashmem for large images, since mmaps come at a price 144 if (bitmap->getSize() >= 32 * 1024) { 145 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); 146 } else { 147 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); 148 } 149 pr->setDitherImage(ditherImage); 150 bitmap->setPixelRef(pr)->unref(); 151 pr->isOpaque(bitmap); 152 return pr; 153} 154 155// since we "may" create a purgeable imageref, we require the stream be ref'able 156// i.e. dynamically allocated, since its lifetime may exceed the current stack 157// frame. 158static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, 159 jobject options, bool allowPurgeable, bool forcePurgeable = false, 160 bool applyScale = false, float scale = 1.0f) { 161 162 int sampleSize = 1; 163 164 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; 165 SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; 166 167 bool doDither = true; 168 bool isMutable = false; 169 bool willScale = applyScale && scale != 1.0f; 170 bool isPurgeable = !willScale && 171 (forcePurgeable || (allowPurgeable && optionsPurgeable(env, options))); 172 bool preferQualityOverSpeed = false; 173 174 jobject javaBitmap = NULL; 175 176 if (options != NULL) { 177 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 178 if (optionsJustBounds(env, options)) { 179 mode = SkImageDecoder::kDecodeBounds_Mode; 180 } 181 182 // initialize these, in case we fail later on 183 env->SetIntField(options, gOptions_widthFieldID, -1); 184 env->SetIntField(options, gOptions_heightFieldID, -1); 185 env->SetObjectField(options, gOptions_mimeFieldID, 0); 186 187 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 188 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 189 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); 190 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 191 preferQualityOverSpeed = env->GetBooleanField(options, 192 gOptions_preferQualityOverSpeedFieldID); 193 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 194 } 195 196 if (willScale && javaBitmap != NULL) { 197 return nullObjectReturn("Cannot pre-scale a reused bitmap"); 198 } 199 200 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 201 if (decoder == NULL) { 202 return nullObjectReturn("SkImageDecoder::Factory returned null"); 203 } 204 205 decoder->setSampleSize(sampleSize); 206 decoder->setDitherImage(doDither); 207 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 208 209 NinePatchPeeker peeker(decoder); 210 JavaPixelAllocator javaAllocator(env); 211 212 SkBitmap* bitmap; 213 bool useExistingBitmap = false; 214 if (javaBitmap == NULL) { 215 bitmap = new SkBitmap; 216 } else { 217 if (sampleSize != 1) { 218 return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1"); 219 } 220 221 bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID); 222 // only reuse the provided bitmap if it is immutable 223 if (!bitmap->isImmutable()) { 224 useExistingBitmap = true; 225 // config of supplied bitmap overrules config set in options 226 prefConfig = bitmap->getConfig(); 227 } else { 228 ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); 229 bitmap = new SkBitmap; 230 } 231 } 232 233 SkAutoTDelete<SkImageDecoder> add(decoder); 234 SkAutoTDelete<SkBitmap> adb(bitmap, !useExistingBitmap); 235 236 decoder->setPeeker(&peeker); 237 if (!isPurgeable) { 238 decoder->setAllocator(&javaAllocator); 239 } 240 241 AutoDecoderCancel adc(options, decoder); 242 243 // To fix the race condition in case "requestCancelDecode" 244 // happens earlier than AutoDecoderCancel object is added 245 // to the gAutoDecoderCancelMutex linked list. 246 if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) { 247 return nullObjectReturn("gOptions_mCancelID"); 248 } 249 250 SkImageDecoder::Mode decodeMode = mode; 251 if (isPurgeable) { 252 decodeMode = SkImageDecoder::kDecodeBounds_Mode; 253 } 254 255 SkBitmap* decoded; 256 if (willScale) { 257 decoded = new SkBitmap; 258 } else { 259 decoded = bitmap; 260 } 261 SkAutoTDelete<SkBitmap> adb2(willScale ? decoded : NULL); 262 263 if (!decoder->decode(stream, decoded, prefConfig, decodeMode, javaBitmap != NULL)) { 264 return nullObjectReturn("decoder->decode returned false"); 265 } 266 267 int scaledWidth = decoded->width(); 268 int scaledHeight = decoded->height(); 269 270 if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) { 271 scaledWidth = int(scaledWidth * scale + 0.5f); 272 scaledHeight = int(scaledHeight * scale + 0.5f); 273 } 274 275 // update options (if any) 276 if (options != NULL) { 277 env->SetIntField(options, gOptions_widthFieldID, scaledWidth); 278 env->SetIntField(options, gOptions_heightFieldID, scaledHeight); 279 env->SetObjectField(options, gOptions_mimeFieldID, 280 getMimeTypeString(env, decoder->getFormat())); 281 } 282 283 // if we're in justBounds mode, return now (skip the java bitmap) 284 if (mode == SkImageDecoder::kDecodeBounds_Mode) { 285 return NULL; 286 } 287 288 jbyteArray ninePatchChunk = NULL; 289 if (peeker.fPatch != NULL) { 290 if (willScale) { 291 scaleNinePatchChunk(peeker.fPatch, scale); 292 } 293 294 size_t ninePatchArraySize = peeker.fPatch->serializedSize(); 295 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 296 if (ninePatchChunk == NULL) { 297 return nullObjectReturn("ninePatchChunk == null"); 298 } 299 300 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); 301 if (array == NULL) { 302 return nullObjectReturn("primitive array == null"); 303 } 304 305 peeker.fPatch->serialize(array); 306 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 307 } 308 309 jintArray layoutBounds = NULL; 310 if (peeker.fLayoutBounds != NULL) { 311 layoutBounds = env->NewIntArray(4); 312 if (layoutBounds == NULL) { 313 return nullObjectReturn("layoutBounds == null"); 314 } 315 316 jint scaledBounds[4]; 317 if (willScale) { 318 for (int i=0; i<4; i++) { 319 scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f); 320 } 321 } else { 322 memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds)); 323 } 324 env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds); 325 if (javaBitmap != NULL) { 326 env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds); 327 } 328 } 329 330 if (willScale) { 331 // This is weird so let me explain: we could use the scale parameter 332 // directly, but for historical reasons this is how the corresponding 333 // Dalvik code has always behaved. We simply recreate the behavior here. 334 // The result is slightly different from simply using scale because of 335 // the 0.5f rounding bias applied when computing the target image size 336 const float sx = scaledWidth / float(decoded->width()); 337 const float sy = scaledHeight / float(decoded->height()); 338 339 SkBitmap::Config config = decoded->config(); 340 switch (config) { 341 case SkBitmap::kNo_Config: 342 case SkBitmap::kIndex8_Config: 343 case SkBitmap::kRLE_Index8_Config: 344 config = SkBitmap::kARGB_8888_Config; 345 break; 346 default: 347 break; 348 } 349 350 bitmap->setConfig(config, scaledWidth, scaledHeight); 351 bitmap->setIsOpaque(decoded->isOpaque()); 352 if (!bitmap->allocPixels(&javaAllocator, NULL)) { 353 return nullObjectReturn("allocation failed for scaled bitmap"); 354 } 355 bitmap->eraseColor(0); 356 357 SkPaint paint; 358 paint.setFilterBitmap(true); 359 360 SkCanvas canvas(*bitmap); 361 canvas.scale(sx, sy); 362 canvas.drawBitmap(*decoded, 0.0f, 0.0f, &paint); 363 } 364 365 if (padding) { 366 if (peeker.fPatch != NULL) { 367 GraphicsJNI::set_jrect(env, padding, 368 peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop, 369 peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom); 370 } else { 371 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 372 } 373 } 374 375 SkPixelRef* pr; 376 if (isPurgeable) { 377 pr = installPixelRef(bitmap, stream, sampleSize, doDither); 378 } else { 379 // if we get here, we're in kDecodePixels_Mode and will therefore 380 // already have a pixelref installed. 381 pr = bitmap->pixelRef(); 382 } 383 if (pr == NULL) { 384 return nullObjectReturn("Got null SkPixelRef"); 385 } 386 387 if (!isMutable && !useExistingBitmap) { 388 // promise we will never change our pixels (great for sharing and pictures) 389 pr->setImmutable(); 390 } 391 392 // detach bitmap from its autodeleter, since we want to own it now 393 adb.detach(); 394 395 if (useExistingBitmap) { 396 // If a java bitmap was passed in for reuse, pass it back 397 return javaBitmap; 398 } 399 // now create the java bitmap 400 return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), 401 isMutable, ninePatchChunk, layoutBounds, -1); 402} 403 404static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, 405 jobject padding, jobject options, jboolean applyScale, jfloat scale) { 406 407 jobject bitmap = NULL; 408 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0); 409 410 if (stream) { 411 // for now we don't allow purgeable with java inputstreams 412 bitmap = doDecode(env, stream, padding, options, false, false, applyScale, scale); 413 stream->unref(); 414 } 415 return bitmap; 416} 417 418static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, 419 jobject padding, jobject options) { 420 421 return nativeDecodeStreamScaled(env, clazz, is, storage, padding, options, false, 1.0f); 422} 423 424static ssize_t getFDSize(int fd) { 425 off64_t curr = ::lseek64(fd, 0, SEEK_CUR); 426 if (curr < 0) { 427 return 0; 428 } 429 size_t size = ::lseek(fd, 0, SEEK_END); 430 ::lseek64(fd, curr, SEEK_SET); 431 return size; 432} 433 434static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, 435 jobject padding, jobject bitmapFactoryOptions) { 436 437 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 438 439 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 440 441 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); 442 bool isShareable = optionsShareable(env, bitmapFactoryOptions); 443 bool weOwnTheFD = false; 444 if (isPurgeable && isShareable) { 445 int newFD = ::dup(descriptor); 446 if (-1 != newFD) { 447 weOwnTheFD = true; 448 descriptor = newFD; 449 } 450 } 451 452 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); 453 SkAutoUnref aur(stream); 454 if (!stream->isValid()) { 455 return NULL; 456 } 457 458 /* Restore our offset when we leave, so we can be called more than once 459 with the same descriptor. This is only required if we didn't dup the 460 file descriptor, but it is OK to do it all the time. 461 */ 462 AutoFDSeek as(descriptor); 463 464 /* Allow purgeable iff we own the FD, i.e., in the puregeable and 465 shareable case. 466 */ 467 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); 468} 469 470/* make a deep copy of the asset, and return it as a stream, or NULL if there 471 was an error. 472 */ 473static SkStream* copyAssetToStream(Asset* asset) { 474 // if we could "ref/reopen" the asset, we may not need to copy it here 475 off64_t size = asset->seek(0, SEEK_SET); 476 if ((off64_t)-1 == size) { 477 SkDebugf("---- copyAsset: asset rewind failed\n"); 478 return NULL; 479 } 480 481 size = asset->getLength(); 482 if (size <= 0) { 483 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); 484 return NULL; 485 } 486 487 SkStream* stream = new SkMemoryStream(size); 488 void* data = const_cast<void*>(stream->getMemoryBase()); 489 off64_t len = asset->read(data, size); 490 if (len != size) { 491 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); 492 delete stream; 493 stream = NULL; 494 } 495 return stream; 496} 497 498static jobject nativeDecodeAssetScaled(JNIEnv* env, jobject clazz, jint native_asset, 499 jobject padding, jobject options, jboolean applyScale, jfloat scale) { 500 501 SkStream* stream; 502 Asset* asset = reinterpret_cast<Asset*>(native_asset); 503 bool forcePurgeable = optionsPurgeable(env, options); 504 if (forcePurgeable) { 505 // if we could "ref/reopen" the asset, we may not need to copy it here 506 // and we could assume optionsShareable, since assets are always RO 507 stream = copyAssetToStream(asset); 508 if (stream == NULL) { 509 return NULL; 510 } 511 } else { 512 // since we know we'll be done with the asset when we return, we can 513 // just use a simple wrapper 514 stream = new AssetStreamAdaptor(asset); 515 } 516 SkAutoUnref aur(stream); 517 return doDecode(env, stream, padding, options, true, forcePurgeable, applyScale, scale); 518} 519 520static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset, 521 jobject padding, jobject options) { 522 523 return nativeDecodeAssetScaled(env, clazz, native_asset, padding, options, false, 1.0f); 524} 525 526static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 527 int offset, int length, jobject options) { 528 529 /* If optionsShareable() we could decide to just wrap the java array and 530 share it, but that means adding a globalref to the java array object 531 and managing its lifetime. For now we just always copy the array's data 532 if optionsPurgeable(), unless we're just decoding bounds. 533 */ 534 bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options); 535 AutoJavaByteArray ar(env, byteArray); 536 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable); 537 SkAutoUnref aur(stream); 538 return doDecode(env, stream, NULL, options, purgeable); 539} 540 541static void nativeRequestCancel(JNIEnv*, jobject joptions) { 542 (void)AutoDecoderCancel::RequestCancel(joptions); 543} 544 545static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { 546 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 547 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE; 548} 549 550/////////////////////////////////////////////////////////////////////////////// 551 552static JNINativeMethod gMethods[] = { 553 { "nativeDecodeStream", 554 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 555 (void*)nativeDecodeStream 556 }, 557 { "nativeDecodeStream", 558 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;", 559 (void*)nativeDecodeStreamScaled 560 }, 561 562 { "nativeDecodeFileDescriptor", 563 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 564 (void*)nativeDecodeFileDescriptor 565 }, 566 567 { "nativeDecodeAsset", 568 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 569 (void*)nativeDecodeAsset 570 }, 571 572 { "nativeDecodeAsset", 573 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;", 574 (void*)nativeDecodeAssetScaled 575 }, 576 577 { "nativeDecodeByteArray", 578 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 579 (void*)nativeDecodeByteArray 580 }, 581 582 { "nativeScaleNinePatch", 583 "([BFLandroid/graphics/Rect;)[B", 584 (void*)nativeScaleNinePatch 585 }, 586 587 { "nativeIsSeekable", 588 "(Ljava/io/FileDescriptor;)Z", 589 (void*)nativeIsSeekable 590 }, 591}; 592 593static JNINativeMethod gOptionsMethods[] = { 594 { "requestCancel", "()V", (void*)nativeRequestCancel } 595}; 596 597static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 598 const char fieldname[], const char type[]) { 599 jfieldID id = env->GetFieldID(clazz, fieldname, type); 600 SkASSERT(id); 601 return id; 602} 603 604int register_android_graphics_BitmapFactory(JNIEnv* env) { 605 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options"); 606 SkASSERT(options_class); 607 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap", 608 "Landroid/graphics/Bitmap;"); 609 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z"); 610 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I"); 611 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig", 612 "Landroid/graphics/Bitmap$Config;"); 613 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z"); 614 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z"); 615 gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z"); 616 gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z"); 617 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class, 618 "inPreferQualityOverSpeed", "Z"); 619 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I"); 620 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I"); 621 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;"); 622 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z"); 623 624 jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); 625 SkASSERT(bitmap_class); 626 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I"); 627 gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I"); 628 int ret = AndroidRuntime::registerNativeMethods(env, 629 "android/graphics/BitmapFactory$Options", 630 gOptionsMethods, 631 SK_ARRAY_COUNT(gOptionsMethods)); 632 if (ret) { 633 return ret; 634 } 635 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory", 636 gMethods, SK_ARRAY_COUNT(gMethods)); 637} 638