BitmapFactory.cpp revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1#define LOG_TAG "BitmapFactory"
2
3#include "SkImageDecoder.h"
4#include "SkPixelRef.h"
5#include "SkStream.h"
6#include "GraphicsJNI.h"
7#include "SkTemplates.h"
8#include "SkUtils.h"
9#include "CreateJavaOutputStreamAdaptor.h"
10
11#include <android_runtime/AndroidRuntime.h>
12#include <utils/Asset.h>
13#include <utils/ResourceTypes.h>
14#include <netinet/in.h>
15#include <sys/mman.h>
16
17static jclass gOptions_class;
18static jfieldID gOptions_justBoundsFieldID;
19static jfieldID gOptions_sampleSizeFieldID;
20static jfieldID gOptions_configFieldID;
21static jfieldID gOptions_ditherFieldID;
22static jfieldID gOptions_widthFieldID;
23static jfieldID gOptions_heightFieldID;
24static jfieldID gOptions_mimeFieldID;
25
26static jclass gFileDescriptor_class;
27static jfieldID gFileDescriptor_descriptor;
28
29#if 0
30    #define TRACE_BITMAP(code)  code
31#else
32    #define TRACE_BITMAP(code)
33#endif
34
35//#define MIN_SIZE_TO_USE_MMAP    (4*1024)
36
37///////////////////////////////////////////////////////////////////////////////
38
39class AutoDecoderCancel {
40public:
41    AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
42    ~AutoDecoderCancel();
43
44    static bool RequestCancel(jobject options);
45
46private:
47    AutoDecoderCancel*  fNext;
48    AutoDecoderCancel*  fPrev;
49    jobject             fJOptions;  // java options object
50    SkImageDecoder*     fDecoder;
51
52#ifdef SK_DEBUG
53    static void Validate();
54#else
55    static void Validate() {}
56#endif
57};
58
59static SkMutex  gAutoDecoderCancelMutex;
60static AutoDecoderCancel* gAutoDecoderCancel;
61#ifdef SK_DEBUG
62    static int gAutoDecoderCancelCount;
63#endif
64
65AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
66                                       SkImageDecoder* decoder) {
67    fJOptions = joptions;
68    fDecoder = decoder;
69
70    if (NULL != joptions) {
71        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
72
73        // Add us as the head of the list
74        fPrev = NULL;
75        fNext = gAutoDecoderCancel;
76        if (gAutoDecoderCancel) {
77            gAutoDecoderCancel->fPrev = this;
78        }
79        gAutoDecoderCancel = this;
80
81        SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
82        Validate();
83    }
84}
85
86AutoDecoderCancel::~AutoDecoderCancel() {
87    if (NULL != fJOptions) {
88        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
89
90        // take us out of the dllist
91        AutoDecoderCancel* prev = fPrev;
92        AutoDecoderCancel* next = fNext;
93
94        if (prev) {
95            SkASSERT(prev->fNext == this);
96            prev->fNext = next;
97        } else {
98            SkASSERT(gAutoDecoderCancel == this);
99            gAutoDecoderCancel = next;
100        }
101        if (next) {
102            SkASSERT(next->fPrev == this);
103            next->fPrev = prev;
104        }
105
106        SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
107        Validate();
108    }
109}
110
111bool AutoDecoderCancel::RequestCancel(jobject joptions) {
112    SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
113
114    Validate();
115
116    AutoDecoderCancel* pair = gAutoDecoderCancel;
117    while (pair != NULL) {
118        if (pair->fJOptions == joptions) {
119            pair->fDecoder->cancelDecode();
120            return true;
121        }
122        pair = pair->fNext;
123    }
124    return false;
125}
126
127#ifdef SK_DEBUG
128// can only call this inside a lock on gAutoDecoderCancelMutex
129void AutoDecoderCancel::Validate() {
130    const int gCount = gAutoDecoderCancelCount;
131
132    if (gCount == 0) {
133        SkASSERT(gAutoDecoderCancel == NULL);
134    } else {
135        SkASSERT(gCount > 0);
136
137        AutoDecoderCancel* curr = gAutoDecoderCancel;
138        SkASSERT(curr);
139        SkASSERT(curr->fPrev == NULL);
140
141        int count = 0;
142        while (curr) {
143            count += 1;
144            SkASSERT(count <= gCount);
145            if (curr->fPrev) {
146                SkASSERT(curr->fPrev->fNext == curr);
147            }
148            if (curr->fNext) {
149                SkASSERT(curr->fNext->fPrev == curr);
150            }
151            curr = curr->fNext;
152        }
153        SkASSERT(count == gCount);
154    }
155}
156#endif
157
158///////////////////////////////////////////////////////////////////////////////
159
160using namespace android;
161
162class NinePatchPeeker : public SkImageDecoder::Peeker {
163public:
164    NinePatchPeeker() {
165        fPatchIsValid = false;
166    }
167
168    ~NinePatchPeeker() {
169        if (fPatchIsValid) {
170            free(fPatch);
171        }
172    }
173
174    bool    fPatchIsValid;
175    Res_png_9patch*  fPatch;
176
177    virtual bool peek(const char tag[], const void* data, size_t length) {
178        if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
179            Res_png_9patch* patch = (Res_png_9patch*) data;
180            size_t patchSize = patch->serializedSize();
181            assert(length == patchSize);
182            // You have to copy the data because it is owned by the png reader
183            Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
184            memcpy(patchNew, patch, patchSize);
185            // this relies on deserialization being done in place
186            Res_png_9patch::deserialize(patchNew);
187            patchNew->fileToDevice();
188            if (fPatchIsValid) {
189                free(fPatch);
190            }
191            fPatch = patchNew;
192            //printf("9patch: (%d,%d)-(%d,%d)\n",
193            //       fPatch.sizeLeft, fPatch.sizeTop,
194            //       fPatch.sizeRight, fPatch.sizeBottom);
195            fPatchIsValid = true;
196        } else {
197            fPatch = NULL;
198        }
199        return true;    // keep on decoding
200    }
201};
202
203class AssetStreamAdaptor : public SkStream {
204public:
205    AssetStreamAdaptor(Asset* a) : fAsset(a) {}
206
207	virtual bool rewind() {
208        off_t pos = fAsset->seek(0, SEEK_SET);
209        return pos != (off_t)-1;
210    }
211
212	virtual size_t read(void* buffer, size_t size) {
213        ssize_t amount;
214
215        if (NULL == buffer) {
216            if (0 == size) {  // caller is asking us for our total length
217                return fAsset->getLength();
218            }
219            // asset->seek returns new total offset
220            // we want to return amount that was skipped
221
222            off_t oldOffset = fAsset->seek(0, SEEK_CUR);
223            if (-1 == oldOffset) {
224                return 0;
225            }
226            off_t newOffset = fAsset->seek(size, SEEK_CUR);
227            if (-1 == newOffset) {
228                return 0;
229            }
230            amount = newOffset - oldOffset;
231        } else {
232            amount = fAsset->read(buffer, size);
233        }
234
235        if (amount < 0) {
236            amount = 0;
237        }
238        return amount;
239    }
240
241private:
242    Asset*  fAsset;
243};
244
245///////////////////////////////////////////////////////////////////////////////
246
247static inline int32_t validOrNeg1(bool isValid, int32_t value) {
248//    return isValid ? value : -1;
249    SkASSERT((int)isValid == 0 || (int)isValid == 1);
250    return ((int32_t)isValid - 1) | value;
251}
252
253static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
254    static const struct {
255        SkImageDecoder::Format fFormat;
256        const char*            fMimeType;
257    } gMimeTypes[] = {
258        { SkImageDecoder::kBMP_Format,  "image/bmp" },
259        { SkImageDecoder::kGIF_Format,  "image/gif" },
260        { SkImageDecoder::kICO_Format,  "image/x-ico" },
261        { SkImageDecoder::kJPEG_Format, "image/jpeg" },
262        { SkImageDecoder::kPNG_Format,  "image/png" },
263        { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
264    };
265
266    const char* cstr = NULL;
267    for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
268        if (gMimeTypes[i].fFormat == format) {
269            cstr = gMimeTypes[i].fMimeType;
270            break;
271        }
272    }
273
274    jstring jstr = 0;
275    if (NULL != cstr) {
276        jstr = env->NewStringUTF(cstr);
277    }
278    return jstr;
279}
280
281static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
282                        jobject options) {
283
284    int sampleSize = 1;
285    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
286    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
287    bool doDither = true;
288
289    if (NULL != options) {
290        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
291        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
292            mode = SkImageDecoder::kDecodeBounds_Mode;
293        }
294        // initialize these, in case we fail later on
295        env->SetIntField(options, gOptions_widthFieldID, -1);
296        env->SetIntField(options, gOptions_heightFieldID, -1);
297        env->SetObjectField(options, gOptions_mimeFieldID, 0);
298
299        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
300        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
301        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
302    }
303
304    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
305    if (NULL == decoder) {
306        return NULL;
307    }
308
309    decoder->setSampleSize(sampleSize);
310    decoder->setDitherImage(doDither);
311
312    NinePatchPeeker     peeker;
313    JavaPixelAllocator  allocator(env);
314    SkBitmap*           bitmap = new SkBitmap;
315    Res_png_9patch      dummy9Patch;
316
317    SkAutoTDelete<SkImageDecoder>   add(decoder);
318    SkAutoTDelete<SkBitmap>         adb(bitmap);
319
320    decoder->setPeeker(&peeker);
321    decoder->setAllocator(&allocator);
322
323    AutoDecoderCancel   adc(options, decoder);
324
325    if (!decoder->decode(stream, bitmap, prefConfig, mode)) {
326        return NULL;
327    }
328
329    // update options (if any)
330    if (NULL != options) {
331        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
332        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
333        // TODO: set the mimeType field with the data from the codec.
334        // but how to reuse a set of strings, rather than allocating new one
335        // each time?
336        env->SetObjectField(options, gOptions_mimeFieldID,
337                            getMimeTypeString(env, decoder->getFormat()));
338    }
339
340    // if we're in justBounds mode, return now (skip the java bitmap)
341    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
342        return NULL;
343    }
344
345    jbyteArray ninePatchChunk = NULL;
346    if (peeker.fPatchIsValid) {
347        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
348        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
349        if (NULL == ninePatchChunk) {
350            return NULL;
351        }
352        jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
353                                                              NULL);
354        if (NULL == array) {
355            return NULL;
356        }
357        peeker.fPatch->serialize(array);
358        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
359    }
360
361    // detach bitmap from its autotdeleter, since we want to own it now
362    adb.detach();
363
364    if (padding) {
365        if (peeker.fPatchIsValid) {
366            GraphicsJNI::set_jrect(env, padding,
367                                   peeker.fPatch->paddingLeft,
368                                   peeker.fPatch->paddingTop,
369                                   peeker.fPatch->paddingRight,
370                                   peeker.fPatch->paddingBottom);
371        } else {
372            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
373        }
374    }
375
376    // promise we will never change our pixels (great for sharing and pictures)
377    SkPixelRef* ref = bitmap->pixelRef();
378    SkASSERT(ref);
379    ref->setImmutable();
380
381    return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
382}
383
384static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
385                                  jobject is,       // InputStream
386                                  jbyteArray storage,   // byte[]
387                                  jobject padding,
388                                  jobject options) {  // BitmapFactory$Options
389    jobject bitmap = NULL;
390    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
391
392    if (stream) {
393        bitmap = doDecode(env, stream, padding, options);
394        stream->unref();
395    }
396    return bitmap;
397}
398
399static ssize_t getFDSize(int fd) {
400    off_t curr = ::lseek(fd, 0, SEEK_CUR);
401    if (curr < 0) {
402        return 0;
403    }
404    size_t size = ::lseek(fd, 0, SEEK_END);
405    ::lseek(fd, curr, SEEK_SET);
406    return size;
407}
408
409/** Restore the file descriptor's offset in our destructor
410 */
411class AutoFDSeek {
412public:
413    AutoFDSeek(int fd) : fFD(fd) {
414        fCurr = ::lseek(fd, 0, SEEK_CUR);
415    }
416    ~AutoFDSeek() {
417        if (fCurr >= 0) {
418            ::lseek(fFD, fCurr, SEEK_SET);
419        }
420    }
421private:
422    int     fFD;
423    off_t   fCurr;
424};
425
426static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
427                                          jobject fileDescriptor,
428                                          jobject padding,
429                                          jobject bitmapFactoryOptions) {
430    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
431
432    jint descriptor = env->GetIntField(fileDescriptor,
433                                       gFileDescriptor_descriptor);
434
435#ifdef MIN_SIZE_TO_USE_MMAP
436    // First try to use mmap
437    size_t size = getFDSize(descriptor);
438    if (size >= MIN_SIZE_TO_USE_MMAP) {
439        void* addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, 0);
440//        SkDebugf("-------- mmap returned %p %d\n", addr, size);
441        if (MAP_FAILED != addr) {
442            SkMemoryStream strm(addr, size);
443            jobject obj = doDecode(env, &strm, padding, bitmapFactoryOptions);
444            munmap(addr, size);
445            return obj;
446        }
447    }
448#endif
449
450    // we pass false for closeWhenDone, since the caller owns the descriptor
451    SkFDStream file(descriptor, false);
452    if (!file.isValid()) {
453        return NULL;
454    }
455
456    /* Restore our offset when we leave, so the caller doesn't have to.
457       This is a real feature, so we can be called more than once with the
458       same descriptor.
459    */
460    AutoFDSeek as(descriptor);
461
462    return doDecode(env, &file, padding, bitmapFactoryOptions);
463}
464
465static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
466                                 jint native_asset,    // Asset
467                                 jobject padding,       // Rect
468                                 jobject options) { // BitmapFactory$Options
469    AssetStreamAdaptor  mystream((Asset*)native_asset);
470
471    return doDecode(env, &mystream, padding, options);
472}
473
474static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
475                                     int offset, int length, jobject options) {
476    AutoJavaByteArray   ar(env, byteArray);
477    SkMemoryStream  stream(ar.ptr() + offset, length);
478
479    return doDecode(env, &stream, NULL, options);
480}
481
482static void nativeRequestCancel(JNIEnv*, jobject joptions) {
483    (void)AutoDecoderCancel::RequestCancel(joptions);
484}
485
486static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
487        jobject padding) {
488
489    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
490    if (array != NULL) {
491        size_t chunkSize = env->GetArrayLength(chunkObject);
492        void* storage = alloca(chunkSize);
493        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
494        memcpy(chunk, array, chunkSize);
495        android::Res_png_9patch::deserialize(chunk);
496
497        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
498        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
499        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
500        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
501
502        for (int i = 0; i < chunk->numXDivs; i++) {
503            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
504            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
505                chunk->xDivs[i]++;
506            }
507        }
508
509        for (int i = 0; i < chunk->numYDivs; i++) {
510            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
511            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
512                chunk->yDivs[i]++;
513            }
514        }
515
516        memcpy(array, chunk, chunkSize);
517
518        if (padding) {
519            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
520                    chunk->paddingRight, chunk->paddingBottom);
521        }
522
523        env->ReleaseByteArrayElements(chunkObject, array, 0);
524    }
525    return chunkObject;
526}
527
528///////////////////////////////////////////////////////////////////////////////
529
530static JNINativeMethod gMethods[] = {
531    {   "nativeDecodeStream",
532        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
533        (void*)nativeDecodeStream
534    },
535
536    {   "nativeDecodeFileDescriptor",
537        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
538        (void*)nativeDecodeFileDescriptor
539    },
540
541    {   "nativeDecodeAsset",
542        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
543        (void*)nativeDecodeAsset
544    },
545
546    {   "nativeDecodeByteArray",
547        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
548        (void*)nativeDecodeByteArray
549    },
550
551    {   "nativeScaleNinePatch",
552        "([BFLandroid/graphics/Rect;)[B",
553        (void*)nativeScaleNinePatch
554    }
555
556};
557
558static JNINativeMethod gOptionsMethods[] = {
559    {   "requestCancel", "()V", (void*)nativeRequestCancel }
560};
561
562static jclass make_globalref(JNIEnv* env, const char classname[]) {
563    jclass c = env->FindClass(classname);
564    SkASSERT(c);
565    return (jclass)env->NewGlobalRef(c);
566}
567
568static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
569                                const char fieldname[], const char type[]) {
570    jfieldID id = env->GetFieldID(clazz, fieldname, type);
571    SkASSERT(id);
572    return id;
573}
574
575#define kClassPathName  "android/graphics/BitmapFactory"
576
577#define RETURN_ERR_IF_NULL(value) \
578    do { if (!(value)) { assert(0); return -1; } } while (false)
579
580int register_android_graphics_BitmapFactory(JNIEnv* env);
581int register_android_graphics_BitmapFactory(JNIEnv* env) {
582    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
583    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
584    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
585    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
586            "Landroid/graphics/Bitmap$Config;");
587    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
588    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
589    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
590    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
591
592    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
593    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
594
595    int ret = AndroidRuntime::registerNativeMethods(env,
596                                    "android/graphics/BitmapFactory$Options",
597                                    gOptionsMethods,
598                                    SK_ARRAY_COUNT(gOptionsMethods));
599    if (ret) {
600        return ret;
601    }
602    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
603                                         gMethods, SK_ARRAY_COUNT(gMethods));
604}
605