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