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