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