BitmapFactory.cpp revision 787e35793f47abdb6cc10f328cd58d362c7bd250
1#define LOG_TAG "BitmapFactory"
2
3#include "BitmapFactory.h"
4#include "NinePatchPeeker.h"
5#include "SkImageDecoder.h"
6#include "SkImageRef_ashmem.h"
7#include "SkImageRef_GlobalPool.h"
8#include "SkPixelRef.h"
9#include "SkStream.h"
10#include "SkTemplates.h"
11#include "SkUtils.h"
12#include "CreateJavaOutputStreamAdaptor.h"
13#include "AutoDecodeCancel.h"
14#include "Utils.h"
15#include "JNIHelp.h"
16
17#include <android_runtime/AndroidRuntime.h>
18#include <androidfw/Asset.h>
19#include <androidfw/ResourceTypes.h>
20#include <netinet/in.h>
21#include <sys/mman.h>
22#include <sys/stat.h>
23
24jfieldID gOptions_justBoundsFieldID;
25jfieldID gOptions_sampleSizeFieldID;
26jfieldID gOptions_configFieldID;
27jfieldID gOptions_mutableFieldID;
28jfieldID gOptions_ditherFieldID;
29jfieldID gOptions_purgeableFieldID;
30jfieldID gOptions_shareableFieldID;
31jfieldID gOptions_preferQualityOverSpeedFieldID;
32jfieldID gOptions_scaledFieldID;
33jfieldID gOptions_densityFieldID;
34jfieldID gOptions_screenDensityFieldID;
35jfieldID gOptions_targetDensityFieldID;
36jfieldID gOptions_widthFieldID;
37jfieldID gOptions_heightFieldID;
38jfieldID gOptions_mimeFieldID;
39jfieldID gOptions_mCancelID;
40jfieldID gOptions_bitmapFieldID;
41jfieldID gBitmap_nativeBitmapFieldID;
42jfieldID gBitmap_layoutBoundsFieldID;
43
44#if 0
45    #define TRACE_BITMAP(code)  code
46#else
47    #define TRACE_BITMAP(code)
48#endif
49
50using namespace android;
51
52static inline int32_t validOrNeg1(bool isValid, int32_t value) {
53//    return isValid ? value : -1;
54    SkASSERT((int)isValid == 0 || (int)isValid == 1);
55    return ((int32_t)isValid - 1) | value;
56}
57
58jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
59    static const struct {
60        SkImageDecoder::Format fFormat;
61        const char*            fMimeType;
62    } gMimeTypes[] = {
63        { SkImageDecoder::kBMP_Format,  "image/bmp" },
64        { SkImageDecoder::kGIF_Format,  "image/gif" },
65        { SkImageDecoder::kICO_Format,  "image/x-ico" },
66        { SkImageDecoder::kJPEG_Format, "image/jpeg" },
67        { SkImageDecoder::kPNG_Format,  "image/png" },
68        { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
69    };
70
71    const char* cstr = NULL;
72    for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
73        if (gMimeTypes[i].fFormat == format) {
74            cstr = gMimeTypes[i].fMimeType;
75            break;
76        }
77    }
78
79    jstring jstr = 0;
80    if (NULL != cstr) {
81        jstr = env->NewStringUTF(cstr);
82    }
83    return jstr;
84}
85
86static bool optionsPurgeable(JNIEnv* env, jobject options) {
87    return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
88}
89
90static bool optionsShareable(JNIEnv* env, jobject options) {
91    return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
92}
93
94static bool optionsJustBounds(JNIEnv* env, jobject options) {
95    return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
96}
97
98static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
99    chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
100    chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
101    chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
102    chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
103
104    for (int i = 0; i < chunk->numXDivs; i++) {
105        chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
106        if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
107            chunk->xDivs[i]++;
108        }
109    }
110
111    for (int i = 0; i < chunk->numYDivs; i++) {
112        chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
113        if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
114            chunk->yDivs[i]++;
115        }
116    }
117}
118
119static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
120        int sampleSize, bool ditherImage) {
121
122    SkImageRef* pr;
123    // only use ashmem for large images, since mmaps come at a price
124    if (bitmap->getSize() >= 32 * 1024) {
125        pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
126    } else {
127        pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
128    }
129    pr->setDitherImage(ditherImage);
130    bitmap->setPixelRef(pr)->unref();
131    pr->isOpaque(bitmap);
132    return pr;
133}
134
135static SkBitmap::Config configForScaledOutput(SkBitmap::Config config) {
136    switch (config) {
137        case SkBitmap::kNo_Config:
138        case SkBitmap::kIndex8_Config:
139        case SkBitmap::kRLE_Index8_Config:
140            return SkBitmap::kARGB_8888_Config;
141        default:
142            break;
143    }
144    return config;
145}
146
147class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
148public:
149    ScaleCheckingAllocator(float scale, int size)
150            : mScale(scale), mSize(size) {
151    }
152
153    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
154        // accounts for scale in final allocation, using eventual size and config
155        const int bytesPerPixel = SkBitmap::ComputeBytesPerPixel(
156                configForScaledOutput(bitmap->getConfig()));
157        const int requestedSize = bytesPerPixel *
158                int(bitmap->width() * mScale + 0.5f) *
159                int(bitmap->height() * mScale + 0.5f);
160        if (requestedSize > mSize) {
161            ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
162                    mSize, requestedSize);
163            return false;
164        }
165        return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
166    }
167private:
168    const float mScale;
169    const int mSize;
170};
171
172class RecyclingPixelAllocator : public SkBitmap::Allocator {
173public:
174    RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
175            : mPixelRef(pixelRef), mSize(size) {
176        SkSafeRef(mPixelRef);
177    }
178
179    ~RecyclingPixelAllocator() {
180        SkSafeUnref(mPixelRef);
181    }
182
183    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
184        if (!bitmap->getSize64().is32() || bitmap->getSize() > mSize) {
185            ALOGW("bitmap marked for reuse (%d bytes) can't fit new bitmap (%d bytes)",
186                    mSize, bitmap->getSize());
187            return false;
188        }
189        bitmap->setPixelRef(mPixelRef);
190        bitmap->lockPixels();
191        return true;
192    }
193
194private:
195    SkPixelRef* const mPixelRef;
196    const unsigned int mSize;
197};
198
199// since we "may" create a purgeable imageref, we require the stream be ref'able
200// i.e. dynamically allocated, since its lifetime may exceed the current stack
201// frame.
202static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
203        jobject options, bool allowPurgeable, bool forcePurgeable = false) {
204
205    int sampleSize = 1;
206
207    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
208    SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
209
210    bool doDither = true;
211    bool isMutable = false;
212    float scale = 1.0f;
213    bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
214    bool preferQualityOverSpeed = false;
215
216    jobject javaBitmap = NULL;
217
218    if (options != NULL) {
219        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
220        if (optionsJustBounds(env, options)) {
221            mode = SkImageDecoder::kDecodeBounds_Mode;
222        }
223
224        // initialize these, in case we fail later on
225        env->SetIntField(options, gOptions_widthFieldID, -1);
226        env->SetIntField(options, gOptions_heightFieldID, -1);
227        env->SetObjectField(options, gOptions_mimeFieldID, 0);
228
229        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
230        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
231        isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
232        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
233        preferQualityOverSpeed = env->GetBooleanField(options,
234                gOptions_preferQualityOverSpeedFieldID);
235        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
236
237        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
238            const int density = env->GetIntField(options, gOptions_densityFieldID);
239            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
240            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
241            if (density != 0 && targetDensity != 0 && density != screenDensity) {
242                scale = (float) targetDensity / density;
243            }
244        }
245    }
246
247    const bool willScale = scale != 1.0f;
248    isPurgeable &= !willScale;
249
250    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
251    if (decoder == NULL) {
252        return nullObjectReturn("SkImageDecoder::Factory returned null");
253    }
254
255    decoder->setSampleSize(sampleSize);
256    decoder->setDitherImage(doDither);
257    decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
258
259    SkBitmap* outputBitmap = NULL;
260    unsigned int existingBufferSize = 0;
261    if (javaBitmap != NULL) {
262        outputBitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
263        if (outputBitmap->isImmutable()) {
264            ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
265            javaBitmap = NULL;
266            outputBitmap = NULL;
267        } else {
268            existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
269        }
270    }
271
272    SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
273    if (outputBitmap == NULL) outputBitmap = adb.get();
274
275    NinePatchPeeker peeker(decoder);
276    decoder->setPeeker(&peeker);
277
278    SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
279
280    JavaPixelAllocator javaAllocator(env);
281    RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
282    ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
283    SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
284            (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
285    if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
286        if (!willScale) {
287            decoder->setAllocator(outputAllocator);
288        } else if (javaBitmap != NULL) {
289            // check for eventual scaled bounds at allocation time, so we don't decode the bitmap
290            // only to find the scaled result too large to fit in the allocation
291            decoder->setAllocator(&scaleCheckingAllocator);
292        }
293    }
294
295    // Only setup the decoder to be deleted after its stack-based, refcounted
296    // components (allocators, peekers, etc) are declared. This prevents RefCnt
297    // asserts from firing due to the order objects are deleted from the stack.
298    SkAutoTDelete<SkImageDecoder> add(decoder);
299
300    AutoDecoderCancel adc(options, decoder);
301
302    // To fix the race condition in case "requestCancelDecode"
303    // happens earlier than AutoDecoderCancel object is added
304    // to the gAutoDecoderCancelMutex linked list.
305    if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
306        return nullObjectReturn("gOptions_mCancelID");
307    }
308
309    SkBitmap decodingBitmap;
310    if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
311        return nullObjectReturn("decoder->decode returned false");
312    }
313
314    int scaledWidth = decodingBitmap.width();
315    int scaledHeight = decodingBitmap.height();
316
317    if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
318        scaledWidth = int(scaledWidth * scale + 0.5f);
319        scaledHeight = int(scaledHeight * scale + 0.5f);
320    }
321
322    // update options (if any)
323    if (options != NULL) {
324        env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
325        env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
326        env->SetObjectField(options, gOptions_mimeFieldID,
327                getMimeTypeString(env, decoder->getFormat()));
328    }
329
330    // if we're in justBounds mode, return now (skip the java bitmap)
331    if (mode == SkImageDecoder::kDecodeBounds_Mode) {
332        return NULL;
333    }
334
335    jbyteArray ninePatchChunk = NULL;
336    if (peeker.fPatch != NULL) {
337        if (willScale) {
338            scaleNinePatchChunk(peeker.fPatch, scale);
339        }
340
341        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
342        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
343        if (ninePatchChunk == NULL) {
344            return nullObjectReturn("ninePatchChunk == null");
345        }
346
347        jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
348        if (array == NULL) {
349            return nullObjectReturn("primitive array == null");
350        }
351
352        peeker.fPatch->serialize(array);
353        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
354    }
355
356    jintArray layoutBounds = NULL;
357    if (peeker.fLayoutBounds != NULL) {
358        layoutBounds = env->NewIntArray(4);
359        if (layoutBounds == NULL) {
360            return nullObjectReturn("layoutBounds == null");
361        }
362
363        jint scaledBounds[4];
364        if (willScale) {
365            for (int i=0; i<4; i++) {
366                scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f);
367            }
368        } else {
369            memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds));
370        }
371        env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds);
372        if (javaBitmap != NULL) {
373            env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
374        }
375    }
376
377    if (willScale) {
378        // This is weird so let me explain: we could use the scale parameter
379        // directly, but for historical reasons this is how the corresponding
380        // Dalvik code has always behaved. We simply recreate the behavior here.
381        // The result is slightly different from simply using scale because of
382        // the 0.5f rounding bias applied when computing the target image size
383        const float sx = scaledWidth / float(decodingBitmap.width());
384        const float sy = scaledHeight / float(decodingBitmap.height());
385
386        // TODO: avoid copying when scaled size equals decodingBitmap size
387        SkBitmap::Config config = configForScaledOutput(decodingBitmap.config());
388        outputBitmap->setConfig(config, scaledWidth, scaledHeight);
389        outputBitmap->setIsOpaque(decodingBitmap.isOpaque());
390        if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
391            return nullObjectReturn("allocation failed for scaled bitmap");
392        }
393        outputBitmap->eraseColor(0);
394
395        SkPaint paint;
396        paint.setFilterBitmap(true);
397
398        SkCanvas canvas(*outputBitmap);
399        canvas.scale(sx, sy);
400        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
401    } else {
402        outputBitmap->swap(decodingBitmap);
403    }
404
405    if (padding) {
406        if (peeker.fPatch != NULL) {
407            GraphicsJNI::set_jrect(env, padding,
408                    peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
409                    peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
410        } else {
411            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
412        }
413    }
414
415    SkPixelRef* pr;
416    if (isPurgeable) {
417        pr = installPixelRef(outputBitmap, stream, sampleSize, doDither);
418    } else {
419        // if we get here, we're in kDecodePixels_Mode and will therefore
420        // already have a pixelref installed.
421        pr = outputBitmap->pixelRef();
422    }
423    if (pr == NULL) {
424        return nullObjectReturn("Got null SkPixelRef");
425    }
426
427    if (!isMutable && javaBitmap == NULL) {
428        // promise we will never change our pixels (great for sharing and pictures)
429        pr->setImmutable();
430    }
431
432    // detach bitmap from its autodeleter, since we want to own it now
433    adb.detach();
434
435    if (javaBitmap != NULL) {
436        GraphicsJNI::reinitBitmap(env, javaBitmap);
437        outputBitmap->notifyPixelsChanged();
438        // If a java bitmap was passed in for reuse, pass it back
439        return javaBitmap;
440    }
441    // now create the java bitmap
442    return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
443            isMutable, ninePatchChunk, layoutBounds, -1);
444}
445
446static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
447        jobject padding, jobject options) {
448
449    jobject bitmap = NULL;
450    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
451
452    if (stream) {
453        // for now we don't allow purgeable with java inputstreams
454        bitmap = doDecode(env, stream, padding, options, false, false);
455        stream->unref();
456    }
457    return bitmap;
458}
459
460static ssize_t getFDSize(int fd) {
461    off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
462    if (curr < 0) {
463        return 0;
464    }
465    size_t size = ::lseek(fd, 0, SEEK_END);
466    ::lseek64(fd, curr, SEEK_SET);
467    return size;
468}
469
470static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
471        jobject padding, jobject bitmapFactoryOptions) {
472
473    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
474
475    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
476
477    bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
478    bool isShareable = optionsShareable(env, bitmapFactoryOptions);
479    bool weOwnTheFD = false;
480    if (isPurgeable && isShareable) {
481        int newFD = ::dup(descriptor);
482        if (-1 != newFD) {
483            weOwnTheFD = true;
484            descriptor = newFD;
485        }
486    }
487
488    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
489    SkAutoUnref aur(stream);
490    if (!stream->isValid()) {
491        return NULL;
492    }
493
494    /* Restore our offset when we leave, so we can be called more than once
495       with the same descriptor. This is only required if we didn't dup the
496       file descriptor, but it is OK to do it all the time.
497    */
498    AutoFDSeek as(descriptor);
499
500    /* Allow purgeable iff we own the FD, i.e., in the puregeable and
501       shareable case.
502    */
503    return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
504}
505
506/*  make a deep copy of the asset, and return it as a stream, or NULL if there
507    was an error.
508 */
509static SkStream* copyAssetToStream(Asset* asset) {
510    // if we could "ref/reopen" the asset, we may not need to copy it here
511    off64_t size = asset->seek(0, SEEK_SET);
512    if ((off64_t)-1 == size) {
513        SkDebugf("---- copyAsset: asset rewind failed\n");
514        return NULL;
515    }
516
517    size = asset->getLength();
518    if (size <= 0) {
519        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
520        return NULL;
521    }
522
523    SkStream* stream = new SkMemoryStream(size);
524    void* data = const_cast<void*>(stream->getMemoryBase());
525    off64_t len = asset->read(data, size);
526    if (len != size) {
527        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
528        delete stream;
529        stream = NULL;
530    }
531    return stream;
532}
533
534static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset,
535        jobject padding, jobject options) {
536
537    SkStream* stream;
538    Asset* asset = reinterpret_cast<Asset*>(native_asset);
539    bool forcePurgeable = optionsPurgeable(env, options);
540    if (forcePurgeable) {
541        // if we could "ref/reopen" the asset, we may not need to copy it here
542        // and we could assume optionsShareable, since assets are always RO
543        stream = copyAssetToStream(asset);
544        if (stream == NULL) {
545            return NULL;
546        }
547    } else {
548        // since we know we'll be done with the asset when we return, we can
549        // just use a simple wrapper
550        stream = new AssetStreamAdaptor(asset);
551    }
552    SkAutoUnref aur(stream);
553    return doDecode(env, stream, padding, options, true, forcePurgeable);
554}
555
556static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
557        int offset, int length, jobject options) {
558
559    /*  If optionsShareable() we could decide to just wrap the java array and
560        share it, but that means adding a globalref to the java array object
561        and managing its lifetime. For now we just always copy the array's data
562        if optionsPurgeable(), unless we're just decoding bounds.
563     */
564    bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
565    AutoJavaByteArray ar(env, byteArray);
566    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
567    SkAutoUnref aur(stream);
568    return doDecode(env, stream, NULL, options, purgeable);
569}
570
571static void nativeRequestCancel(JNIEnv*, jobject joptions) {
572    (void)AutoDecoderCancel::RequestCancel(joptions);
573}
574
575static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
576    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
577    return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
578}
579
580///////////////////////////////////////////////////////////////////////////////
581
582static JNINativeMethod gMethods[] = {
583    {   "nativeDecodeStream",
584        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
585        (void*)nativeDecodeStream
586    },
587
588    {   "nativeDecodeFileDescriptor",
589        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
590        (void*)nativeDecodeFileDescriptor
591    },
592
593    {   "nativeDecodeAsset",
594        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
595        (void*)nativeDecodeAsset
596    },
597
598    {   "nativeDecodeByteArray",
599        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
600        (void*)nativeDecodeByteArray
601    },
602
603    {   "nativeIsSeekable",
604        "(Ljava/io/FileDescriptor;)Z",
605        (void*)nativeIsSeekable
606    },
607};
608
609static JNINativeMethod gOptionsMethods[] = {
610    {   "requestCancel", "()V", (void*)nativeRequestCancel }
611};
612
613static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
614                                const char fieldname[], const char type[]) {
615    jfieldID id = env->GetFieldID(clazz, fieldname, type);
616    SkASSERT(id);
617    return id;
618}
619
620int register_android_graphics_BitmapFactory(JNIEnv* env) {
621    jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
622    SkASSERT(options_class);
623    gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
624        "Landroid/graphics/Bitmap;");
625    gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
626    gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
627    gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
628            "Landroid/graphics/Bitmap$Config;");
629    gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
630    gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
631    gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
632    gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
633    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
634            "inPreferQualityOverSpeed", "Z");
635    gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
636    gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
637    gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
638    gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
639    gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
640    gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
641    gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
642    gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
643
644    jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
645    SkASSERT(bitmap_class);
646    gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
647    gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
648    int ret = AndroidRuntime::registerNativeMethods(env,
649                                    "android/graphics/BitmapFactory$Options",
650                                    gOptionsMethods,
651                                    SK_ARRAY_COUNT(gOptionsMethods));
652    if (ret) {
653        return ret;
654    }
655    return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
656                                         gMethods, SK_ARRAY_COUNT(gMethods));
657}
658