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