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