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