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