BitmapFactory.cpp revision 5e2496bcee239ce9ebeff6022b7badf81d87492c
1#define LOG_TAG "BitmapFactory"
2
3#include "BitmapFactory.h"
4#include "CreateJavaOutputStreamAdaptor.h"
5#include "GraphicsJNI.h"
6#include "NinePatchPeeker.h"
7#include "SkAndroidCodec.h"
8#include "SkBRDAllocator.h"
9#include "SkFrontBufferedStream.h"
10#include "SkImageDecoder.h"
11#include "SkMath.h"
12#include "SkPixelRef.h"
13#include "SkStream.h"
14#include "SkUtils.h"
15#include "Utils.h"
16#include "core_jni_helpers.h"
17
18#include <JNIHelp.h>
19#include <androidfw/Asset.h>
20#include <androidfw/ResourceTypes.h>
21#include <cutils/compiler.h>
22#include <memory>
23#include <netinet/in.h>
24#include <stdio.h>
25#include <sys/mman.h>
26#include <sys/stat.h>
27
28jfieldID gOptions_justBoundsFieldID;
29jfieldID gOptions_sampleSizeFieldID;
30jfieldID gOptions_configFieldID;
31jfieldID gOptions_premultipliedFieldID;
32jfieldID gOptions_mutableFieldID;
33jfieldID gOptions_ditherFieldID;
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;
44
45jfieldID gBitmap_ninePatchInsetsFieldID;
46
47jclass gInsetStruct_class;
48jmethodID gInsetStruct_constructorMethodID;
49
50using namespace android;
51
52jstring encodedFormatToString(JNIEnv* env, SkEncodedFormat format) {
53    const char* mimeType;
54    switch (format) {
55        case SkEncodedFormat::kBMP_SkEncodedFormat:
56            mimeType = "image/bmp";
57            break;
58        case SkEncodedFormat::kGIF_SkEncodedFormat:
59            mimeType = "image/gif";
60            break;
61        case SkEncodedFormat::kICO_SkEncodedFormat:
62            mimeType = "image/x-ico";
63            break;
64        case SkEncodedFormat::kJPEG_SkEncodedFormat:
65            mimeType = "image/jpeg";
66            break;
67        case SkEncodedFormat::kPNG_SkEncodedFormat:
68            mimeType = "image/png";
69            break;
70        case SkEncodedFormat::kWEBP_SkEncodedFormat:
71            mimeType = "image/webp";
72            break;
73        case SkEncodedFormat::kWBMP_SkEncodedFormat:
74            mimeType = "image/vnd.wap.wbmp";
75            break;
76        default:
77            mimeType = nullptr;
78            break;
79    }
80
81    jstring jstr = nullptr;
82    if (mimeType) {
83        // NOTE: Caller should env->ExceptionCheck() for OOM
84        // (can't check for nullptr as it's a valid return value)
85        jstr = env->NewStringUTF(mimeType);
86    }
87    return jstr;
88}
89
90static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
91    for (int i = 0; i < count; i++) {
92        divs[i] = int32_t(divs[i] * scale + 0.5f);
93        if (i > 0 && divs[i] == divs[i - 1]) {
94            divs[i]++; // avoid collisions
95        }
96    }
97
98    if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
99        // if the collision avoidance above put some divs outside the bounds of the bitmap,
100        // slide outer stretchable divs inward to stay within bounds
101        int highestAvailable = maxValue;
102        for (int i = count - 1; i >= 0; i--) {
103            divs[i] = highestAvailable;
104            if (i > 0 && divs[i] <= divs[i-1]){
105                // keep shifting
106                highestAvailable = divs[i] - 1;
107            } else {
108                break;
109            }
110        }
111    }
112}
113
114static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale,
115        int scaledWidth, int scaledHeight) {
116    chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
117    chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
118    chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
119    chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
120
121    scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth);
122    scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight);
123}
124
125static SkColorType colorTypeForScaledOutput(SkColorType colorType) {
126    switch (colorType) {
127        case kUnknown_SkColorType:
128        case kIndex_8_SkColorType:
129            return kN32_SkColorType;
130        default:
131            break;
132    }
133    return colorType;
134}
135
136class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
137public:
138    ScaleCheckingAllocator(float scale, int size)
139            : mScale(scale), mSize(size) {
140    }
141
142    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
143        // accounts for scale in final allocation, using eventual size and config
144        const int bytesPerPixel = SkColorTypeBytesPerPixel(
145                colorTypeForScaledOutput(bitmap->colorType()));
146        const int requestedSize = bytesPerPixel *
147                int(bitmap->width() * mScale + 0.5f) *
148                int(bitmap->height() * mScale + 0.5f);
149        if (requestedSize > mSize) {
150            ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
151                    mSize, requestedSize);
152            return false;
153        }
154        return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
155    }
156private:
157    const float mScale;
158    const int mSize;
159};
160
161class RecyclingPixelAllocator : public SkBitmap::Allocator {
162public:
163    RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size)
164            : mBitmap(bitmap), mSize(size) {
165    }
166
167    ~RecyclingPixelAllocator() {
168    }
169
170    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
171        const SkImageInfo& info = bitmap->info();
172        if (info.colorType() == kUnknown_SkColorType) {
173            ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
174            return false;
175        }
176
177        const int64_t size64 = info.getSafeSize64(bitmap->rowBytes());
178        if (!sk_64_isS32(size64)) {
179            ALOGW("bitmap is too large");
180            return false;
181        }
182
183        const size_t size = sk_64_asS32(size64);
184        if (size > mSize) {
185            ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap "
186                  "(%zu bytes)", mSize, size);
187            return false;
188        }
189
190        mBitmap->reconfigure(info, bitmap->rowBytes(), ctable);
191        bitmap->setPixelRef(mBitmap->refPixelRef())->unref();
192
193        // since we're already allocated, we lockPixels right away
194        // HeapAllocator/JavaPixelAllocator behaves this way too
195        bitmap->lockPixels();
196        return true;
197    }
198
199private:
200    android::Bitmap* const mBitmap;
201    const unsigned int mSize;
202};
203
204static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
205    // This function takes ownership of the input stream.  Since the SkAndroidCodec
206    // will take ownership of the stream, we don't necessarily need to take ownership
207    // here.  This is a precaution - if we were to return before creating the codec,
208    // we need to make sure that we delete the stream.
209    std::unique_ptr<SkStreamRewindable> streamDeleter(stream);
210
211    // Set default values for the options parameters.
212    int sampleSize = 1;
213    bool onlyDecodeSize = false;
214    SkColorType prefColorType = kN32_SkColorType;
215    bool isMutable = false;
216    float scale = 1.0f;
217    bool requireUnpremultiplied = false;
218    jobject javaBitmap = NULL;
219
220    // Update with options supplied by the client.
221    if (options != NULL) {
222        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
223        // Correct a non-positive sampleSize.  sampleSize defaults to zero within the
224        // options object, which is strange.
225        if (sampleSize <= 0) {
226            sampleSize = 1;
227        }
228
229        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
230            onlyDecodeSize = true;
231        }
232
233        // initialize these, in case we fail later on
234        env->SetIntField(options, gOptions_widthFieldID, -1);
235        env->SetIntField(options, gOptions_heightFieldID, -1);
236        env->SetObjectField(options, gOptions_mimeFieldID, 0);
237
238        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
239        prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
240        isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
241        requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
242        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
243
244        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
245            const int density = env->GetIntField(options, gOptions_densityFieldID);
246            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
247            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
248            if (density != 0 && targetDensity != 0 && density != screenDensity) {
249                scale = (float) targetDensity / density;
250            }
251        }
252    }
253    const bool willScale = scale != 1.0f;
254
255    // Create the codec.
256    NinePatchPeeker peeker;
257    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(),
258            &peeker));
259    if (!codec.get()) {
260        return nullObjectReturn("SkAndroidCodec::NewFromStream returned null");
261    }
262
263    // Determine the output size and return if the client only wants the size.
264    SkISize size = codec->getSampledDimensions(sampleSize);
265    if (options != NULL) {
266        jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat());
267        if (env->ExceptionCheck()) {
268            return nullObjectReturn("OOM in getEncodedFormat()");
269        }
270        env->SetIntField(options, gOptions_widthFieldID, size.width());
271        env->SetIntField(options, gOptions_heightFieldID, size.height());
272        env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
273
274        if (onlyDecodeSize) {
275            return nullptr;
276        }
277    }
278
279    android::Bitmap* reuseBitmap = nullptr;
280    unsigned int existingBufferSize = 0;
281    if (javaBitmap != NULL) {
282        reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
283        if (reuseBitmap->peekAtPixelRef()->isImmutable()) {
284            ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
285            javaBitmap = NULL;
286            reuseBitmap = nullptr;
287        } else {
288            existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
289        }
290    }
291
292    JavaPixelAllocator javaAllocator(env);
293    RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
294    ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
295    SkBitmap::HeapAllocator heapAllocator;
296    SkBitmap::Allocator* decodeAllocator;
297    if (javaBitmap != nullptr && willScale) {
298        // This will allocate pixels using a HeapAllocator, since there will be an extra
299        // scaling step that copies these pixels into Java memory.  This allocator
300        // also checks that the recycled javaBitmap is large enough.
301        decodeAllocator = &scaleCheckingAllocator;
302    } else if (javaBitmap != nullptr) {
303        decodeAllocator = &recyclingAllocator;
304    } else if (willScale) {
305        // This will allocate pixels using a HeapAllocator, since there will be an extra
306        // scaling step that copies these pixels into Java memory.
307        decodeAllocator = &heapAllocator;
308    } else {
309        decodeAllocator = &javaAllocator;
310    }
311
312    // Set the decode colorType.  This is necessary because we can't always support
313    // the requested colorType.
314    SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
315
316    // Construct a color table for the decode if necessary
317    SkAutoTUnref<SkColorTable> colorTable(nullptr);
318    SkPMColor* colorPtr = nullptr;
319    int* colorCount = nullptr;
320    int maxColors = 256;
321    SkPMColor colors[256];
322    if (kIndex_8_SkColorType == decodeColorType) {
323        colorTable.reset(new SkColorTable(colors, maxColors));
324
325        // SkColorTable expects us to initialize all of the colors before creating an
326        // SkColorTable.  However, we are using SkBitmap with an Allocator to allocate
327        // memory for the decode, so we need to create the SkColorTable before decoding.
328        // It is safe for SkAndroidCodec to modify the colors because this SkBitmap is
329        // not being used elsewhere.
330        colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
331        colorCount = &maxColors;
332    }
333
334    // Set the alpha type for the decode.
335    SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
336
337    const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType,
338            alphaType);
339    SkImageInfo bitmapInfo = decodeInfo;
340    if (decodeColorType == kGray_8_SkColorType) {
341        // The legacy implementation of BitmapFactory used kAlpha8 for
342        // grayscale images (before kGray8 existed).  While the codec
343        // recognizes kGray8, we need to decode into a kAlpha8 bitmap
344        // in order to avoid a behavior change.
345        bitmapInfo = SkImageInfo::MakeA8(size.width(), size.height());
346    }
347    SkBitmap decodingBitmap;
348    if (!decodingBitmap.setInfo(bitmapInfo) ||
349            !decodingBitmap.tryAllocPixels(decodeAllocator, colorTable)) {
350        // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo()
351        // should only only fail if the calculated value for rowBytes is too
352        // large.
353        // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the
354        // native heap, or the recycled javaBitmap being too small to reuse.
355        return nullptr;
356    }
357
358    // Use SkAndroidCodec to perform the decode.
359    SkAndroidCodec::AndroidOptions codecOptions;
360    codecOptions.fZeroInitialized = (decodeAllocator == &javaAllocator) ?
361            SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized;
362    codecOptions.fColorPtr = colorPtr;
363    codecOptions.fColorCount = colorCount;
364    codecOptions.fSampleSize = sampleSize;
365    SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(),
366            decodingBitmap.rowBytes(), &codecOptions);
367    switch (result) {
368        case SkCodec::kSuccess:
369        case SkCodec::kIncompleteInput:
370            break;
371        default:
372            return nullObjectReturn("codec->getAndoridPixels() failed.");
373    }
374
375    // Some images may initially report that they have alpha due to the format
376    // of the encoded data, but then never use any colors which have alpha
377    // less than 100%.  Here we check if the image really had alpha, and
378    // mark it as opaque if it is actually opaque.
379    if (kOpaque_SkAlphaType != alphaType && !codec->reallyHasAlpha()) {
380        decodingBitmap.setAlphaType(kOpaque_SkAlphaType);
381    }
382
383    int scaledWidth = size.width();
384    int scaledHeight = size.height();
385    if (willScale) {
386        scaledWidth = int(scaledWidth * scale + 0.5f);
387        scaledHeight = int(scaledHeight * scale + 0.5f);
388    }
389
390    jbyteArray ninePatchChunk = NULL;
391    if (peeker.mPatch != NULL) {
392        if (willScale) {
393            scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight);
394        }
395
396        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
397        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
398        if (ninePatchChunk == NULL) {
399            return nullObjectReturn("ninePatchChunk == null");
400        }
401
402        jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
403        if (array == NULL) {
404            return nullObjectReturn("primitive array == null");
405        }
406
407        memcpy(array, peeker.mPatch, peeker.mPatchSize);
408        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
409    }
410
411    jobject ninePatchInsets = NULL;
412    if (peeker.mHasInsets) {
413        ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
414                peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
415                peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
416                peeker.mOutlineRadius, peeker.mOutlineAlpha, scale);
417        if (ninePatchInsets == NULL) {
418            return nullObjectReturn("nine patch insets == null");
419        }
420        if (javaBitmap != NULL) {
421            env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
422        }
423    }
424
425    SkBitmap outputBitmap;
426    if (willScale) {
427        // This is weird so let me explain: we could use the scale parameter
428        // directly, but for historical reasons this is how the corresponding
429        // Dalvik code has always behaved. We simply recreate the behavior here.
430        // The result is slightly different from simply using scale because of
431        // the 0.5f rounding bias applied when computing the target image size
432        const float sx = scaledWidth / float(decodingBitmap.width());
433        const float sy = scaledHeight / float(decodingBitmap.height());
434
435        // Set the allocator for the outputBitmap.
436        SkBitmap::Allocator* outputAllocator;
437        if (javaBitmap != nullptr) {
438            outputAllocator = &recyclingAllocator;
439        } else {
440            outputAllocator = &javaAllocator;
441        }
442
443        SkColorType scaledColorType = colorTypeForScaledOutput(decodingBitmap.colorType());
444        // FIXME: If the alphaType is kUnpremul and the image has alpha, the
445        // colors may not be correct, since Skia does not yet support drawing
446        // to/from unpremultiplied bitmaps.
447        outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
448                scaledColorType, decodingBitmap.alphaType()));
449        if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) {
450            // This should only fail on OOM.  The recyclingAllocator should have
451            // enough memory since we check this before decoding using the
452            // scaleCheckingAllocator.
453            return nullObjectReturn("allocation failed for scaled bitmap");
454        }
455
456        SkPaint paint;
457        // kSrc_Mode instructs us to overwrite the unininitialized pixels in
458        // outputBitmap.  Otherwise we would blend by default, which is not
459        // what we want.
460        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
461        paint.setFilterQuality(kLow_SkFilterQuality);
462
463        SkCanvas canvas(outputBitmap);
464        canvas.scale(sx, sy);
465        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
466    } else {
467        outputBitmap.swap(decodingBitmap);
468    }
469
470    if (padding) {
471        if (peeker.mPatch != NULL) {
472            GraphicsJNI::set_jrect(env, padding,
473                    peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop,
474                    peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom);
475        } else {
476            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
477        }
478    }
479
480    // If we get here, the outputBitmap should have an installed pixelref.
481    if (outputBitmap.pixelRef() == NULL) {
482        return nullObjectReturn("Got null SkPixelRef");
483    }
484
485    if (!isMutable && javaBitmap == NULL) {
486        // promise we will never change our pixels (great for sharing and pictures)
487        outputBitmap.setImmutable();
488    }
489
490    bool isPremultiplied = !requireUnpremultiplied;
491    if (javaBitmap != nullptr) {
492        GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
493        outputBitmap.notifyPixelsChanged();
494        // If a java bitmap was passed in for reuse, pass it back
495        return javaBitmap;
496    }
497
498    int bitmapCreateFlags = 0x0;
499    if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
500    if (isPremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
501
502    // now create the java bitmap
503    return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
504            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
505}
506
507// Need to buffer enough input to be able to rewind as much as might be read by a decoder
508// trying to determine the stream's format. Currently the most is 64, read by
509// SkWebpCodec.
510// FIXME: Get this number from SkCodec
511#define BYTES_TO_BUFFER 64
512
513static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
514        jobject padding, jobject options) {
515
516    jobject bitmap = NULL;
517    std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
518
519    if (stream.get()) {
520        std::unique_ptr<SkStreamRewindable> bufferedStream(
521                SkFrontBufferedStream::Create(stream.release(), BYTES_TO_BUFFER));
522        SkASSERT(bufferedStream.get() != NULL);
523        bitmap = doDecode(env, bufferedStream.release(), padding, options);
524    }
525    return bitmap;
526}
527
528static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
529        jobject padding, jobject bitmapFactoryOptions) {
530
531    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
532
533    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
534
535    struct stat fdStat;
536    if (fstat(descriptor, &fdStat) == -1) {
537        doThrowIOE(env, "broken file descriptor");
538        return nullObjectReturn("fstat return -1");
539    }
540
541    // Restore the descriptor's offset on exiting this function. Even though
542    // we dup the descriptor, both the original and dup refer to the same open
543    // file description and changes to the file offset in one impact the other.
544    AutoFDSeek autoRestore(descriptor);
545
546    // Duplicate the descriptor here to prevent leaking memory. A leak occurs
547    // if we only close the file descriptor and not the file object it is used to
548    // create.  If we don't explicitly clean up the file (which in turn closes the
549    // descriptor) the buffers allocated internally by fseek will be leaked.
550    int dupDescriptor = dup(descriptor);
551
552    FILE* file = fdopen(dupDescriptor, "r");
553    if (file == NULL) {
554        // cleanup the duplicated descriptor since it will not be closed when the
555        // file is cleaned up (fclose).
556        close(dupDescriptor);
557        return nullObjectReturn("Could not open file");
558    }
559
560    std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file,
561            SkFILEStream::kCallerPasses_Ownership));
562
563    // Use a buffered stream. Although an SkFILEStream can be rewound, this
564    // ensures that SkImageDecoder::Factory never rewinds beyond the
565    // current position of the file descriptor.
566    std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream.release(),
567            BYTES_TO_BUFFER));
568
569    return doDecode(env, stream.release(), padding, bitmapFactoryOptions);
570}
571
572static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
573        jobject padding, jobject options) {
574
575    Asset* asset = reinterpret_cast<Asset*>(native_asset);
576    // since we know we'll be done with the asset when we return, we can
577    // just use a simple wrapper
578    std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));
579    return doDecode(env, stream.release(), padding, options);
580}
581
582static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
583        jint offset, jint length, jobject options) {
584
585    AutoJavaByteArray ar(env, byteArray);
586    std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, false));
587    return doDecode(env, stream.release(), NULL, options);
588}
589
590static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
591    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
592    return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
593}
594
595jobject decodeBitmap(JNIEnv* env, void* data, size_t size) {
596    SkMemoryStream stream(data, size);
597    return doDecode(env, &stream, NULL, NULL);
598}
599
600///////////////////////////////////////////////////////////////////////////////
601
602static const JNINativeMethod gMethods[] = {
603    {   "nativeDecodeStream",
604        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
605        (void*)nativeDecodeStream
606    },
607
608    {   "nativeDecodeFileDescriptor",
609        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
610        (void*)nativeDecodeFileDescriptor
611    },
612
613    {   "nativeDecodeAsset",
614        "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
615        (void*)nativeDecodeAsset
616    },
617
618    {   "nativeDecodeByteArray",
619        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
620        (void*)nativeDecodeByteArray
621    },
622
623    {   "nativeIsSeekable",
624        "(Ljava/io/FileDescriptor;)Z",
625        (void*)nativeIsSeekable
626    },
627};
628
629int register_android_graphics_BitmapFactory(JNIEnv* env) {
630    jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options");
631    gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap",
632            "Landroid/graphics/Bitmap;");
633    gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z");
634    gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I");
635    gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig",
636            "Landroid/graphics/Bitmap$Config;");
637    gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z");
638    gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z");
639    gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z");
640    gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class,
641            "inPreferQualityOverSpeed", "Z");
642    gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z");
643    gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I");
644    gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I");
645    gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I");
646    gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I");
647    gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I");
648    gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;");
649    gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z");
650
651    jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap");
652    gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets",
653            "Landroid/graphics/NinePatch$InsetStruct;");
654
655    gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
656        "android/graphics/NinePatch$InsetStruct"));
657    gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
658                                                        "(IIIIIIIIFIF)V");
659
660    return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory",
661                                         gMethods, NELEM(gMethods));
662}
663