BitmapFactory.cpp revision caf813fe1ec10dda75cd752cb3ff80872ae7ac0b
1#define LOG_TAG "BitmapFactory"
2
3#include "BitmapFactory.h"
4#include "NinePatchPeeker.h"
5#include "SkImageDecoder.h"
6#include "SkImageRef_ashmem.h"
7#include "SkImageRef_GlobalPool.h"
8#include "SkPixelRef.h"
9#include "SkStream.h"
10#include "SkTemplates.h"
11#include "SkUtils.h"
12#include "CreateJavaOutputStreamAdaptor.h"
13#include "AutoDecodeCancel.h"
14#include "Utils.h"
15#include "JNIHelp.h"
16
17#include <android_runtime/AndroidRuntime.h>
18#include <androidfw/Asset.h>
19#include <androidfw/ResourceTypes.h>
20#include <netinet/in.h>
21#include <sys/mman.h>
22#include <sys/stat.h>
23
24jfieldID gOptions_justBoundsFieldID;
25jfieldID gOptions_sampleSizeFieldID;
26jfieldID gOptions_configFieldID;
27jfieldID gOptions_mutableFieldID;
28jfieldID gOptions_ditherFieldID;
29jfieldID gOptions_purgeableFieldID;
30jfieldID gOptions_shareableFieldID;
31jfieldID gOptions_preferQualityOverSpeedFieldID;
32jfieldID gOptions_widthFieldID;
33jfieldID gOptions_heightFieldID;
34jfieldID gOptions_mimeFieldID;
35jfieldID gOptions_mCancelID;
36jfieldID gOptions_bitmapFieldID;
37jfieldID gBitmap_nativeBitmapFieldID;
38
39#if 0
40    #define TRACE_BITMAP(code)  code
41#else
42    #define TRACE_BITMAP(code)
43#endif
44
45using namespace android;
46
47static inline int32_t validOrNeg1(bool isValid, int32_t value) {
48//    return isValid ? value : -1;
49    SkASSERT((int)isValid == 0 || (int)isValid == 1);
50    return ((int32_t)isValid - 1) | value;
51}
52
53jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
54    static const struct {
55        SkImageDecoder::Format fFormat;
56        const char*            fMimeType;
57    } gMimeTypes[] = {
58        { SkImageDecoder::kBMP_Format,  "image/bmp" },
59        { SkImageDecoder::kGIF_Format,  "image/gif" },
60        { SkImageDecoder::kICO_Format,  "image/x-ico" },
61        { SkImageDecoder::kJPEG_Format, "image/jpeg" },
62        { SkImageDecoder::kPNG_Format,  "image/png" },
63        { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
64    };
65
66    const char* cstr = NULL;
67    for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
68        if (gMimeTypes[i].fFormat == format) {
69            cstr = gMimeTypes[i].fMimeType;
70            break;
71        }
72    }
73
74    jstring jstr = 0;
75    if (NULL != cstr) {
76        jstr = env->NewStringUTF(cstr);
77    }
78    return jstr;
79}
80
81static bool optionsPurgeable(JNIEnv* env, jobject options) {
82    return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
83}
84
85static bool optionsShareable(JNIEnv* env, jobject options) {
86    return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
87}
88
89static bool optionsJustBounds(JNIEnv* env, jobject options) {
90    return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
91}
92
93static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
94                                   int sampleSize, bool ditherImage) {
95    SkImageRef* pr;
96    // only use ashmem for large images, since mmaps come at a price
97    if (bitmap->getSize() >= 32 * 1024) {
98        pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
99    } else {
100        pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
101    }
102    pr->setDitherImage(ditherImage);
103    bitmap->setPixelRef(pr)->unref();
104    pr->isOpaque(bitmap);
105    return pr;
106}
107
108// since we "may" create a purgeable imageref, we require the stream be ref'able
109// i.e. dynamically allocated, since its lifetime may exceed the current stack
110// frame.
111static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
112                        jobject options, bool allowPurgeable,
113                        bool forcePurgeable = false) {
114    int sampleSize = 1;
115    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
116    SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
117    bool doDither = true;
118    bool isMutable = false;
119    bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
120    bool preferQualityOverSpeed = false;
121    jobject javaBitmap = NULL;
122
123    if (NULL != options) {
124        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
125        if (optionsJustBounds(env, options)) {
126            mode = SkImageDecoder::kDecodeBounds_Mode;
127        }
128        // initialize these, in case we fail later on
129        env->SetIntField(options, gOptions_widthFieldID, -1);
130        env->SetIntField(options, gOptions_heightFieldID, -1);
131        env->SetObjectField(options, gOptions_mimeFieldID, 0);
132
133        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
134        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
135        isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
136        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
137        preferQualityOverSpeed = env->GetBooleanField(options,
138                gOptions_preferQualityOverSpeedFieldID);
139        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
140    }
141
142    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
143    if (NULL == decoder) {
144        return nullObjectReturn("SkImageDecoder::Factory returned null");
145    }
146
147    decoder->setSampleSize(sampleSize);
148    decoder->setDitherImage(doDither);
149    decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
150
151    NinePatchPeeker     peeker(decoder);
152    JavaPixelAllocator  javaAllocator(env);
153    SkBitmap*           bitmap;
154    if (javaBitmap == NULL) {
155        bitmap = new SkBitmap;
156    } else {
157        if (sampleSize != 1) {
158            return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1");
159        }
160        bitmap = (SkBitmap *) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
161        // config of supplied bitmap overrules config set in options
162        prefConfig = bitmap->getConfig();
163    }
164    Res_png_9patch      dummy9Patch;
165
166    SkAutoTDelete<SkImageDecoder>   add(decoder);
167    SkAutoTDelete<SkBitmap>         adb(bitmap, (javaBitmap == NULL));
168
169    decoder->setPeeker(&peeker);
170    if (!isPurgeable) {
171        decoder->setAllocator(&javaAllocator);
172    }
173
174    AutoDecoderCancel   adc(options, decoder);
175
176    // To fix the race condition in case "requestCancelDecode"
177    // happens earlier than AutoDecoderCancel object is added
178    // to the gAutoDecoderCancelMutex linked list.
179    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
180        return nullObjectReturn("gOptions_mCancelID");
181    }
182
183    SkImageDecoder::Mode decodeMode = mode;
184    if (isPurgeable) {
185        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
186    }
187    if (!decoder->decode(stream, bitmap, prefConfig, decodeMode, javaBitmap != NULL)) {
188        return nullObjectReturn("decoder->decode returned false");
189    }
190
191    // update options (if any)
192    if (NULL != options) {
193        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
194        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
195        // TODO: set the mimeType field with the data from the codec.
196        // but how to reuse a set of strings, rather than allocating new one
197        // each time?
198        env->SetObjectField(options, gOptions_mimeFieldID,
199                            getMimeTypeString(env, decoder->getFormat()));
200    }
201
202    // if we're in justBounds mode, return now (skip the java bitmap)
203    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
204        return NULL;
205    }
206
207    jbyteArray ninePatchChunk = NULL;
208    if (peeker.fPatchIsValid) {
209        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
210        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
211        if (NULL == ninePatchChunk) {
212            return nullObjectReturn("ninePatchChunk == null");
213        }
214        jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
215                                                              NULL);
216        if (NULL == array) {
217            return nullObjectReturn("primitive array == null");
218        }
219        peeker.fPatch->serialize(array);
220        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
221    }
222
223    // detach bitmap from its autodeleter, since we want to own it now
224    adb.detach();
225
226    if (padding) {
227        if (peeker.fPatchIsValid) {
228            GraphicsJNI::set_jrect(env, padding,
229                                   peeker.fPatch->paddingLeft,
230                                   peeker.fPatch->paddingTop,
231                                   peeker.fPatch->paddingRight,
232                                   peeker.fPatch->paddingBottom);
233        } else {
234            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
235        }
236    }
237
238    SkPixelRef* pr;
239    if (isPurgeable) {
240        pr = installPixelRef(bitmap, stream, sampleSize, doDither);
241    } else {
242        // if we get here, we're in kDecodePixels_Mode and will therefore
243        // already have a pixelref installed.
244        pr = bitmap->pixelRef();
245    }
246
247    if (!isMutable) {
248        // promise we will never change our pixels (great for sharing and pictures)
249        pr->setImmutable();
250    }
251
252    if (javaBitmap != NULL) {
253        // If a java bitmap was passed in for reuse, pass it back
254        return javaBitmap;
255    }
256    // now create the java bitmap
257    return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
258            isMutable, ninePatchChunk);
259}
260
261static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
262                                  jobject is,       // InputStream
263                                  jbyteArray storage,   // byte[]
264                                  jobject padding,
265                                  jobject options) {  // BitmapFactory$Options
266    jobject bitmap = NULL;
267    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
268
269    if (stream) {
270        // for now we don't allow purgeable with java inputstreams
271        bitmap = doDecode(env, stream, padding, options, false);
272        stream->unref();
273    }
274    return bitmap;
275}
276
277static ssize_t getFDSize(int fd) {
278    off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
279    if (curr < 0) {
280        return 0;
281    }
282    size_t size = ::lseek(fd, 0, SEEK_END);
283    ::lseek64(fd, curr, SEEK_SET);
284    return size;
285}
286
287static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
288                                          jobject fileDescriptor,
289                                          jobject padding,
290                                          jobject bitmapFactoryOptions) {
291    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
292
293    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
294
295    bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
296    bool isShareable = optionsShareable(env, bitmapFactoryOptions);
297    bool weOwnTheFD = false;
298    if (isPurgeable && isShareable) {
299        int newFD = ::dup(descriptor);
300        if (-1 != newFD) {
301            weOwnTheFD = true;
302            descriptor = newFD;
303        }
304    }
305
306    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
307    SkAutoUnref aur(stream);
308    if (!stream->isValid()) {
309        return NULL;
310    }
311
312    /* Restore our offset when we leave, so we can be called more than once
313       with the same descriptor. This is only required if we didn't dup the
314       file descriptor, but it is OK to do it all the time.
315    */
316    AutoFDSeek as(descriptor);
317
318    /* Allow purgeable iff we own the FD, i.e., in the puregeable and
319       shareable case.
320    */
321    return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
322}
323
324/*  make a deep copy of the asset, and return it as a stream, or NULL if there
325    was an error.
326 */
327static SkStream* copyAssetToStream(Asset* asset) {
328    // if we could "ref/reopen" the asset, we may not need to copy it here
329    off64_t size = asset->seek(0, SEEK_SET);
330    if ((off64_t)-1 == size) {
331        SkDebugf("---- copyAsset: asset rewind failed\n");
332        return NULL;
333    }
334
335    size = asset->getLength();
336    if (size <= 0) {
337        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
338        return NULL;
339    }
340
341    SkStream* stream = new SkMemoryStream(size);
342    void* data = const_cast<void*>(stream->getMemoryBase());
343    off64_t len = asset->read(data, size);
344    if (len != size) {
345        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
346        delete stream;
347        stream = NULL;
348    }
349    return stream;
350}
351
352static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
353                                 jint native_asset,    // Asset
354                                 jobject padding,       // Rect
355                                 jobject options) { // BitmapFactory$Options
356    SkStream* stream;
357    Asset* asset = reinterpret_cast<Asset*>(native_asset);
358    bool forcePurgeable = optionsPurgeable(env, options);
359    if (forcePurgeable) {
360        // if we could "ref/reopen" the asset, we may not need to copy it here
361        // and we could assume optionsShareable, since assets are always RO
362        stream = copyAssetToStream(asset);
363        if (NULL == stream) {
364            return NULL;
365        }
366    } else {
367        // since we know we'll be done with the asset when we return, we can
368        // just use a simple wrapper
369        stream = new AssetStreamAdaptor(asset);
370    }
371    SkAutoUnref aur(stream);
372    return doDecode(env, stream, padding, options, true, forcePurgeable);
373}
374
375static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
376                                     int offset, int length, jobject options) {
377    /*  If optionsShareable() we could decide to just wrap the java array and
378        share it, but that means adding a globalref to the java array object
379        and managing its lifetime. For now we just always copy the array's data
380        if optionsPurgeable(), unless we're just decoding bounds.
381     */
382    bool purgeable = optionsPurgeable(env, options)
383            && !optionsJustBounds(env, options);
384    AutoJavaByteArray ar(env, byteArray);
385    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
386    SkAutoUnref aur(stream);
387    return doDecode(env, stream, NULL, options, purgeable);
388}
389
390static void nativeRequestCancel(JNIEnv*, jobject joptions) {
391    (void)AutoDecoderCancel::RequestCancel(joptions);
392}
393
394static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
395        jobject padding) {
396
397    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
398    if (array != NULL) {
399        size_t chunkSize = env->GetArrayLength(chunkObject);
400        void* storage = alloca(chunkSize);
401        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
402        memcpy(chunk, array, chunkSize);
403        android::Res_png_9patch::deserialize(chunk);
404
405        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
406        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
407        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
408        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
409
410        for (int i = 0; i < chunk->numXDivs; i++) {
411            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
412            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
413                chunk->xDivs[i]++;
414            }
415        }
416
417        for (int i = 0; i < chunk->numYDivs; i++) {
418            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
419            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
420                chunk->yDivs[i]++;
421            }
422        }
423
424        memcpy(array, chunk, chunkSize);
425
426        if (padding) {
427            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
428                    chunk->paddingRight, chunk->paddingBottom);
429        }
430
431        env->ReleaseByteArrayElements(chunkObject, array, 0);
432    }
433    return chunkObject;
434}
435
436static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
437    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
438    return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
439}
440
441///////////////////////////////////////////////////////////////////////////////
442
443static JNINativeMethod gMethods[] = {
444    {   "nativeDecodeStream",
445        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
446        (void*)nativeDecodeStream
447    },
448
449    {   "nativeDecodeFileDescriptor",
450        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
451        (void*)nativeDecodeFileDescriptor
452    },
453
454    {   "nativeDecodeAsset",
455        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
456        (void*)nativeDecodeAsset
457    },
458
459    {   "nativeDecodeByteArray",
460        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
461        (void*)nativeDecodeByteArray
462    },
463
464    {   "nativeScaleNinePatch",
465        "([BFLandroid/graphics/Rect;)[B",
466        (void*)nativeScaleNinePatch
467    },
468
469    {   "nativeIsSeekable",
470        "(Ljava/io/FileDescriptor;)Z",
471        (void*)nativeIsSeekable
472    },
473};
474
475static JNINativeMethod gOptionsMethods[] = {
476    {   "requestCancel", "()V", (void*)nativeRequestCancel }
477};
478
479static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
480                                const char fieldname[], const char type[]) {
481    jfieldID id = env->GetFieldID(clazz, fieldname, type);
482    SkASSERT(id);
483    return id;
484}
485
486int register_android_graphics_BitmapFactory(JNIEnv* env) {
487    jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
488    SkASSERT(options_class);
489    gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
490        "Landroid/graphics/Bitmap;");
491    gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
492    gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
493    gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
494            "Landroid/graphics/Bitmap$Config;");
495    gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
496    gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
497    gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
498    gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
499    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
500            "inPreferQualityOverSpeed", "Z");
501    gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
502    gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
503    gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
504    gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
505
506    jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
507    SkASSERT(bitmap_class);
508    gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
509
510    int ret = AndroidRuntime::registerNativeMethods(env,
511                                    "android/graphics/BitmapFactory$Options",
512                                    gOptionsMethods,
513                                    SK_ARRAY_COUNT(gOptionsMethods));
514    if (ret) {
515        return ret;
516    }
517    return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
518                                         gMethods, SK_ARRAY_COUNT(gMethods));
519}
520