BitmapFactory.cpp revision 46d8444631b4b1253a76bfcc78a29d26014d022f
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        return nullObjectReturn("decoder->decode returned false");
314    }
315
316    int scaledWidth = decodingBitmap.width();
317    int scaledHeight = decodingBitmap.height();
318
319    if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
320        scaledWidth = int(scaledWidth * scale + 0.5f);
321        scaledHeight = int(scaledHeight * scale + 0.5f);
322    }
323
324    // update options (if any)
325    if (options != NULL) {
326        env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
327        env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
328        env->SetObjectField(options, gOptions_mimeFieldID,
329                getMimeTypeString(env, decoder->getFormat()));
330    }
331
332    // if we're in justBounds mode, return now (skip the java bitmap)
333    if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) {
334        return NULL;
335    }
336
337    jbyteArray ninePatchChunk = NULL;
338    if (peeker.mPatch != NULL) {
339        if (willScale) {
340            scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight);
341        }
342
343        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
344        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
345        if (ninePatchChunk == NULL) {
346            return nullObjectReturn("ninePatchChunk == null");
347        }
348
349        jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
350        if (array == NULL) {
351            return nullObjectReturn("primitive array == null");
352        }
353
354        memcpy(array, peeker.mPatch, peeker.mPatchSize);
355        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
356    }
357
358    jobject ninePatchInsets = NULL;
359    if (peeker.mHasInsets) {
360        ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
361                peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
362                peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
363                peeker.mOutlineRadius, peeker.mOutlineAlpha, scale);
364        if (ninePatchInsets == NULL) {
365            return nullObjectReturn("nine patch insets == null");
366        }
367        if (javaBitmap != NULL) {
368            env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
369        }
370    }
371
372    if (willScale) {
373        // This is weird so let me explain: we could use the scale parameter
374        // directly, but for historical reasons this is how the corresponding
375        // Dalvik code has always behaved. We simply recreate the behavior here.
376        // The result is slightly different from simply using scale because of
377        // the 0.5f rounding bias applied when computing the target image size
378        const float sx = scaledWidth / float(decodingBitmap.width());
379        const float sy = scaledHeight / float(decodingBitmap.height());
380
381        // TODO: avoid copying when scaled size equals decodingBitmap size
382        SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType());
383        // FIXME: If the alphaType is kUnpremul and the image has alpha, the
384        // colors may not be correct, since Skia does not yet support drawing
385        // to/from unpremultiplied bitmaps.
386        outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
387                colorType, decodingBitmap.alphaType()));
388        if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
389            return nullObjectReturn("allocation failed for scaled bitmap");
390        }
391
392        // If outputBitmap's pixels are newly allocated by Java, there is no need
393        // to erase to 0, since the pixels were initialized to 0.
394        if (outputAllocator != &javaAllocator) {
395            outputBitmap->eraseColor(0);
396        }
397
398        SkPaint paint;
399        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
400
401        SkCanvas canvas(*outputBitmap);
402        canvas.scale(sx, sy);
403        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
404    } else {
405        outputBitmap->swap(decodingBitmap);
406    }
407
408    if (padding) {
409        if (peeker.mPatch != NULL) {
410            GraphicsJNI::set_jrect(env, padding,
411                    peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop,
412                    peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom);
413        } else {
414            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
415        }
416    }
417
418    // if we get here, we're in kDecodePixels_Mode and will therefore
419    // already have a pixelref installed.
420    if (outputBitmap->pixelRef() == NULL) {
421        return nullObjectReturn("Got null SkPixelRef");
422    }
423
424    if (!isMutable && javaBitmap == NULL) {
425        // promise we will never change our pixels (great for sharing and pictures)
426        outputBitmap->setImmutable();
427    }
428
429    // detach bitmap from its autodeleter, since we want to own it now
430    adb.detach();
431
432    if (javaBitmap != NULL) {
433        bool isPremultiplied = !requireUnpremultiplied;
434        GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied);
435        outputBitmap->notifyPixelsChanged();
436        // If a java bitmap was passed in for reuse, pass it back
437        return javaBitmap;
438    }
439
440    int bitmapCreateFlags = 0x0;
441    if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
442    if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
443
444    // now create the java bitmap
445    return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
446            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
447}
448
449// Need to buffer enough input to be able to rewind as much as might be read by a decoder
450// trying to determine the stream's format. Currently the most is 64, read by
451// SkImageDecoder_libwebp.
452// FIXME: Get this number from SkImageDecoder
453#define BYTES_TO_BUFFER 64
454
455static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
456        jobject padding, jobject options) {
457
458    jobject bitmap = NULL;
459    SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
460
461    if (stream.get()) {
462        SkAutoTUnref<SkStreamRewindable> bufferedStream(
463                SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER));
464        SkASSERT(bufferedStream.get() != NULL);
465        bitmap = doDecode(env, bufferedStream, padding, options);
466    }
467    return bitmap;
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    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
476
477    struct stat fdStat;
478    if (fstat(descriptor, &fdStat) == -1) {
479        doThrowIOE(env, "broken file descriptor");
480        return nullObjectReturn("fstat return -1");
481    }
482
483    // Restore the descriptor's offset on exiting this function. Even though
484    // we dup the descriptor, both the original and dup refer to the same open
485    // file description and changes to the file offset in one impact the other.
486    AutoFDSeek autoRestore(descriptor);
487
488    // Duplicate the descriptor here to prevent leaking memory. A leak occurs
489    // if we only close the file descriptor and not the file object it is used to
490    // create.  If we don't explicitly clean up the file (which in turn closes the
491    // descriptor) the buffers allocated internally by fseek will be leaked.
492    int dupDescriptor = dup(descriptor);
493
494    FILE* file = fdopen(dupDescriptor, "r");
495    if (file == NULL) {
496        // cleanup the duplicated descriptor since it will not be closed when the
497        // file is cleaned up (fclose).
498        close(dupDescriptor);
499        return nullObjectReturn("Could not open file");
500    }
501
502    SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,
503                         SkFILEStream::kCallerPasses_Ownership));
504
505    // Use a buffered stream. Although an SkFILEStream can be rewound, this
506    // ensures that SkImageDecoder::Factory never rewinds beyond the
507    // current position of the file descriptor.
508    SkAutoTUnref<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream,
509            BYTES_TO_BUFFER));
510
511    return doDecode(env, stream, padding, bitmapFactoryOptions);
512}
513
514static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
515        jobject padding, jobject options) {
516
517    Asset* asset = reinterpret_cast<Asset*>(native_asset);
518    // since we know we'll be done with the asset when we return, we can
519    // just use a simple wrapper
520    SkAutoTUnref<SkStreamRewindable> stream(new AssetStreamAdaptor(asset,
521            AssetStreamAdaptor::kNo_OwnAsset, AssetStreamAdaptor::kNo_HasMemoryBase));
522    return doDecode(env, stream, padding, options);
523}
524
525static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
526        jint offset, jint length, jobject options) {
527
528    AutoJavaByteArray ar(env, byteArray);
529    SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
530    SkAutoUnref aur(stream);
531    return doDecode(env, stream, NULL, options);
532}
533
534static void nativeRequestCancel(JNIEnv*, jobject joptions) {
535    (void)AutoDecoderCancel::RequestCancel(joptions);
536}
537
538static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
539    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
540    return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
541}
542
543///////////////////////////////////////////////////////////////////////////////
544
545static JNINativeMethod gMethods[] = {
546    {   "nativeDecodeStream",
547        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
548        (void*)nativeDecodeStream
549    },
550
551    {   "nativeDecodeFileDescriptor",
552        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
553        (void*)nativeDecodeFileDescriptor
554    },
555
556    {   "nativeDecodeAsset",
557        "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
558        (void*)nativeDecodeAsset
559    },
560
561    {   "nativeDecodeByteArray",
562        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
563        (void*)nativeDecodeByteArray
564    },
565
566    {   "nativeIsSeekable",
567        "(Ljava/io/FileDescriptor;)Z",
568        (void*)nativeIsSeekable
569    },
570};
571
572static JNINativeMethod gOptionsMethods[] = {
573    {   "requestCancel", "()V", (void*)nativeRequestCancel }
574};
575
576static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
577                                const char fieldname[], const char type[]) {
578    jfieldID id = env->GetFieldID(clazz, fieldname, type);
579    SkASSERT(id);
580    return id;
581}
582
583int register_android_graphics_BitmapFactory(JNIEnv* env) {
584    jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
585    SkASSERT(options_class);
586    gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
587            "Landroid/graphics/Bitmap;");
588    gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
589    gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
590    gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
591            "Landroid/graphics/Bitmap$Config;");
592    gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z");
593    gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
594    gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
595    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
596            "inPreferQualityOverSpeed", "Z");
597    gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
598    gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
599    gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
600    gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
601    gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
602    gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
603    gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
604    gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
605
606    jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
607    SkASSERT(bitmap_class);
608    gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "J");
609    gBitmap_ninePatchInsetsFieldID = getFieldIDCheck(env, bitmap_class, "mNinePatchInsets",
610            "Landroid/graphics/NinePatch$InsetStruct;");
611
612    gInsetStruct_class = (jclass) env->NewGlobalRef(env->FindClass("android/graphics/NinePatch$InsetStruct"));
613    gInsetStruct_constructorMethodID = env->GetMethodID(gInsetStruct_class, "<init>", "(IIIIIIIIFIF)V");
614
615    int ret = AndroidRuntime::registerNativeMethods(env,
616                                    "android/graphics/BitmapFactory$Options",
617                                    gOptionsMethods,
618                                    SK_ARRAY_COUNT(gOptionsMethods));
619    if (ret) {
620        return ret;
621    }
622    return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
623                                         gMethods, SK_ARRAY_COUNT(gMethods));
624}
625