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