1 #define LOG_TAG "BitmapFactory"
2
3 #include "BitmapFactory.h"
4 #include "NinePatchPeeker.h"
5 #include "SkData.h"
6 #include "SkFrontBufferedStream.h"
7 #include "SkImageDecoder.h"
8 #include "SkImageRef_ashmem.h"
9 #include "SkImageRef_GlobalPool.h"
10 #include "SkPixelRef.h"
11 #include "SkStream.h"
12 #include "SkTemplates.h"
13 #include "SkUtils.h"
14 #include "CreateJavaOutputStreamAdaptor.h"
15 #include "AutoDecodeCancel.h"
16 #include "Utils.h"
17 #include "JNIHelp.h"
18 #include "GraphicsJNI.h"
19
20 #include <android_runtime/AndroidRuntime.h>
21 #include <androidfw/Asset.h>
22 #include <androidfw/ResourceTypes.h>
23 #include <netinet/in.h>
24 #include <stdio.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27
28 jfieldID gOptions_justBoundsFieldID;
29 jfieldID gOptions_sampleSizeFieldID;
30 jfieldID gOptions_configFieldID;
31 jfieldID gOptions_premultipliedFieldID;
32 jfieldID gOptions_mutableFieldID;
33 jfieldID gOptions_ditherFieldID;
34 jfieldID gOptions_purgeableFieldID;
35 jfieldID gOptions_shareableFieldID;
36 jfieldID gOptions_preferQualityOverSpeedFieldID;
37 jfieldID gOptions_scaledFieldID;
38 jfieldID gOptions_densityFieldID;
39 jfieldID gOptions_screenDensityFieldID;
40 jfieldID gOptions_targetDensityFieldID;
41 jfieldID gOptions_widthFieldID;
42 jfieldID gOptions_heightFieldID;
43 jfieldID gOptions_mimeFieldID;
44 jfieldID gOptions_mCancelID;
45 jfieldID gOptions_bitmapFieldID;
46 jfieldID gBitmap_nativeBitmapFieldID;
47 jfieldID gBitmap_layoutBoundsFieldID;
48
49 #if 0
50 #define TRACE_BITMAP(code) code
51 #else
52 #define TRACE_BITMAP(code)
53 #endif
54
55 using namespace android;
56
validOrNeg1(bool isValid,int32_t value)57 static inline int32_t validOrNeg1(bool isValid, int32_t value) {
58 // return isValid ? value : -1;
59 SkASSERT((int)isValid == 0 || (int)isValid == 1);
60 return ((int32_t)isValid - 1) | value;
61 }
62
getMimeTypeString(JNIEnv * env,SkImageDecoder::Format format)63 jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
64 static const struct {
65 SkImageDecoder::Format fFormat;
66 const char* fMimeType;
67 } gMimeTypes[] = {
68 { SkImageDecoder::kBMP_Format, "image/bmp" },
69 { SkImageDecoder::kGIF_Format, "image/gif" },
70 { SkImageDecoder::kICO_Format, "image/x-ico" },
71 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
72 { SkImageDecoder::kPNG_Format, "image/png" },
73 { SkImageDecoder::kWEBP_Format, "image/webp" },
74 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
75 };
76
77 const char* cstr = NULL;
78 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
79 if (gMimeTypes[i].fFormat == format) {
80 cstr = gMimeTypes[i].fMimeType;
81 break;
82 }
83 }
84
85 jstring jstr = 0;
86 if (NULL != cstr) {
87 jstr = env->NewStringUTF(cstr);
88 }
89 return jstr;
90 }
91
optionsPurgeable(JNIEnv * env,jobject options)92 static bool optionsPurgeable(JNIEnv* env, jobject options) {
93 return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
94 }
95
optionsShareable(JNIEnv * env,jobject options)96 static bool optionsShareable(JNIEnv* env, jobject options) {
97 return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
98 }
99
optionsJustBounds(JNIEnv * env,jobject options)100 static bool optionsJustBounds(JNIEnv* env, jobject options) {
101 return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
102 }
103
scaleNinePatchChunk(android::Res_png_9patch * chunk,float scale)104 static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
105 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
106 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
107 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
108 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
109
110 for (int i = 0; i < chunk->numXDivs; i++) {
111 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
112 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
113 chunk->xDivs[i]++;
114 }
115 }
116
117 for (int i = 0; i < chunk->numYDivs; i++) {
118 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
119 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
120 chunk->yDivs[i]++;
121 }
122 }
123 }
124
installPixelRef(SkBitmap * bitmap,SkStreamRewindable * stream,int sampleSize,bool ditherImage)125 static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream,
126 int sampleSize, bool ditherImage) {
127
128 SkImageInfo bitmapInfo;
129 if (!bitmap->asImageInfo(&bitmapInfo)) {
130 ALOGW("bitmap has unknown configuration so no memory has been allocated");
131 return NULL;
132 }
133
134 SkImageRef* pr;
135 // only use ashmem for large images, since mmaps come at a price
136 if (bitmap->getSize() >= 32 * 1024) {
137 pr = new SkImageRef_ashmem(bitmapInfo, stream, sampleSize);
138 } else {
139 pr = new SkImageRef_GlobalPool(bitmapInfo, stream, sampleSize);
140 }
141 pr->setDitherImage(ditherImage);
142 bitmap->setPixelRef(pr)->unref();
143 pr->isOpaque(bitmap);
144 return pr;
145 }
146
configForScaledOutput(SkBitmap::Config config)147 static SkBitmap::Config configForScaledOutput(SkBitmap::Config config) {
148 switch (config) {
149 case SkBitmap::kNo_Config:
150 case SkBitmap::kIndex8_Config:
151 return SkBitmap::kARGB_8888_Config;
152 default:
153 break;
154 }
155 return config;
156 }
157
158 class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
159 public:
ScaleCheckingAllocator(float scale,int size)160 ScaleCheckingAllocator(float scale, int size)
161 : mScale(scale), mSize(size) {
162 }
163
allocPixelRef(SkBitmap * bitmap,SkColorTable * ctable)164 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
165 // accounts for scale in final allocation, using eventual size and config
166 const int bytesPerPixel = SkBitmap::ComputeBytesPerPixel(
167 configForScaledOutput(bitmap->config()));
168 const int requestedSize = bytesPerPixel *
169 int(bitmap->width() * mScale + 0.5f) *
170 int(bitmap->height() * mScale + 0.5f);
171 if (requestedSize > mSize) {
172 ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
173 mSize, requestedSize);
174 return false;
175 }
176 return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
177 }
178 private:
179 const float mScale;
180 const int mSize;
181 };
182
183 class RecyclingPixelAllocator : public SkBitmap::Allocator {
184 public:
RecyclingPixelAllocator(SkPixelRef * pixelRef,unsigned int size)185 RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
186 : mPixelRef(pixelRef), mSize(size) {
187 SkSafeRef(mPixelRef);
188 }
189
~RecyclingPixelAllocator()190 ~RecyclingPixelAllocator() {
191 SkSafeUnref(mPixelRef);
192 }
193
allocPixelRef(SkBitmap * bitmap,SkColorTable * ctable)194 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
195 if (!bitmap->getSize64().is32() || bitmap->getSize() > mSize) {
196 ALOGW("bitmap marked for reuse (%d bytes) can't fit new bitmap (%d bytes)",
197 mSize, bitmap->getSize());
198 return false;
199 }
200
201 SkImageInfo bitmapInfo;
202 if (!bitmap->asImageInfo(&bitmapInfo)) {
203 ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
204 return false;
205 }
206
207 // Create a new pixelref with the new ctable that wraps the previous pixelref
208 SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef),
209 bitmapInfo, bitmap->rowBytes(), ctable);
210
211 bitmap->setPixelRef(pr)->unref();
212 // since we're already allocated, we lockPixels right away
213 // HeapAllocator/JavaPixelAllocator behaves this way too
214 bitmap->lockPixels();
215 return true;
216 }
217
218 private:
219 SkPixelRef* const mPixelRef;
220 const unsigned int mSize;
221 };
222
223 // since we "may" create a purgeable imageref, we require the stream be ref'able
224 // i.e. dynamically allocated, since its lifetime may exceed the current stack
225 // frame.
doDecode(JNIEnv * env,SkStreamRewindable * stream,jobject padding,jobject options,bool allowPurgeable,bool forcePurgeable=false)226 static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
227 jobject options, bool allowPurgeable, bool forcePurgeable = false) {
228
229 int sampleSize = 1;
230
231 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
232 SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
233
234 bool doDither = true;
235 bool isMutable = false;
236 float scale = 1.0f;
237 bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
238 bool preferQualityOverSpeed = false;
239 bool requireUnpremultiplied = false;
240
241 jobject javaBitmap = NULL;
242
243 if (options != NULL) {
244 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
245 if (optionsJustBounds(env, options)) {
246 mode = SkImageDecoder::kDecodeBounds_Mode;
247 }
248
249 // initialize these, in case we fail later on
250 env->SetIntField(options, gOptions_widthFieldID, -1);
251 env->SetIntField(options, gOptions_heightFieldID, -1);
252 env->SetObjectField(options, gOptions_mimeFieldID, 0);
253
254 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
255 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
256 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
257 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
258 preferQualityOverSpeed = env->GetBooleanField(options,
259 gOptions_preferQualityOverSpeedFieldID);
260 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
261 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
262
263 if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
264 const int density = env->GetIntField(options, gOptions_densityFieldID);
265 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
266 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
267 if (density != 0 && targetDensity != 0 && density != screenDensity) {
268 scale = (float) targetDensity / density;
269 }
270 }
271 }
272
273 const bool willScale = scale != 1.0f;
274 isPurgeable &= !willScale;
275
276 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
277 if (decoder == NULL) {
278 return nullObjectReturn("SkImageDecoder::Factory returned null");
279 }
280
281 decoder->setSampleSize(sampleSize);
282 decoder->setDitherImage(doDither);
283 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
284 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
285
286 SkBitmap* outputBitmap = NULL;
287 unsigned int existingBufferSize = 0;
288 if (javaBitmap != NULL) {
289 outputBitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
290 if (outputBitmap->isImmutable()) {
291 ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
292 javaBitmap = NULL;
293 outputBitmap = NULL;
294 } else {
295 existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
296 }
297 }
298
299 SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
300 if (outputBitmap == NULL) outputBitmap = adb.get();
301
302 NinePatchPeeker peeker(decoder);
303 decoder->setPeeker(&peeker);
304
305 SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
306
307 JavaPixelAllocator javaAllocator(env);
308 RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
309 ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
310 SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
311 (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
312 if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
313 if (!willScale) {
314 // If the java allocator is being used to allocate the pixel memory, the decoder
315 // need not write zeroes, since the memory is initialized to 0.
316 decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator);
317 decoder->setAllocator(outputAllocator);
318 } else if (javaBitmap != NULL) {
319 // check for eventual scaled bounds at allocation time, so we don't decode the bitmap
320 // only to find the scaled result too large to fit in the allocation
321 decoder->setAllocator(&scaleCheckingAllocator);
322 }
323 }
324
325 // Only setup the decoder to be deleted after its stack-based, refcounted
326 // components (allocators, peekers, etc) are declared. This prevents RefCnt
327 // asserts from firing due to the order objects are deleted from the stack.
328 SkAutoTDelete<SkImageDecoder> add(decoder);
329
330 AutoDecoderCancel adc(options, decoder);
331
332 // To fix the race condition in case "requestCancelDecode"
333 // happens earlier than AutoDecoderCancel object is added
334 // to the gAutoDecoderCancelMutex linked list.
335 if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
336 return nullObjectReturn("gOptions_mCancelID");
337 }
338
339 SkBitmap decodingBitmap;
340 if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
341 return nullObjectReturn("decoder->decode returned false");
342 }
343
344 int scaledWidth = decodingBitmap.width();
345 int scaledHeight = decodingBitmap.height();
346
347 if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
348 scaledWidth = int(scaledWidth * scale + 0.5f);
349 scaledHeight = int(scaledHeight * scale + 0.5f);
350 }
351
352 // update options (if any)
353 if (options != NULL) {
354 env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
355 env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
356 env->SetObjectField(options, gOptions_mimeFieldID,
357 getMimeTypeString(env, decoder->getFormat()));
358 }
359
360 // if we're in justBounds mode, return now (skip the java bitmap)
361 if (mode == SkImageDecoder::kDecodeBounds_Mode) {
362 return NULL;
363 }
364
365 jbyteArray ninePatchChunk = NULL;
366 if (peeker.fPatch != NULL) {
367 if (willScale) {
368 scaleNinePatchChunk(peeker.fPatch, scale);
369 }
370
371 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
372 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
373 if (ninePatchChunk == NULL) {
374 return nullObjectReturn("ninePatchChunk == null");
375 }
376
377 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
378 if (array == NULL) {
379 return nullObjectReturn("primitive array == null");
380 }
381
382 peeker.fPatch->serialize(array);
383 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
384 }
385
386 jintArray layoutBounds = NULL;
387 if (peeker.fLayoutBounds != NULL) {
388 layoutBounds = env->NewIntArray(4);
389 if (layoutBounds == NULL) {
390 return nullObjectReturn("layoutBounds == null");
391 }
392
393 jint scaledBounds[4];
394 if (willScale) {
395 for (int i=0; i<4; i++) {
396 scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f);
397 }
398 } else {
399 memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds));
400 }
401 env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds);
402 if (javaBitmap != NULL) {
403 env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
404 }
405 }
406
407 if (willScale) {
408 // This is weird so let me explain: we could use the scale parameter
409 // directly, but for historical reasons this is how the corresponding
410 // Dalvik code has always behaved. We simply recreate the behavior here.
411 // The result is slightly different from simply using scale because of
412 // the 0.5f rounding bias applied when computing the target image size
413 const float sx = scaledWidth / float(decodingBitmap.width());
414 const float sy = scaledHeight / float(decodingBitmap.height());
415
416 // TODO: avoid copying when scaled size equals decodingBitmap size
417 SkBitmap::Config config = configForScaledOutput(decodingBitmap.config());
418 // FIXME: If the alphaType is kUnpremul and the image has alpha, the
419 // colors may not be correct, since Skia does not yet support drawing
420 // to/from unpremultiplied bitmaps.
421 outputBitmap->setConfig(config, scaledWidth, scaledHeight, 0,
422 decodingBitmap.alphaType());
423 if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
424 return nullObjectReturn("allocation failed for scaled bitmap");
425 }
426
427 // If outputBitmap's pixels are newly allocated by Java, there is no need
428 // to erase to 0, since the pixels were initialized to 0.
429 if (outputAllocator != &javaAllocator) {
430 outputBitmap->eraseColor(0);
431 }
432
433 SkPaint paint;
434 paint.setFilterLevel(SkPaint::kLow_FilterLevel);
435
436 SkCanvas canvas(*outputBitmap);
437 canvas.scale(sx, sy);
438 canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
439 } else {
440 outputBitmap->swap(decodingBitmap);
441 }
442
443 if (padding) {
444 if (peeker.fPatch != NULL) {
445 GraphicsJNI::set_jrect(env, padding,
446 peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
447 peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
448 } else {
449 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
450 }
451 }
452
453 SkPixelRef* pr;
454 if (isPurgeable) {
455 pr = installPixelRef(outputBitmap, stream, sampleSize, doDither);
456 } else {
457 // if we get here, we're in kDecodePixels_Mode and will therefore
458 // already have a pixelref installed.
459 pr = outputBitmap->pixelRef();
460 }
461 if (pr == NULL) {
462 return nullObjectReturn("Got null SkPixelRef");
463 }
464
465 if (!isMutable && javaBitmap == NULL) {
466 // promise we will never change our pixels (great for sharing and pictures)
467 pr->setImmutable();
468 }
469
470 // detach bitmap from its autodeleter, since we want to own it now
471 adb.detach();
472
473 if (javaBitmap != NULL) {
474 bool isPremultiplied = !requireUnpremultiplied;
475 GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied);
476 outputBitmap->notifyPixelsChanged();
477 // If a java bitmap was passed in for reuse, pass it back
478 return javaBitmap;
479 }
480
481 int bitmapCreateFlags = 0x0;
482 if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
483 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
484
485 // now create the java bitmap
486 return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
487 bitmapCreateFlags, ninePatchChunk, layoutBounds, -1);
488 }
489
490 // Need to buffer enough input to be able to rewind as much as might be read by a decoder
491 // trying to determine the stream's format. Currently the most is 64, read by
492 // SkImageDecoder_libwebp.
493 // FIXME: Get this number from SkImageDecoder
494 #define BYTES_TO_BUFFER 64
495
nativeDecodeStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage,jobject padding,jobject options)496 static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
497 jobject padding, jobject options) {
498
499 jobject bitmap = NULL;
500 SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
501
502 if (stream.get()) {
503 SkAutoTUnref<SkStreamRewindable> bufferedStream(
504 SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER));
505 SkASSERT(bufferedStream.get() != NULL);
506 // for now we don't allow purgeable with java inputstreams
507 bitmap = doDecode(env, bufferedStream, padding, options, false, false);
508 }
509 return bitmap;
510 }
511
nativeDecodeFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor,jobject padding,jobject bitmapFactoryOptions)512 static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
513 jobject padding, jobject bitmapFactoryOptions) {
514
515 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
516
517 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
518
519 struct stat fdStat;
520 if (fstat(descriptor, &fdStat) == -1) {
521 doThrowIOE(env, "broken file descriptor");
522 return nullObjectReturn("fstat return -1");
523 }
524
525 // Restore the descriptor's offset on exiting this function.
526 AutoFDSeek autoRestore(descriptor);
527
528 FILE* file = fdopen(descriptor, "r");
529 if (file == NULL) {
530 return nullObjectReturn("Could not open file");
531 }
532
533 SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,
534 SkFILEStream::kCallerRetains_Ownership));
535
536 SkAutoTUnref<SkStreamRewindable> stream;
537
538 // Retain the old behavior of allowing purgeable if both purgeable and
539 // shareable are set to true.
540 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions)
541 && optionsShareable(env, bitmapFactoryOptions);
542 if (isPurgeable) {
543 // Copy the stream, so the image can be decoded multiple times without
544 // continuing to modify the original file descriptor.
545 // Copy beginning from the current position.
546 const size_t fileSize = fileStream->getLength() - fileStream->getPosition();
547 void* buffer = sk_malloc_flags(fileSize, 0);
548 if (buffer == NULL) {
549 return nullObjectReturn("Could not make a copy for ashmem");
550 }
551
552 SkAutoTUnref<SkData> data(SkData::NewFromMalloc(buffer, fileSize));
553
554 if (fileStream->read(buffer, fileSize) != fileSize) {
555 return nullObjectReturn("Could not read the file.");
556 }
557
558 stream.reset(new SkMemoryStream(data));
559 } else {
560 // Use a buffered stream. Although an SkFILEStream can be rewound, this
561 // ensures that SkImageDecoder::Factory never rewinds beyond the
562 // current position of the file descriptor.
563 stream.reset(SkFrontBufferedStream::Create(fileStream, BYTES_TO_BUFFER));
564 }
565
566 return doDecode(env, stream, padding, bitmapFactoryOptions, isPurgeable);
567 }
568
nativeDecodeAsset(JNIEnv * env,jobject clazz,jint native_asset,jobject padding,jobject options)569 static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset,
570 jobject padding, jobject options) {
571
572 SkStreamRewindable* stream;
573 Asset* asset = reinterpret_cast<Asset*>(native_asset);
574 bool forcePurgeable = optionsPurgeable(env, options);
575 if (forcePurgeable) {
576 // if we could "ref/reopen" the asset, we may not need to copy it here
577 // and we could assume optionsShareable, since assets are always RO
578 stream = CopyAssetToStream(asset);
579 if (stream == NULL) {
580 return NULL;
581 }
582 } else {
583 // since we know we'll be done with the asset when we return, we can
584 // just use a simple wrapper
585 stream = new AssetStreamAdaptor(asset,
586 AssetStreamAdaptor::kNo_OwnAsset,
587 AssetStreamAdaptor::kNo_HasMemoryBase);
588 }
589 SkAutoUnref aur(stream);
590 return doDecode(env, stream, padding, options, forcePurgeable, forcePurgeable);
591 }
592
nativeDecodeByteArray(JNIEnv * env,jobject,jbyteArray byteArray,int offset,int length,jobject options)593 static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
594 int offset, int length, jobject options) {
595
596 /* If optionsShareable() we could decide to just wrap the java array and
597 share it, but that means adding a globalref to the java array object
598 and managing its lifetime. For now we just always copy the array's data
599 if optionsPurgeable(), unless we're just decoding bounds.
600 */
601 bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
602 AutoJavaByteArray ar(env, byteArray);
603 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
604 SkAutoUnref aur(stream);
605 return doDecode(env, stream, NULL, options, purgeable);
606 }
607
nativeRequestCancel(JNIEnv *,jobject joptions)608 static void nativeRequestCancel(JNIEnv*, jobject joptions) {
609 (void)AutoDecoderCancel::RequestCancel(joptions);
610 }
611
nativeIsSeekable(JNIEnv * env,jobject,jobject fileDescriptor)612 static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
613 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
614 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
615 }
616
617 ///////////////////////////////////////////////////////////////////////////////
618
619 static JNINativeMethod gMethods[] = {
620 { "nativeDecodeStream",
621 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
622 (void*)nativeDecodeStream
623 },
624
625 { "nativeDecodeFileDescriptor",
626 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
627 (void*)nativeDecodeFileDescriptor
628 },
629
630 { "nativeDecodeAsset",
631 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
632 (void*)nativeDecodeAsset
633 },
634
635 { "nativeDecodeByteArray",
636 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
637 (void*)nativeDecodeByteArray
638 },
639
640 { "nativeIsSeekable",
641 "(Ljava/io/FileDescriptor;)Z",
642 (void*)nativeIsSeekable
643 },
644 };
645
646 static JNINativeMethod gOptionsMethods[] = {
647 { "requestCancel", "()V", (void*)nativeRequestCancel }
648 };
649
getFieldIDCheck(JNIEnv * env,jclass clazz,const char fieldname[],const char type[])650 static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
651 const char fieldname[], const char type[]) {
652 jfieldID id = env->GetFieldID(clazz, fieldname, type);
653 SkASSERT(id);
654 return id;
655 }
656
register_android_graphics_BitmapFactory(JNIEnv * env)657 int register_android_graphics_BitmapFactory(JNIEnv* env) {
658 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
659 SkASSERT(options_class);
660 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
661 "Landroid/graphics/Bitmap;");
662 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
663 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
664 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
665 "Landroid/graphics/Bitmap$Config;");
666 gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z");
667 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
668 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
669 gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
670 gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
671 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
672 "inPreferQualityOverSpeed", "Z");
673 gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
674 gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
675 gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
676 gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
677 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
678 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
679 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
680 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
681
682 jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
683 SkASSERT(bitmap_class);
684 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
685 gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
686 int ret = AndroidRuntime::registerNativeMethods(env,
687 "android/graphics/BitmapFactory$Options",
688 gOptionsMethods,
689 SK_ARRAY_COUNT(gOptionsMethods));
690 if (ret) {
691 return ret;
692 }
693 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
694 gMethods, SK_ARRAY_COUNT(gMethods));
695 }
696