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 optionsReportSizeToVM(JNIEnv* env, jobject options) {
151    return NULL == options ||
152            !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
153}
154
155
156static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
157                                   int sampleSize, bool ditherImage) {
158    SkImageRef* pr;
159    // only use ashmem for large images, since mmaps come at a price
160    if (bitmap->getSize() >= 32 * 1024) {
161        pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
162    } else {
163        pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
164    }
165    pr->setDitherImage(ditherImage);
166    bitmap->setPixelRef(pr)->unref();
167    pr->isOpaque(bitmap);
168    return pr;
169}
170
171// since we "may" create a purgeable imageref, we require the stream be ref'able
172// i.e. dynamically allocated, since its lifetime may exceed the current stack
173// frame.
174static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
175                        jobject options, bool allowPurgeable,
176                        bool forcePurgeable = false) {
177    int sampleSize = 1;
178    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
179    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
180    bool doDither = true;
181    bool isPurgeable = forcePurgeable ||
182                        (allowPurgeable && optionsPurgeable(env, options));
183    bool reportSizeToVM = optionsReportSizeToVM(env, options);
184    bool preferQualityOverSpeed = false;
185
186    if (NULL != options) {
187        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
188        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
189            mode = SkImageDecoder::kDecodeBounds_Mode;
190        }
191        // initialize these, in case we fail later on
192        env->SetIntField(options, gOptions_widthFieldID, -1);
193        env->SetIntField(options, gOptions_heightFieldID, -1);
194        env->SetObjectField(options, gOptions_mimeFieldID, 0);
195
196        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
197        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
198        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
199        preferQualityOverSpeed = env->GetBooleanField(options,
200                gOptions_preferQualityOverSpeedFieldID);
201    }
202
203    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
204    if (NULL == decoder) {
205        return nullObjectReturn("SkImageDecoder::Factory returned null");
206    }
207
208    decoder->setSampleSize(sampleSize);
209    decoder->setDitherImage(doDither);
210    decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
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().
424     */
425    AutoJavaByteArray ar(env, byteArray);
426    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
427                                          optionsPurgeable(env, options));
428    SkAutoUnref aur(stream);
429    return doDecode(env, stream, NULL, options, true);
430}
431
432static void nativeRequestCancel(JNIEnv*, jobject joptions) {
433    (void)AutoDecoderCancel::RequestCancel(joptions);
434}
435
436static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
437        jobject padding) {
438
439    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
440    if (array != NULL) {
441        size_t chunkSize = env->GetArrayLength(chunkObject);
442        void* storage = alloca(chunkSize);
443        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
444        memcpy(chunk, array, chunkSize);
445        android::Res_png_9patch::deserialize(chunk);
446
447        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
448        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
449        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
450        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
451
452        for (int i = 0; i < chunk->numXDivs; i++) {
453            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
454            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
455                chunk->xDivs[i]++;
456            }
457        }
458
459        for (int i = 0; i < chunk->numYDivs; i++) {
460            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
461            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
462                chunk->yDivs[i]++;
463            }
464        }
465
466        memcpy(array, chunk, chunkSize);
467
468        if (padding) {
469            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
470                    chunk->paddingRight, chunk->paddingBottom);
471        }
472
473        env->ReleaseByteArrayElements(chunkObject, array, 0);
474    }
475    return chunkObject;
476}
477
478static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
479    SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
480
481    // these are the only default configs that make sense for codecs right now
482    static const SkBitmap::Config gValidDefConfig[] = {
483        SkBitmap::kRGB_565_Config,
484        SkBitmap::kARGB_8888_Config,
485    };
486
487    for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
488        if (config == gValidDefConfig[i]) {
489            SkImageDecoder::SetDeviceConfig(config);
490            break;
491        }
492    }
493}
494
495///////////////////////////////////////////////////////////////////////////////
496
497static JNINativeMethod gMethods[] = {
498    {   "nativeDecodeStream",
499        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
500        (void*)nativeDecodeStream
501    },
502
503    {   "nativeDecodeFileDescriptor",
504        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
505        (void*)nativeDecodeFileDescriptor
506    },
507
508    {   "nativeDecodeAsset",
509        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
510        (void*)nativeDecodeAsset
511    },
512
513    {   "nativeDecodeByteArray",
514        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
515        (void*)nativeDecodeByteArray
516    },
517
518    {   "nativeScaleNinePatch",
519        "([BFLandroid/graphics/Rect;)[B",
520        (void*)nativeScaleNinePatch
521    },
522
523    {   "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
524};
525
526static JNINativeMethod gOptionsMethods[] = {
527    {   "requestCancel", "()V", (void*)nativeRequestCancel }
528};
529
530static jclass make_globalref(JNIEnv* env, const char classname[]) {
531    jclass c = env->FindClass(classname);
532    SkASSERT(c);
533    return (jclass)env->NewGlobalRef(c);
534}
535
536static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
537                                const char fieldname[], const char type[]) {
538    jfieldID id = env->GetFieldID(clazz, fieldname, type);
539    SkASSERT(id);
540    return id;
541}
542
543#define kClassPathName  "android/graphics/BitmapFactory"
544
545#define RETURN_ERR_IF_NULL(value) \
546    do { if (!(value)) { assert(0); return -1; } } while (false)
547
548int register_android_graphics_BitmapFactory(JNIEnv* env);
549int register_android_graphics_BitmapFactory(JNIEnv* env) {
550    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
551    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
552    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
553    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
554            "Landroid/graphics/Bitmap$Config;");
555    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
556    gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
557    gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
558    gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
559    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class,
560            "inPreferQualityOverSpeed", "Z");
561    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
562    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
563    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
564    gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
565
566    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
567    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
568
569    int ret = AndroidRuntime::registerNativeMethods(env,
570                                    "android/graphics/BitmapFactory$Options",
571                                    gOptionsMethods,
572                                    SK_ARRAY_COUNT(gOptionsMethods));
573    if (ret) {
574        return ret;
575    }
576    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
577                                         gMethods, SK_ARRAY_COUNT(gMethods));
578}
579