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