BitmapFactory.cpp revision 4f0a8df9fe70b44e82eca40263b11e9331f38939
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
14#include <android_runtime/AndroidRuntime.h>
15#include <utils/Asset.h>
16#include <utils/ResourceTypes.h>
17#include <netinet/in.h>
18#include <sys/mman.h>
19
20jclass gOptions_class;
21jfieldID gOptions_justBoundsFieldID;
22jfieldID gOptions_sampleSizeFieldID;
23jfieldID gOptions_configFieldID;
24jfieldID gOptions_ditherFieldID;
25jfieldID gOptions_purgeableFieldID;
26jfieldID gOptions_shareableFieldID;
27jfieldID gOptions_nativeAllocFieldID;
28jfieldID gOptions_widthFieldID;
29jfieldID gOptions_heightFieldID;
30jfieldID gOptions_mimeFieldID;
31jfieldID gOptions_mCancelID;
32
33static jclass gFileDescriptor_class;
34static jfieldID gFileDescriptor_descriptor;
35
36#if 0
37    #define TRACE_BITMAP(code)  code
38#else
39    #define TRACE_BITMAP(code)
40#endif
41
42using namespace android;
43
44class NinePatchPeeker : public SkImageDecoder::Peeker {
45    SkImageDecoder* fHost;
46public:
47    NinePatchPeeker(SkImageDecoder* host) {
48        // the host lives longer than we do, so a raw ptr is safe
49        fHost = host;
50        fPatchIsValid = false;
51    }
52
53    ~NinePatchPeeker() {
54        if (fPatchIsValid) {
55            free(fPatch);
56        }
57    }
58
59    bool    fPatchIsValid;
60    Res_png_9patch*  fPatch;
61
62    virtual bool peek(const char tag[], const void* data, size_t length) {
63        if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
64            Res_png_9patch* patch = (Res_png_9patch*) data;
65            size_t patchSize = patch->serializedSize();
66            assert(length == patchSize);
67            // You have to copy the data because it is owned by the png reader
68            Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
69            memcpy(patchNew, patch, patchSize);
70            // this relies on deserialization being done in place
71            Res_png_9patch::deserialize(patchNew);
72            patchNew->fileToDevice();
73            if (fPatchIsValid) {
74                free(fPatch);
75            }
76            fPatch = patchNew;
77            //printf("9patch: (%d,%d)-(%d,%d)\n",
78            //       fPatch.sizeLeft, fPatch.sizeTop,
79            //       fPatch.sizeRight, fPatch.sizeBottom);
80            fPatchIsValid = true;
81
82            // now update our host to force index or 32bit config
83            // 'cause we don't want 565 predithered, since as a 9patch, we know
84            // we will be stretched, and therefore we want to dither afterwards.
85            static const SkBitmap::Config gNo565Pref[] = {
86                SkBitmap::kIndex8_Config,
87                SkBitmap::kIndex8_Config,
88                SkBitmap::kARGB_8888_Config,
89                SkBitmap::kARGB_8888_Config,
90                SkBitmap::kARGB_8888_Config,
91                SkBitmap::kARGB_8888_Config,
92            };
93            fHost->setPrefConfigTable(gNo565Pref);
94        } else {
95            fPatch = NULL;
96        }
97        return true;    // keep on decoding
98    }
99};
100
101class AssetStreamAdaptor : public SkStream {
102public:
103    AssetStreamAdaptor(Asset* a) : fAsset(a) {}
104
105    virtual bool rewind() {
106        off_t pos = fAsset->seek(0, SEEK_SET);
107        if (pos == (off_t)-1) {
108            SkDebugf("----- fAsset->seek(rewind) failed\n");
109            return false;
110        }
111        return true;
112    }
113
114    virtual size_t read(void* buffer, size_t size) {
115        ssize_t amount;
116
117        if (NULL == buffer) {
118            if (0 == size) {  // caller is asking us for our total length
119                return fAsset->getLength();
120            }
121            // asset->seek returns new total offset
122            // we want to return amount that was skipped
123
124            off_t oldOffset = fAsset->seek(0, SEEK_CUR);
125            if (-1 == oldOffset) {
126                SkDebugf("---- fAsset->seek(oldOffset) failed\n");
127                return 0;
128            }
129            off_t newOffset = fAsset->seek(size, SEEK_CUR);
130            if (-1 == newOffset) {
131                SkDebugf("---- fAsset->seek(%d) failed\n", size);
132                return 0;
133            }
134            amount = newOffset - oldOffset;
135        } else {
136            amount = fAsset->read(buffer, size);
137            if (amount <= 0) {
138                SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
139            }
140        }
141
142        if (amount < 0) {
143            amount = 0;
144        }
145        return amount;
146    }
147
148private:
149    Asset*  fAsset;
150};
151
152///////////////////////////////////////////////////////////////////////////////
153
154static inline int32_t validOrNeg1(bool isValid, int32_t value) {
155//    return isValid ? value : -1;
156    SkASSERT((int)isValid == 0 || (int)isValid == 1);
157    return ((int32_t)isValid - 1) | value;
158}
159
160jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
161    static const struct {
162        SkImageDecoder::Format fFormat;
163        const char*            fMimeType;
164    } gMimeTypes[] = {
165        { SkImageDecoder::kBMP_Format,  "image/bmp" },
166        { SkImageDecoder::kGIF_Format,  "image/gif" },
167        { SkImageDecoder::kICO_Format,  "image/x-ico" },
168        { SkImageDecoder::kJPEG_Format, "image/jpeg" },
169        { SkImageDecoder::kPNG_Format,  "image/png" },
170        { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
171    };
172
173    const char* cstr = NULL;
174    for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
175        if (gMimeTypes[i].fFormat == format) {
176            cstr = gMimeTypes[i].fMimeType;
177            break;
178        }
179    }
180
181    jstring jstr = 0;
182    if (NULL != cstr) {
183        jstr = env->NewStringUTF(cstr);
184    }
185    return jstr;
186}
187
188static bool optionsPurgeable(JNIEnv* env, jobject options) {
189    return options != NULL &&
190            env->GetBooleanField(options, gOptions_purgeableFieldID);
191}
192
193static bool optionsShareable(JNIEnv* env, jobject options) {
194    return options != NULL &&
195            env->GetBooleanField(options, gOptions_shareableFieldID);
196}
197
198static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
199    return NULL == options ||
200            !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
201}
202
203static jobject nullObjectReturn(const char msg[]) {
204    if (msg) {
205        SkDebugf("--- %s\n", msg);
206    }
207    return NULL;
208}
209
210static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
211                                   int sampleSize, bool ditherImage) {
212    SkImageRef* pr;
213    // only use ashmem for large images, since mmaps come at a price
214    if (bitmap->getSize() >= 32 * 1024) {
215        pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
216    } else {
217        pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
218    }
219    pr->setDitherImage(ditherImage);
220    bitmap->setPixelRef(pr)->unref();
221    pr->isOpaque(bitmap);
222    return pr;
223}
224
225// since we "may" create a purgeable imageref, we require the stream be ref'able
226// i.e. dynamically allocated, since its lifetime may exceed the current stack
227// frame.
228static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
229                        jobject options, bool allowPurgeable,
230                        bool forcePurgeable = false) {
231    int sampleSize = 1;
232    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
233    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
234    bool doDither = true;
235    bool isPurgeable = forcePurgeable ||
236                        (allowPurgeable && optionsPurgeable(env, options));
237    bool reportSizeToVM = optionsReportSizeToVM(env, options);
238
239    if (NULL != options) {
240        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
241        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
242            mode = SkImageDecoder::kDecodeBounds_Mode;
243        }
244        // initialize these, in case we fail later on
245        env->SetIntField(options, gOptions_widthFieldID, -1);
246        env->SetIntField(options, gOptions_heightFieldID, -1);
247        env->SetObjectField(options, gOptions_mimeFieldID, 0);
248
249        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
250        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
251        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
252    }
253
254    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
255    if (NULL == decoder) {
256        return nullObjectReturn("SkImageDecoder::Factory returned null");
257    }
258
259    decoder->setSampleSize(sampleSize);
260    decoder->setDitherImage(doDither);
261
262    NinePatchPeeker     peeker(decoder);
263    JavaPixelAllocator  javaAllocator(env, reportSizeToVM);
264    SkBitmap*           bitmap = new SkBitmap;
265    Res_png_9patch      dummy9Patch;
266
267    SkAutoTDelete<SkImageDecoder>   add(decoder);
268    SkAutoTDelete<SkBitmap>         adb(bitmap);
269
270    decoder->setPeeker(&peeker);
271    if (!isPurgeable) {
272        decoder->setAllocator(&javaAllocator);
273    }
274
275    AutoDecoderCancel   adc(options, decoder);
276
277    // To fix the race condition in case "requestCancelDecode"
278    // happens earlier than AutoDecoderCancel object is added
279    // to the gAutoDecoderCancelMutex linked list.
280    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
281        return nullObjectReturn("gOptions_mCancelID");;
282    }
283
284    SkImageDecoder::Mode decodeMode = mode;
285    if (isPurgeable) {
286        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
287    }
288    if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
289        return nullObjectReturn("decoder->decode returned false");
290    }
291
292    // update options (if any)
293    if (NULL != options) {
294        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
295        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
296        // TODO: set the mimeType field with the data from the codec.
297        // but how to reuse a set of strings, rather than allocating new one
298        // each time?
299        env->SetObjectField(options, gOptions_mimeFieldID,
300                            getMimeTypeString(env, decoder->getFormat()));
301    }
302
303    // if we're in justBounds mode, return now (skip the java bitmap)
304    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
305        return NULL;
306    }
307
308    jbyteArray ninePatchChunk = NULL;
309    if (peeker.fPatchIsValid) {
310        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
311        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
312        if (NULL == ninePatchChunk) {
313            return nullObjectReturn("ninePatchChunk == null");
314        }
315        jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
316                                                              NULL);
317        if (NULL == array) {
318            return nullObjectReturn("primitive array == null");
319        }
320        peeker.fPatch->serialize(array);
321        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
322    }
323
324    // detach bitmap from its autotdeleter, since we want to own it now
325    adb.detach();
326
327    if (padding) {
328        if (peeker.fPatchIsValid) {
329            GraphicsJNI::set_jrect(env, padding,
330                                   peeker.fPatch->paddingLeft,
331                                   peeker.fPatch->paddingTop,
332                                   peeker.fPatch->paddingRight,
333                                   peeker.fPatch->paddingBottom);
334        } else {
335            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
336        }
337    }
338
339    SkPixelRef* pr;
340    if (isPurgeable) {
341        pr = installPixelRef(bitmap, stream, sampleSize, doDither);
342    } else {
343        // if we get here, we're in kDecodePixels_Mode and will therefore
344        // already have a pixelref installed.
345        pr = bitmap->pixelRef();
346    }
347    // promise we will never change our pixels (great for sharing and pictures)
348    pr->setImmutable();
349    // now create the java bitmap
350    return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
351}
352
353static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
354                                  jobject is,       // InputStream
355                                  jbyteArray storage,   // byte[]
356                                  jobject padding,
357                                  jobject options) {  // BitmapFactory$Options
358    jobject bitmap = NULL;
359    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
360
361    if (stream) {
362        // for now we don't allow purgeable with java inputstreams
363        bitmap = doDecode(env, stream, padding, options, false);
364        stream->unref();
365    }
366    return bitmap;
367}
368
369static ssize_t getFDSize(int fd) {
370    off_t curr = ::lseek(fd, 0, SEEK_CUR);
371    if (curr < 0) {
372        return 0;
373    }
374    size_t size = ::lseek(fd, 0, SEEK_END);
375    ::lseek(fd, curr, SEEK_SET);
376    return size;
377}
378
379/** Restore the file descriptor's offset in our destructor
380 */
381class AutoFDSeek {
382public:
383    AutoFDSeek(int fd) : fFD(fd) {
384        fCurr = ::lseek(fd, 0, SEEK_CUR);
385    }
386    ~AutoFDSeek() {
387        if (fCurr >= 0) {
388            ::lseek(fFD, fCurr, SEEK_SET);
389        }
390    }
391private:
392    int     fFD;
393    off_t   fCurr;
394};
395
396static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
397                                          jobject fileDescriptor,
398                                          jobject padding,
399                                          jobject bitmapFactoryOptions) {
400    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
401
402    jint descriptor = env->GetIntField(fileDescriptor,
403                                       gFileDescriptor_descriptor);
404
405    bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
406    bool isShareable = optionsShareable(env, bitmapFactoryOptions);
407    bool weOwnTheFD = false;
408    if (isPurgeable && isShareable) {
409        int newFD = ::dup(descriptor);
410        if (-1 != newFD) {
411            weOwnTheFD = true;
412            descriptor = newFD;
413        }
414    }
415
416    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
417    SkAutoUnref aur(stream);
418    if (!stream->isValid()) {
419        return NULL;
420    }
421
422    /* Restore our offset when we leave, so we can be called more than once
423       with the same descriptor. This is only required if we didn't dup the
424       file descriptor, but it is OK to do it all the time.
425    */
426    AutoFDSeek as(descriptor);
427
428    /* Allow purgeable iff we own the FD, i.e., in the puregeable and
429       shareable case.
430    */
431    return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
432}
433
434/*  make a deep copy of the asset, and return it as a stream, or NULL if there
435    was an error.
436 */
437static SkStream* copyAssetToStream(Asset* asset) {
438    // if we could "ref/reopen" the asset, we may not need to copy it here
439    off_t size = asset->seek(0, SEEK_SET);
440    if ((off_t)-1 == size) {
441        SkDebugf("---- copyAsset: asset rewind failed\n");
442        return NULL;
443    }
444
445    size = asset->getLength();
446    if (size <= 0) {
447        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
448        return NULL;
449    }
450
451    SkStream* stream = new SkMemoryStream(size);
452    void* data = const_cast<void*>(stream->getMemoryBase());
453    off_t len = asset->read(data, size);
454    if (len != size) {
455        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
456        delete stream;
457        stream = NULL;
458    }
459    return stream;
460}
461
462static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
463                                 jint native_asset,    // Asset
464                                 jobject padding,       // Rect
465                                 jobject options) { // BitmapFactory$Options
466    SkStream* stream;
467    Asset* asset = reinterpret_cast<Asset*>(native_asset);
468    bool forcePurgeable = optionsPurgeable(env, options);
469    if (forcePurgeable) {
470        // if we could "ref/reopen" the asset, we may not need to copy it here
471        // and we could assume optionsShareable, since assets are always RO
472        stream = copyAssetToStream(asset);
473        if (NULL == stream) {
474            return NULL;
475        }
476    } else {
477        // since we know we'll be done with the asset when we return, we can
478        // just use a simple wrapper
479        stream = new AssetStreamAdaptor(asset);
480    }
481    SkAutoUnref aur(stream);
482    return doDecode(env, stream, padding, options, true, forcePurgeable);
483}
484
485static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
486                                     int offset, int length, jobject options) {
487    /*  If optionsShareable() we could decide to just wrap the java array and
488        share it, but that means adding a globalref to the java array object
489        and managing its lifetime. For now we just always copy the array's data
490        if optionsPurgeable().
491     */
492    AutoJavaByteArray ar(env, byteArray);
493    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
494                                          optionsPurgeable(env, options));
495    SkAutoUnref aur(stream);
496    return doDecode(env, stream, NULL, options, true);
497}
498
499static void nativeRequestCancel(JNIEnv*, jobject joptions) {
500    (void)AutoDecoderCancel::RequestCancel(joptions);
501}
502
503static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
504        jobject padding) {
505
506    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
507    if (array != NULL) {
508        size_t chunkSize = env->GetArrayLength(chunkObject);
509        void* storage = alloca(chunkSize);
510        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
511        memcpy(chunk, array, chunkSize);
512        android::Res_png_9patch::deserialize(chunk);
513
514        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
515        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
516        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
517        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
518
519        for (int i = 0; i < chunk->numXDivs; i++) {
520            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
521            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
522                chunk->xDivs[i]++;
523            }
524        }
525
526        for (int i = 0; i < chunk->numYDivs; i++) {
527            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
528            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
529                chunk->yDivs[i]++;
530            }
531        }
532
533        memcpy(array, chunk, chunkSize);
534
535        if (padding) {
536            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
537                    chunk->paddingRight, chunk->paddingBottom);
538        }
539
540        env->ReleaseByteArrayElements(chunkObject, array, 0);
541    }
542    return chunkObject;
543}
544
545static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
546    SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
547
548    // these are the only default configs that make sense for codecs right now
549    static const SkBitmap::Config gValidDefConfig[] = {
550        SkBitmap::kRGB_565_Config,
551        SkBitmap::kARGB_8888_Config,
552    };
553
554    for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
555        if (config == gValidDefConfig[i]) {
556            SkImageDecoder::SetDeviceConfig(config);
557            break;
558        }
559    }
560}
561
562static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
563    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
564    int width, height;
565    if (NULL == decoder) {
566        doThrowIOE(env, "Image format not supported");
567        return nullObjectReturn("SkImageDecoder::Factory returned null");
568    }
569
570    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
571    decoder->setAllocator(javaAllocator);
572    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
573    decoder->setReporter(javaMemoryReporter);
574    javaAllocator->unref();
575    javaMemoryReporter->unref();
576
577    if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
578        char msg[1024];
579        snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
580        doThrowIOE(env, msg);
581        return nullObjectReturn("decoder->buildTileIndex returned false");
582    }
583
584    SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
585
586    return GraphicsJNI::createLargeBitmap(env, bm);
587}
588
589static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
590                                     int offset, int length, jboolean isShareable) {
591    AutoJavaByteArray ar(env, byteArray);
592    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
593    SkAutoUnref aur(stream);
594    if (isShareable) {
595        aur.detach();
596    }
597    return doBuildTileIndex(env, stream, isShareable);
598}
599
600static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
601                                          jobject fileDescriptor, jboolean isShareable) {
602    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
603
604    jint descriptor = env->GetIntField(fileDescriptor,
605                                       gFileDescriptor_descriptor);
606    bool weOwnTheFD = false;
607
608    if (isShareable) {
609        int newFD = ::dup(descriptor);
610        if (-1 != newFD) {
611            weOwnTheFD = true;
612            descriptor = newFD;
613        }
614    }
615
616    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
617    SkAutoUnref aur(stream);
618    if (!stream->isValid()) {
619        return NULL;
620    }
621
622    if (isShareable) {
623        aur.detach();
624    }
625
626    /* Restore our offset when we leave, so we can be called more than once
627       with the same descriptor. This is only required if we didn't dup the
628       file descriptor, but it is OK to do it all the time.
629    */
630    AutoFDSeek as(descriptor);
631
632    return doBuildTileIndex(env, stream, isShareable);
633}
634
635static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
636                                  jobject is,       // InputStream
637                                  jbyteArray storage, // byte[]
638                                  jboolean isShareable) {
639    jobject largeBitmap = NULL;
640    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
641
642    if (stream) {
643        // for now we don't allow shareable with java inputstreams
644        largeBitmap = doBuildTileIndex(env, stream, false);
645        stream->unref();
646    }
647    return largeBitmap;
648}
649
650static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
651                                 jint native_asset, // Asset
652                                 jboolean isShareable) {
653    SkStream* stream;
654    Asset* asset = reinterpret_cast<Asset*>(native_asset);
655    stream = new AssetStreamAdaptor(asset);
656    SkAutoUnref aur(stream);
657    if (isShareable) {
658        aur.detach();
659    }
660    return doBuildTileIndex(env, stream, isShareable);
661}
662
663///////////////////////////////////////////////////////////////////////////////
664
665static JNINativeMethod gMethods[] = {
666    {   "nativeDecodeStream",
667        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
668        (void*)nativeDecodeStream
669    },
670
671    {   "nativeDecodeFileDescriptor",
672        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
673        (void*)nativeDecodeFileDescriptor
674    },
675
676    {   "nativeDecodeAsset",
677        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
678        (void*)nativeDecodeAsset
679    },
680
681    {   "nativeDecodeByteArray",
682        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
683        (void*)nativeDecodeByteArray
684    },
685
686    {   "nativeScaleNinePatch",
687        "([BFLandroid/graphics/Rect;)[B",
688        (void*)nativeScaleNinePatch
689    },
690
691    {   "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
692
693    {   "nativeCreateLargeBitmap",
694        "([BIIZ)Landroid/graphics/LargeBitmap;",
695        (void*)nativeCreateLargeBitmapFromByteArray
696    },
697
698    {   "nativeCreateLargeBitmap",
699        "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
700        (void*)nativeCreateLargeBitmapFromStream
701    },
702
703    {   "nativeCreateLargeBitmap",
704        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
705        (void*)nativeCreateLargeBitmapFromFileDescriptor
706    },
707
708    {   "nativeCreateLargeBitmap",
709        "(IZ)Landroid/graphics/LargeBitmap;",
710        (void*)nativeCreateLargeBitmapFromAsset
711    },
712};
713
714static JNINativeMethod gOptionsMethods[] = {
715    {   "requestCancel", "()V", (void*)nativeRequestCancel }
716};
717
718static jclass make_globalref(JNIEnv* env, const char classname[]) {
719    jclass c = env->FindClass(classname);
720    SkASSERT(c);
721    return (jclass)env->NewGlobalRef(c);
722}
723
724static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
725                                const char fieldname[], const char type[]) {
726    jfieldID id = env->GetFieldID(clazz, fieldname, type);
727    SkASSERT(id);
728    return id;
729}
730
731#define kClassPathName  "android/graphics/BitmapFactory"
732
733#define RETURN_ERR_IF_NULL(value) \
734    do { if (!(value)) { assert(0); return -1; } } while (false)
735
736int register_android_graphics_BitmapFactory(JNIEnv* env);
737int register_android_graphics_BitmapFactory(JNIEnv* env) {
738    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
739    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
740    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
741    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
742            "Landroid/graphics/Bitmap$Config;");
743    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
744    gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
745    gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
746    gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
747    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
748    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
749    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
750    gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
751
752    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
753    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
754
755    int ret = AndroidRuntime::registerNativeMethods(env,
756                                    "android/graphics/BitmapFactory$Options",
757                                    gOptionsMethods,
758                                    SK_ARRAY_COUNT(gOptionsMethods));
759    if (ret) {
760        return ret;
761    }
762    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
763                                         gMethods, SK_ARRAY_COUNT(gMethods));
764}
765