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