BitmapFactory.cpp revision f1f48bc7f200f54c76b22d845d8ba8419879b375
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    return pr;
222}
223
224// since we "may" create a purgeable imageref, we require the stream be ref'able
225// i.e. dynamically allocated, since its lifetime may exceed the current stack
226// frame.
227static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
228                        jobject options, bool allowPurgeable,
229                        bool forcePurgeable = false) {
230    int sampleSize = 1;
231    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
232    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
233    bool doDither = true;
234    bool isPurgeable = forcePurgeable ||
235                        (allowPurgeable && optionsPurgeable(env, options));
236    bool reportSizeToVM = optionsReportSizeToVM(env, options);
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 nullObjectReturn("SkImageDecoder::Factory returned null");
256    }
257
258    decoder->setSampleSize(sampleSize);
259    decoder->setDitherImage(doDither);
260
261    NinePatchPeeker     peeker(decoder);
262    JavaPixelAllocator  javaAllocator(env, reportSizeToVM);
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    if (!isPurgeable) {
271        decoder->setAllocator(&javaAllocator);
272    }
273
274    AutoDecoderCancel   adc(options, decoder);
275
276    // To fix the race condition in case "requestCancelDecode"
277    // happens earlier than AutoDecoderCancel object is added
278    // to the gAutoDecoderCancelMutex linked list.
279    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
280        return nullObjectReturn("gOptions_mCancelID");;
281    }
282
283    SkImageDecoder::Mode decodeMode = mode;
284    if (isPurgeable) {
285        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
286    }
287    if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
288        return nullObjectReturn("decoder->decode returned false");
289    }
290
291    // update options (if any)
292    if (NULL != options) {
293        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
294        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
295        // TODO: set the mimeType field with the data from the codec.
296        // but how to reuse a set of strings, rather than allocating new one
297        // each time?
298        env->SetObjectField(options, gOptions_mimeFieldID,
299                            getMimeTypeString(env, decoder->getFormat()));
300    }
301
302    // if we're in justBounds mode, return now (skip the java bitmap)
303    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
304        return NULL;
305    }
306
307    jbyteArray ninePatchChunk = NULL;
308    if (peeker.fPatchIsValid) {
309        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
310        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
311        if (NULL == ninePatchChunk) {
312            return nullObjectReturn("ninePatchChunk == null");
313        }
314        jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
315                                                              NULL);
316        if (NULL == array) {
317            return nullObjectReturn("primitive array == null");
318        }
319        peeker.fPatch->serialize(array);
320        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
321    }
322
323    // detach bitmap from its autotdeleter, since we want to own it now
324    adb.detach();
325
326    if (padding) {
327        if (peeker.fPatchIsValid) {
328            GraphicsJNI::set_jrect(env, padding,
329                                   peeker.fPatch->paddingLeft,
330                                   peeker.fPatch->paddingTop,
331                                   peeker.fPatch->paddingRight,
332                                   peeker.fPatch->paddingBottom);
333        } else {
334            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
335        }
336    }
337
338    SkPixelRef* pr;
339    if (isPurgeable) {
340        pr = installPixelRef(bitmap, stream, sampleSize, doDither);
341    } else {
342        // if we get here, we're in kDecodePixels_Mode and will therefore
343        // already have a pixelref installed.
344        pr = bitmap->pixelRef();
345    }
346    // promise we will never change our pixels (great for sharing and pictures)
347    pr->setImmutable();
348    // now create the java bitmap
349    return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
350}
351
352static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
353                                  jobject is,       // InputStream
354                                  jbyteArray storage,   // byte[]
355                                  jobject padding,
356                                  jobject options) {  // BitmapFactory$Options
357    jobject bitmap = NULL;
358    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
359
360    if (stream) {
361        // for now we don't allow purgeable with java inputstreams
362        bitmap = doDecode(env, stream, padding, options, false);
363        stream->unref();
364    }
365    return bitmap;
366}
367
368static ssize_t getFDSize(int fd) {
369    off_t curr = ::lseek(fd, 0, SEEK_CUR);
370    if (curr < 0) {
371        return 0;
372    }
373    size_t size = ::lseek(fd, 0, SEEK_END);
374    ::lseek(fd, curr, SEEK_SET);
375    return size;
376}
377
378/** Restore the file descriptor's offset in our destructor
379 */
380class AutoFDSeek {
381public:
382    AutoFDSeek(int fd) : fFD(fd) {
383        fCurr = ::lseek(fd, 0, SEEK_CUR);
384    }
385    ~AutoFDSeek() {
386        if (fCurr >= 0) {
387            ::lseek(fFD, fCurr, SEEK_SET);
388        }
389    }
390private:
391    int     fFD;
392    off_t   fCurr;
393};
394
395static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
396                                          jobject fileDescriptor,
397                                          jobject padding,
398                                          jobject bitmapFactoryOptions) {
399    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
400
401    jint descriptor = env->GetIntField(fileDescriptor,
402                                       gFileDescriptor_descriptor);
403
404    bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
405    bool isShareable = optionsShareable(env, bitmapFactoryOptions);
406    bool weOwnTheFD = false;
407    if (isPurgeable && isShareable) {
408        int newFD = ::dup(descriptor);
409        if (-1 != newFD) {
410            weOwnTheFD = true;
411            descriptor = newFD;
412        }
413    }
414
415    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
416    SkAutoUnref aur(stream);
417    if (!stream->isValid()) {
418        return NULL;
419    }
420
421    /* Restore our offset when we leave, so we can be called more than once
422       with the same descriptor. This is only required if we didn't dup the
423       file descriptor, but it is OK to do it all the time.
424    */
425    AutoFDSeek as(descriptor);
426
427    /* Allow purgeable iff we own the FD, i.e., in the puregeable and
428       shareable case.
429    */
430    return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
431}
432
433/*  make a deep copy of the asset, and return it as a stream, or NULL if there
434    was an error.
435 */
436static SkStream* copyAssetToStream(Asset* asset) {
437    // if we could "ref/reopen" the asset, we may not need to copy it here
438    off_t size = asset->seek(0, SEEK_SET);
439    if ((off_t)-1 == size) {
440        SkDebugf("---- copyAsset: asset rewind failed\n");
441        return NULL;
442    }
443
444    size = asset->getLength();
445    if (size <= 0) {
446        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
447        return NULL;
448    }
449
450    SkStream* stream = new SkMemoryStream(size);
451    void* data = const_cast<void*>(stream->getMemoryBase());
452    off_t len = asset->read(data, size);
453    if (len != size) {
454        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
455        delete stream;
456        stream = NULL;
457    }
458    return stream;
459}
460
461static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
462                                 jint native_asset,    // Asset
463                                 jobject padding,       // Rect
464                                 jobject options) { // BitmapFactory$Options
465    SkStream* stream;
466    Asset* asset = reinterpret_cast<Asset*>(native_asset);
467    // assets can always be rebuilt, so force this
468    bool forcePurgeable = true;
469
470    if (forcePurgeable || optionsPurgeable(env, options)) {
471        // if we could "ref/reopen" the asset, we may not need to copy it here
472        // and we could assume optionsShareable, since assets are always RO
473        stream = copyAssetToStream(asset);
474        if (NULL == stream) {
475            return NULL;
476        }
477    } else {
478        // since we know we'll be done with the asset when we return, we can
479        // just use a simple wrapper
480        stream = new AssetStreamAdaptor(asset);
481    }
482    SkAutoUnref aur(stream);
483    return doDecode(env, stream, padding, options, true, forcePurgeable);
484}
485
486static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
487                                     int offset, int length, jobject options) {
488    /*  If optionsShareable() we could decide to just wrap the java array and
489        share it, but that means adding a globalref to the java array object
490        and managing its lifetime. For now we just always copy the array's data
491        if optionsPurgeable().
492     */
493    AutoJavaByteArray ar(env, byteArray);
494    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
495                                          optionsPurgeable(env, options));
496    SkAutoUnref aur(stream);
497    return doDecode(env, stream, NULL, options, true);
498}
499
500static void nativeRequestCancel(JNIEnv*, jobject joptions) {
501    (void)AutoDecoderCancel::RequestCancel(joptions);
502}
503
504static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
505        jobject padding) {
506
507    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
508    if (array != NULL) {
509        size_t chunkSize = env->GetArrayLength(chunkObject);
510        void* storage = alloca(chunkSize);
511        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
512        memcpy(chunk, array, chunkSize);
513        android::Res_png_9patch::deserialize(chunk);
514
515        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
516        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
517        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
518        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
519
520        for (int i = 0; i < chunk->numXDivs; i++) {
521            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
522            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
523                chunk->xDivs[i]++;
524            }
525        }
526
527        for (int i = 0; i < chunk->numYDivs; i++) {
528            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
529            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
530                chunk->yDivs[i]++;
531            }
532        }
533
534        memcpy(array, chunk, chunkSize);
535
536        if (padding) {
537            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
538                    chunk->paddingRight, chunk->paddingBottom);
539        }
540
541        env->ReleaseByteArrayElements(chunkObject, array, 0);
542    }
543    return chunkObject;
544}
545
546static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
547    SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
548
549    // these are the only default configs that make sense for codecs right now
550    static const SkBitmap::Config gValidDefConfig[] = {
551        SkBitmap::kRGB_565_Config,
552        SkBitmap::kARGB_8888_Config,
553    };
554
555    for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
556        if (config == gValidDefConfig[i]) {
557            SkImageDecoder::SetDeviceConfig(config);
558            break;
559        }
560    }
561}
562
563static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
564    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
565    int width, height;
566    if (NULL == decoder) {
567        doThrowIOE(env, "Image format not supported");
568        return nullObjectReturn("SkImageDecoder::Factory returned null");
569    }
570
571    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
572    decoder->setAllocator(javaAllocator);
573    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
574    decoder->setReporter(javaMemoryReporter);
575    javaAllocator->unref();
576    javaMemoryReporter->unref();
577
578    if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
579        char msg[1024];
580        snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
581        doThrowIOE(env, msg);
582        return nullObjectReturn("decoder->buildTileIndex returned false");
583    }
584
585    SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
586
587    return GraphicsJNI::createLargeBitmap(env, bm);
588}
589
590static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
591                                     int offset, int length, jboolean isShareable) {
592    AutoJavaByteArray ar(env, byteArray);
593    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
594    SkAutoUnref aur(stream);
595    if (isShareable) {
596        aur.detach();
597    }
598    return doBuildTileIndex(env, stream, isShareable);
599}
600
601static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
602                                          jobject fileDescriptor, jboolean isShareable) {
603    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
604
605    jint descriptor = env->GetIntField(fileDescriptor,
606                                       gFileDescriptor_descriptor);
607    bool weOwnTheFD = false;
608
609    if (isShareable) {
610        int newFD = ::dup(descriptor);
611        if (-1 != newFD) {
612            weOwnTheFD = true;
613            descriptor = newFD;
614        }
615    }
616
617    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
618    SkAutoUnref aur(stream);
619    if (!stream->isValid()) {
620        return NULL;
621    }
622
623    if (isShareable) {
624        aur.detach();
625    }
626
627    /* Restore our offset when we leave, so we can be called more than once
628       with the same descriptor. This is only required if we didn't dup the
629       file descriptor, but it is OK to do it all the time.
630    */
631    AutoFDSeek as(descriptor);
632
633    return doBuildTileIndex(env, stream, isShareable);
634}
635
636static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
637                                  jobject is,       // InputStream
638                                  jbyteArray storage, // byte[]
639                                  jboolean isShareable) {
640    jobject largeBitmap = NULL;
641    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
642
643    if (stream) {
644        // for now we don't allow shareable with java inputstreams
645        largeBitmap = doBuildTileIndex(env, stream, false);
646        stream->unref();
647    }
648    return largeBitmap;
649}
650
651static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
652                                 jint native_asset, // Asset
653                                 jboolean isShareable) {
654    SkStream* stream;
655    Asset* asset = reinterpret_cast<Asset*>(native_asset);
656    stream = new AssetStreamAdaptor(asset);
657    SkAutoUnref aur(stream);
658    if (isShareable) {
659        aur.detach();
660    }
661    return doBuildTileIndex(env, stream, isShareable);
662}
663
664///////////////////////////////////////////////////////////////////////////////
665
666static JNINativeMethod gMethods[] = {
667    {   "nativeDecodeStream",
668        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
669        (void*)nativeDecodeStream
670    },
671
672    {   "nativeDecodeFileDescriptor",
673        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
674        (void*)nativeDecodeFileDescriptor
675    },
676
677    {   "nativeDecodeAsset",
678        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
679        (void*)nativeDecodeAsset
680    },
681
682    {   "nativeDecodeByteArray",
683        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
684        (void*)nativeDecodeByteArray
685    },
686
687    {   "nativeScaleNinePatch",
688        "([BFLandroid/graphics/Rect;)[B",
689        (void*)nativeScaleNinePatch
690    },
691
692    {   "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
693
694    {   "nativeCreateLargeBitmap",
695        "([BIIZ)Landroid/graphics/LargeBitmap;",
696        (void*)nativeCreateLargeBitmapFromByteArray
697    },
698
699    {   "nativeCreateLargeBitmap",
700        "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
701        (void*)nativeCreateLargeBitmapFromStream
702    },
703
704    {   "nativeCreateLargeBitmap",
705        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
706        (void*)nativeCreateLargeBitmapFromFileDescriptor
707    },
708
709    {   "nativeCreateLargeBitmap",
710        "(IZ)Landroid/graphics/LargeBitmap;",
711        (void*)nativeCreateLargeBitmapFromAsset
712    },
713};
714
715static JNINativeMethod gOptionsMethods[] = {
716    {   "requestCancel", "()V", (void*)nativeRequestCancel }
717};
718
719static jclass make_globalref(JNIEnv* env, const char classname[]) {
720    jclass c = env->FindClass(classname);
721    SkASSERT(c);
722    return (jclass)env->NewGlobalRef(c);
723}
724
725static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
726                                const char fieldname[], const char type[]) {
727    jfieldID id = env->GetFieldID(clazz, fieldname, type);
728    SkASSERT(id);
729    return id;
730}
731
732#define kClassPathName  "android/graphics/BitmapFactory"
733
734#define RETURN_ERR_IF_NULL(value) \
735    do { if (!(value)) { assert(0); return -1; } } while (false)
736
737int register_android_graphics_BitmapFactory(JNIEnv* env);
738int register_android_graphics_BitmapFactory(JNIEnv* env) {
739    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
740    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
741    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
742    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
743            "Landroid/graphics/Bitmap$Config;");
744    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
745    gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
746    gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
747    gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
748    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
749    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
750    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
751    gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
752
753    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
754    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
755
756    int ret = AndroidRuntime::registerNativeMethods(env,
757                                    "android/graphics/BitmapFactory$Options",
758                                    gOptionsMethods,
759                                    SK_ARRAY_COUNT(gOptionsMethods));
760    if (ret) {
761        return ret;
762    }
763    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
764                                         gMethods, SK_ARRAY_COUNT(gMethods));
765}
766