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