BitmapFactory.cpp revision 288471d8a57e1c318742cbfc28697877436fdb87
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    if (optionsPurgeable(env, options)) {
469        // if we could "ref/reopen" the asset, we may not need to copy it here
470        // and we could assume optionsShareable, since assets are always RO
471        stream = copyAssetToStream(asset);
472        if (NULL == stream) {
473            return NULL;
474        }
475    } else {
476        // since we know we'll be done with the asset when we return, we can
477        // just use a simple wrapper
478        stream = new AssetStreamAdaptor(asset);
479    }
480    SkAutoUnref aur(stream);
481    return doDecode(env, stream, padding, options, true, forcePurgeable);
482}
483
484static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
485                                     int offset, int length, jobject options) {
486    /*  If optionsShareable() we could decide to just wrap the java array and
487        share it, but that means adding a globalref to the java array object
488        and managing its lifetime. For now we just always copy the array's data
489        if optionsPurgeable().
490     */
491    AutoJavaByteArray ar(env, byteArray);
492    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
493                                          optionsPurgeable(env, options));
494    SkAutoUnref aur(stream);
495    return doDecode(env, stream, NULL, options, true);
496}
497
498static void nativeRequestCancel(JNIEnv*, jobject joptions) {
499    (void)AutoDecoderCancel::RequestCancel(joptions);
500}
501
502static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
503        jobject padding) {
504
505    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
506    if (array != NULL) {
507        size_t chunkSize = env->GetArrayLength(chunkObject);
508        void* storage = alloca(chunkSize);
509        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
510        memcpy(chunk, array, chunkSize);
511        android::Res_png_9patch::deserialize(chunk);
512
513        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
514        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
515        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
516        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
517
518        for (int i = 0; i < chunk->numXDivs; i++) {
519            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
520            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
521                chunk->xDivs[i]++;
522            }
523        }
524
525        for (int i = 0; i < chunk->numYDivs; i++) {
526            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
527            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
528                chunk->yDivs[i]++;
529            }
530        }
531
532        memcpy(array, chunk, chunkSize);
533
534        if (padding) {
535            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
536                    chunk->paddingRight, chunk->paddingBottom);
537        }
538
539        env->ReleaseByteArrayElements(chunkObject, array, 0);
540    }
541    return chunkObject;
542}
543
544static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
545    SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
546
547    // these are the only default configs that make sense for codecs right now
548    static const SkBitmap::Config gValidDefConfig[] = {
549        SkBitmap::kRGB_565_Config,
550        SkBitmap::kARGB_8888_Config,
551    };
552
553    for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
554        if (config == gValidDefConfig[i]) {
555            SkImageDecoder::SetDeviceConfig(config);
556            break;
557        }
558    }
559}
560
561static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
562    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
563    int width, height;
564    if (NULL == decoder) {
565        doThrowIOE(env, "Image format not supported");
566        return nullObjectReturn("SkImageDecoder::Factory returned null");
567    }
568
569    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
570    decoder->setAllocator(javaAllocator);
571    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
572    decoder->setReporter(javaMemoryReporter);
573    javaAllocator->unref();
574    javaMemoryReporter->unref();
575
576    if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
577        char msg[1024];
578        snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
579        doThrowIOE(env, msg);
580        return nullObjectReturn("decoder->buildTileIndex returned false");
581    }
582
583    SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
584
585    return GraphicsJNI::createLargeBitmap(env, bm);
586}
587
588static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
589                                     int offset, int length, jboolean isShareable) {
590    AutoJavaByteArray ar(env, byteArray);
591    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
592    SkAutoUnref aur(stream);
593    if (isShareable) {
594        aur.detach();
595    }
596    return doBuildTileIndex(env, stream, isShareable);
597}
598
599static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
600                                          jobject fileDescriptor, jboolean isShareable) {
601    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
602
603    jint descriptor = env->GetIntField(fileDescriptor,
604                                       gFileDescriptor_descriptor);
605    bool weOwnTheFD = false;
606
607    if (isShareable) {
608        int newFD = ::dup(descriptor);
609        if (-1 != newFD) {
610            weOwnTheFD = true;
611            descriptor = newFD;
612        }
613    }
614
615    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
616    SkAutoUnref aur(stream);
617    if (!stream->isValid()) {
618        return NULL;
619    }
620
621    if (isShareable) {
622        aur.detach();
623    }
624
625    /* Restore our offset when we leave, so we can be called more than once
626       with the same descriptor. This is only required if we didn't dup the
627       file descriptor, but it is OK to do it all the time.
628    */
629    AutoFDSeek as(descriptor);
630
631    return doBuildTileIndex(env, stream, isShareable);
632}
633
634static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
635                                  jobject is,       // InputStream
636                                  jbyteArray storage, // byte[]
637                                  jboolean isShareable) {
638    jobject largeBitmap = NULL;
639    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
640
641    if (stream) {
642        // for now we don't allow shareable with java inputstreams
643        largeBitmap = doBuildTileIndex(env, stream, false);
644        stream->unref();
645    }
646    return largeBitmap;
647}
648
649static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
650                                 jint native_asset, // Asset
651                                 jboolean isShareable) {
652    SkStream* stream;
653    Asset* asset = reinterpret_cast<Asset*>(native_asset);
654    stream = new AssetStreamAdaptor(asset);
655    SkAutoUnref aur(stream);
656    if (isShareable) {
657        aur.detach();
658    }
659    return doBuildTileIndex(env, stream, isShareable);
660}
661
662///////////////////////////////////////////////////////////////////////////////
663
664static JNINativeMethod gMethods[] = {
665    {   "nativeDecodeStream",
666        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
667        (void*)nativeDecodeStream
668    },
669
670    {   "nativeDecodeFileDescriptor",
671        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
672        (void*)nativeDecodeFileDescriptor
673    },
674
675    {   "nativeDecodeAsset",
676        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
677        (void*)nativeDecodeAsset
678    },
679
680    {   "nativeDecodeByteArray",
681        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
682        (void*)nativeDecodeByteArray
683    },
684
685    {   "nativeScaleNinePatch",
686        "([BFLandroid/graphics/Rect;)[B",
687        (void*)nativeScaleNinePatch
688    },
689
690    {   "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
691
692    {   "nativeCreateLargeBitmap",
693        "([BIIZ)Landroid/graphics/LargeBitmap;",
694        (void*)nativeCreateLargeBitmapFromByteArray
695    },
696
697    {   "nativeCreateLargeBitmap",
698        "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
699        (void*)nativeCreateLargeBitmapFromStream
700    },
701
702    {   "nativeCreateLargeBitmap",
703        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
704        (void*)nativeCreateLargeBitmapFromFileDescriptor
705    },
706
707    {   "nativeCreateLargeBitmap",
708        "(IZ)Landroid/graphics/LargeBitmap;",
709        (void*)nativeCreateLargeBitmapFromAsset
710    },
711};
712
713static JNINativeMethod gOptionsMethods[] = {
714    {   "requestCancel", "()V", (void*)nativeRequestCancel }
715};
716
717static jclass make_globalref(JNIEnv* env, const char classname[]) {
718    jclass c = env->FindClass(classname);
719    SkASSERT(c);
720    return (jclass)env->NewGlobalRef(c);
721}
722
723static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
724                                const char fieldname[], const char type[]) {
725    jfieldID id = env->GetFieldID(clazz, fieldname, type);
726    SkASSERT(id);
727    return id;
728}
729
730#define kClassPathName  "android/graphics/BitmapFactory"
731
732#define RETURN_ERR_IF_NULL(value) \
733    do { if (!(value)) { assert(0); return -1; } } while (false)
734
735int register_android_graphics_BitmapFactory(JNIEnv* env);
736int register_android_graphics_BitmapFactory(JNIEnv* env) {
737    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
738    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
739    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
740    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
741            "Landroid/graphics/Bitmap$Config;");
742    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
743    gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
744    gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
745    gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
746    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
747    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
748    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
749    gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
750
751    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
752    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
753
754    int ret = AndroidRuntime::registerNativeMethods(env,
755                                    "android/graphics/BitmapFactory$Options",
756                                    gOptionsMethods,
757                                    SK_ARRAY_COUNT(gOptionsMethods));
758    if (ret) {
759        return ret;
760    }
761    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
762                                         gMethods, SK_ARRAY_COUNT(gMethods));
763}
764