BitmapFactory.cpp revision 3a091b79978caa9b5d58ae19f693279e5a717c2a
1#define LOG_TAG "BitmapFactory"
2
3#include "BitmapFactory.h"
4#include "NinePatchPeeker.h"
5#include "SkFrontBufferedStream.h"
6#include "SkImageDecoder.h"
7#include "SkMath.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#include "GraphicsJNI.h"
17
18#include <android_runtime/AndroidRuntime.h>
19#include <androidfw/Asset.h>
20#include <androidfw/ResourceTypes.h>
21#include <cutils/compiler.h>
22#include <netinet/in.h>
23#include <stdio.h>
24#include <sys/mman.h>
25#include <sys/stat.h>
26
27jfieldID gOptions_justBoundsFieldID;
28jfieldID gOptions_sampleSizeFieldID;
29jfieldID gOptions_configFieldID;
30jfieldID gOptions_premultipliedFieldID;
31jfieldID gOptions_mutableFieldID;
32jfieldID gOptions_ditherFieldID;
33jfieldID gOptions_preferQualityOverSpeedFieldID;
34jfieldID gOptions_scaledFieldID;
35jfieldID gOptions_densityFieldID;
36jfieldID gOptions_screenDensityFieldID;
37jfieldID gOptions_targetDensityFieldID;
38jfieldID gOptions_widthFieldID;
39jfieldID gOptions_heightFieldID;
40jfieldID gOptions_mimeFieldID;
41jfieldID gOptions_mCancelID;
42jfieldID gOptions_bitmapFieldID;
43
44jfieldID gBitmap_nativeBitmapFieldID;
45jfieldID gBitmap_ninePatchInsetsFieldID;
46
47jclass gInsetStruct_class;
48jmethodID gInsetStruct_constructorMethodID;
49
50using namespace android;
51
52jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
53    static const struct {
54        SkImageDecoder::Format fFormat;
55        const char*            fMimeType;
56    } gMimeTypes[] = {
57        { SkImageDecoder::kBMP_Format,  "image/bmp" },
58        { SkImageDecoder::kGIF_Format,  "image/gif" },
59        { SkImageDecoder::kICO_Format,  "image/x-ico" },
60        { SkImageDecoder::kJPEG_Format, "image/jpeg" },
61        { SkImageDecoder::kPNG_Format,  "image/png" },
62        { SkImageDecoder::kWEBP_Format, "image/webp" },
63        { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
64    };
65
66    const char* cstr = NULL;
67    for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
68        if (gMimeTypes[i].fFormat == format) {
69            cstr = gMimeTypes[i].fMimeType;
70            break;
71        }
72    }
73
74    jstring jstr = 0;
75    if (NULL != cstr) {
76        jstr = env->NewStringUTF(cstr);
77    }
78    return jstr;
79}
80
81static bool optionsJustBounds(JNIEnv* env, jobject options) {
82    return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
83}
84
85static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
86    for (int i = 0; i < count; i++) {
87        divs[i] = int32_t(divs[i] * scale + 0.5f);
88        if (i > 0 && divs[i] == divs[i - 1]) {
89            divs[i]++; // avoid collisions
90        }
91    }
92
93    if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
94        // if the collision avoidance above put some divs outside the bounds of the bitmap,
95        // slide outer stretchable divs inward to stay within bounds
96        int highestAvailable = maxValue;
97        for (int i = count - 1; i >= 0; i--) {
98            divs[i] = highestAvailable;
99            if (i > 0 && divs[i] <= divs[i-1]){
100                // keep shifting
101                highestAvailable = divs[i] - 1;
102            } else {
103                break;
104            }
105        }
106    }
107}
108
109static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale,
110        int scaledWidth, int scaledHeight) {
111    chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
112    chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
113    chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
114    chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
115
116    scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth);
117    scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight);
118}
119
120static SkColorType colorTypeForScaledOutput(SkColorType colorType) {
121    switch (colorType) {
122        case kUnknown_SkColorType:
123        case kIndex_8_SkColorType:
124            return kN32_SkColorType;
125        default:
126            break;
127    }
128    return colorType;
129}
130
131class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
132public:
133    ScaleCheckingAllocator(float scale, int size)
134            : mScale(scale), mSize(size) {
135    }
136
137    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
138        // accounts for scale in final allocation, using eventual size and config
139        const int bytesPerPixel = SkColorTypeBytesPerPixel(
140                colorTypeForScaledOutput(bitmap->colorType()));
141        const int requestedSize = bytesPerPixel *
142                int(bitmap->width() * mScale + 0.5f) *
143                int(bitmap->height() * mScale + 0.5f);
144        if (requestedSize > mSize) {
145            ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
146                    mSize, requestedSize);
147            return false;
148        }
149        return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
150    }
151private:
152    const float mScale;
153    const int mSize;
154};
155
156class RecyclingPixelAllocator : public SkBitmap::Allocator {
157public:
158    RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
159            : mPixelRef(pixelRef), mSize(size) {
160        SkSafeRef(mPixelRef);
161    }
162
163    ~RecyclingPixelAllocator() {
164        SkSafeUnref(mPixelRef);
165    }
166
167    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
168        const SkImageInfo& info = bitmap->info();
169        if (info.fColorType == kUnknown_SkColorType) {
170            ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
171            return false;
172        }
173
174        const int64_t size64 = info.getSafeSize64(bitmap->rowBytes());
175        if (!sk_64_isS32(size64)) {
176            ALOGW("bitmap is too large");
177            return false;
178        }
179
180        const size_t size = sk_64_asS32(size64);
181        if (size > mSize) {
182            ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap "
183                  "(%zu bytes)", mSize, size);
184            return false;
185        }
186
187        // Create a new pixelref with the new ctable that wraps the previous pixelref
188        SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef),
189                info, bitmap->rowBytes(), ctable);
190
191        bitmap->setPixelRef(pr)->unref();
192        // since we're already allocated, we lockPixels right away
193        // HeapAllocator/JavaPixelAllocator behaves this way too
194        bitmap->lockPixels();
195        return true;
196    }
197
198private:
199    SkPixelRef* const mPixelRef;
200    const unsigned int mSize;
201};
202
203static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
204
205    int sampleSize = 1;
206
207    SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
208    SkColorType prefColorType = kN32_SkColorType;
209
210    bool doDither = true;
211    bool isMutable = false;
212    float scale = 1.0f;
213    bool preferQualityOverSpeed = false;
214    bool requireUnpremultiplied = false;
215
216    jobject javaBitmap = NULL;
217
218    if (options != NULL) {
219        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
220        if (optionsJustBounds(env, options)) {
221            decodeMode = 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        prefColorType = GraphicsJNI::getNativeBitmapColorType(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        requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
236        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
237
238        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
239            const int density = env->GetIntField(options, gOptions_densityFieldID);
240            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
241            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
242            if (density != 0 && targetDensity != 0 && density != screenDensity) {
243                scale = (float) targetDensity / density;
244            }
245        }
246    }
247
248    const bool willScale = scale != 1.0f;
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    decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
259
260    SkBitmap* outputBitmap = NULL;
261    unsigned int existingBufferSize = 0;
262    if (javaBitmap != NULL) {
263        outputBitmap = (SkBitmap*) env->GetLongField(javaBitmap, gBitmap_nativeBitmapFieldID);
264        if (outputBitmap->isImmutable()) {
265            ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
266            javaBitmap = NULL;
267            outputBitmap = NULL;
268        } else {
269            existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
270        }
271    }
272
273    SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
274    if (outputBitmap == NULL) outputBitmap = adb.get();
275
276    NinePatchPeeker peeker(decoder);
277    decoder->setPeeker(&peeker);
278
279    JavaPixelAllocator javaAllocator(env);
280    RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
281    ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
282    SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
283            (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
284    if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
285        if (!willScale) {
286            // If the java allocator is being used to allocate the pixel memory, the decoder
287            // need not write zeroes, since the memory is initialized to 0.
288            decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator);
289            decoder->setAllocator(outputAllocator);
290        } else if (javaBitmap != NULL) {
291            // check for eventual scaled bounds at allocation time, so we don't decode the bitmap
292            // only to find the scaled result too large to fit in the allocation
293            decoder->setAllocator(&scaleCheckingAllocator);
294        }
295    }
296
297    // Only setup the decoder to be deleted after its stack-based, refcounted
298    // components (allocators, peekers, etc) are declared. This prevents RefCnt
299    // asserts from firing due to the order objects are deleted from the stack.
300    SkAutoTDelete<SkImageDecoder> add(decoder);
301
302    AutoDecoderCancel adc(options, decoder);
303
304    // To fix the race condition in case "requestCancelDecode"
305    // happens earlier than AutoDecoderCancel object is added
306    // to the gAutoDecoderCancelMutex linked list.
307    if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
308        return nullObjectReturn("gOptions_mCancelID");
309    }
310
311    SkBitmap decodingBitmap;
312    if (decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)
313                != SkImageDecoder::kSuccess) {
314        return nullObjectReturn("decoder->decode returned false");
315    }
316
317    int scaledWidth = decodingBitmap.width();
318    int scaledHeight = decodingBitmap.height();
319
320    if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
321        scaledWidth = int(scaledWidth * scale + 0.5f);
322        scaledHeight = int(scaledHeight * scale + 0.5f);
323    }
324
325    // update options (if any)
326    if (options != NULL) {
327        env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
328        env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
329        env->SetObjectField(options, gOptions_mimeFieldID,
330                getMimeTypeString(env, decoder->getFormat()));
331    }
332
333    // if we're in justBounds mode, return now (skip the java bitmap)
334    if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) {
335        return NULL;
336    }
337
338    jbyteArray ninePatchChunk = NULL;
339    if (peeker.mPatch != NULL) {
340        if (willScale) {
341            scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight);
342        }
343
344        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
345        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
346        if (ninePatchChunk == NULL) {
347            return nullObjectReturn("ninePatchChunk == null");
348        }
349
350        jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
351        if (array == NULL) {
352            return nullObjectReturn("primitive array == null");
353        }
354
355        memcpy(array, peeker.mPatch, peeker.mPatchSize);
356        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
357    }
358
359    jobject ninePatchInsets = NULL;
360    if (peeker.mHasInsets) {
361        ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
362                peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
363                peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
364                peeker.mOutlineRadius, peeker.mOutlineAlpha, scale);
365        if (ninePatchInsets == NULL) {
366            return nullObjectReturn("nine patch insets == null");
367        }
368        if (javaBitmap != NULL) {
369            env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
370        }
371    }
372
373    if (willScale) {
374        // This is weird so let me explain: we could use the scale parameter
375        // directly, but for historical reasons this is how the corresponding
376        // Dalvik code has always behaved. We simply recreate the behavior here.
377        // The result is slightly different from simply using scale because of
378        // the 0.5f rounding bias applied when computing the target image size
379        const float sx = scaledWidth / float(decodingBitmap.width());
380        const float sy = scaledHeight / float(decodingBitmap.height());
381
382        // TODO: avoid copying when scaled size equals decodingBitmap size
383        SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType());
384        // FIXME: If the alphaType is kUnpremul and the image has alpha, the
385        // colors may not be correct, since Skia does not yet support drawing
386        // to/from unpremultiplied bitmaps.
387        outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
388                colorType, decodingBitmap.alphaType()));
389        if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
390            return nullObjectReturn("allocation failed for scaled bitmap");
391        }
392
393        // If outputBitmap's pixels are newly allocated by Java, there is no need
394        // to erase to 0, since the pixels were initialized to 0.
395        if (outputAllocator != &javaAllocator) {
396            outputBitmap->eraseColor(0);
397        }
398
399        SkPaint paint;
400        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
401
402        SkCanvas canvas(*outputBitmap);
403        canvas.scale(sx, sy);
404        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
405    } else {
406        outputBitmap->swap(decodingBitmap);
407    }
408
409    if (padding) {
410        if (peeker.mPatch != NULL) {
411            GraphicsJNI::set_jrect(env, padding,
412                    peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop,
413                    peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom);
414        } else {
415            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
416        }
417    }
418
419    // if we get here, we're in kDecodePixels_Mode and will therefore
420    // already have a pixelref installed.
421    if (outputBitmap->pixelRef() == NULL) {
422        return nullObjectReturn("Got null SkPixelRef");
423    }
424
425    if (!isMutable && javaBitmap == NULL) {
426        // promise we will never change our pixels (great for sharing and pictures)
427        outputBitmap->setImmutable();
428    }
429
430    // detach bitmap from its autodeleter, since we want to own it now
431    adb.detach();
432
433    if (javaBitmap != NULL) {
434        bool isPremultiplied = !requireUnpremultiplied;
435        GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied);
436        outputBitmap->notifyPixelsChanged();
437        // If a java bitmap was passed in for reuse, pass it back
438        return javaBitmap;
439    }
440
441    int bitmapCreateFlags = 0x0;
442    if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
443    if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
444
445    // now create the java bitmap
446    return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
447            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
448}
449
450// Need to buffer enough input to be able to rewind as much as might be read by a decoder
451// trying to determine the stream's format. Currently the most is 64, read by
452// SkImageDecoder_libwebp.
453// FIXME: Get this number from SkImageDecoder
454#define BYTES_TO_BUFFER 64
455
456static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
457        jobject padding, jobject options) {
458
459    jobject bitmap = NULL;
460    SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
461
462    if (stream.get()) {
463        SkAutoTUnref<SkStreamRewindable> bufferedStream(
464                SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER));
465        SkASSERT(bufferedStream.get() != NULL);
466        bitmap = doDecode(env, bufferedStream, padding, options);
467    }
468    return bitmap;
469}
470
471static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
472        jobject padding, jobject bitmapFactoryOptions) {
473
474    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
475
476    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
477
478    struct stat fdStat;
479    if (fstat(descriptor, &fdStat) == -1) {
480        doThrowIOE(env, "broken file descriptor");
481        return nullObjectReturn("fstat return -1");
482    }
483
484    // Restore the descriptor's offset on exiting this function. Even though
485    // we dup the descriptor, both the original and dup refer to the same open
486    // file description and changes to the file offset in one impact the other.
487    AutoFDSeek autoRestore(descriptor);
488
489    // Duplicate the descriptor here to prevent leaking memory. A leak occurs
490    // if we only close the file descriptor and not the file object it is used to
491    // create.  If we don't explicitly clean up the file (which in turn closes the
492    // descriptor) the buffers allocated internally by fseek will be leaked.
493    int dupDescriptor = dup(descriptor);
494
495    FILE* file = fdopen(dupDescriptor, "r");
496    if (file == NULL) {
497        // cleanup the duplicated descriptor since it will not be closed when the
498        // file is cleaned up (fclose).
499        close(dupDescriptor);
500        return nullObjectReturn("Could not open file");
501    }
502
503    SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,
504                         SkFILEStream::kCallerPasses_Ownership));
505
506    // Use a buffered stream. Although an SkFILEStream can be rewound, this
507    // ensures that SkImageDecoder::Factory never rewinds beyond the
508    // current position of the file descriptor.
509    SkAutoTUnref<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream,
510            BYTES_TO_BUFFER));
511
512    return doDecode(env, stream, padding, bitmapFactoryOptions);
513}
514
515static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
516        jobject padding, jobject options) {
517
518    Asset* asset = reinterpret_cast<Asset*>(native_asset);
519    // since we know we'll be done with the asset when we return, we can
520    // just use a simple wrapper
521    SkAutoTUnref<SkStreamRewindable> stream(new AssetStreamAdaptor(asset,
522            AssetStreamAdaptor::kNo_OwnAsset, AssetStreamAdaptor::kNo_HasMemoryBase));
523    return doDecode(env, stream, padding, options);
524}
525
526static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
527        jint offset, jint length, jobject options) {
528
529    AutoJavaByteArray ar(env, byteArray);
530    SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
531    SkAutoUnref aur(stream);
532    return doDecode(env, stream, NULL, options);
533}
534
535static void nativeRequestCancel(JNIEnv*, jobject joptions) {
536    (void)AutoDecoderCancel::RequestCancel(joptions);
537}
538
539static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
540    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
541    return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
542}
543
544///////////////////////////////////////////////////////////////////////////////
545
546static JNINativeMethod gMethods[] = {
547    {   "nativeDecodeStream",
548        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
549        (void*)nativeDecodeStream
550    },
551
552    {   "nativeDecodeFileDescriptor",
553        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
554        (void*)nativeDecodeFileDescriptor
555    },
556
557    {   "nativeDecodeAsset",
558        "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
559        (void*)nativeDecodeAsset
560    },
561
562    {   "nativeDecodeByteArray",
563        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
564        (void*)nativeDecodeByteArray
565    },
566
567    {   "nativeIsSeekable",
568        "(Ljava/io/FileDescriptor;)Z",
569        (void*)nativeIsSeekable
570    },
571};
572
573static JNINativeMethod gOptionsMethods[] = {
574    {   "requestCancel", "()V", (void*)nativeRequestCancel }
575};
576
577static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
578                                const char fieldname[], const char type[]) {
579    jfieldID id = env->GetFieldID(clazz, fieldname, type);
580    SkASSERT(id);
581    return id;
582}
583
584int register_android_graphics_BitmapFactory(JNIEnv* env) {
585    jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
586    SkASSERT(options_class);
587    gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
588            "Landroid/graphics/Bitmap;");
589    gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
590    gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
591    gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
592            "Landroid/graphics/Bitmap$Config;");
593    gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z");
594    gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
595    gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
596    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
597            "inPreferQualityOverSpeed", "Z");
598    gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
599    gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
600    gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
601    gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
602    gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
603    gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
604    gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
605    gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
606
607    jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
608    SkASSERT(bitmap_class);
609    gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "J");
610    gBitmap_ninePatchInsetsFieldID = getFieldIDCheck(env, bitmap_class, "mNinePatchInsets",
611            "Landroid/graphics/NinePatch$InsetStruct;");
612
613    gInsetStruct_class = (jclass) env->NewGlobalRef(env->FindClass("android/graphics/NinePatch$InsetStruct"));
614    gInsetStruct_constructorMethodID = env->GetMethodID(gInsetStruct_class, "<init>", "(IIIIIIIIFIF)V");
615
616    int ret = AndroidRuntime::registerNativeMethods(env,
617                                    "android/graphics/BitmapFactory$Options",
618                                    gOptionsMethods,
619                                    SK_ARRAY_COUNT(gOptionsMethods));
620    if (ret) {
621        return ret;
622    }
623    return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
624                                         gMethods, SK_ARRAY_COUNT(gMethods));
625}
626