BitmapFactory.cpp revision 3b1b68d6c764a4f60d034e57a94879b7df65fd43
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    // Do not allow ninepatch decodes to 565.  In the past, decodes to 565
264    // would dither, and we do not want to pre-dither ninepatches, since we
265    // know that they will be stretched.  We no longer dither 565 decodes,
266    // but we continue to prevent ninepatches from decoding to 565, in order
267    // to maintain the old behavior.
268    if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) {
269        prefColorType = kN32_SkColorType;
270    }
271
272    // Determine the output size and return if the client only wants the size.
273    SkISize size = codec->getSampledDimensions(sampleSize);
274    if (options != NULL) {
275        jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat());
276        if (env->ExceptionCheck()) {
277            return nullObjectReturn("OOM in encodedFormatToString()");
278        }
279        env->SetIntField(options, gOptions_widthFieldID, size.width());
280        env->SetIntField(options, gOptions_heightFieldID, size.height());
281        env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
282
283        if (onlyDecodeSize) {
284            return nullptr;
285        }
286    }
287
288    android::Bitmap* reuseBitmap = nullptr;
289    unsigned int existingBufferSize = 0;
290    if (javaBitmap != NULL) {
291        reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
292        if (reuseBitmap->peekAtPixelRef()->isImmutable()) {
293            ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
294            javaBitmap = NULL;
295            reuseBitmap = nullptr;
296        } else {
297            existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
298        }
299    }
300
301    JavaPixelAllocator javaAllocator(env);
302    RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
303    ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
304    SkBitmap::HeapAllocator heapAllocator;
305    SkBitmap::Allocator* decodeAllocator;
306    if (javaBitmap != nullptr && willScale) {
307        // This will allocate pixels using a HeapAllocator, since there will be an extra
308        // scaling step that copies these pixels into Java memory.  This allocator
309        // also checks that the recycled javaBitmap is large enough.
310        decodeAllocator = &scaleCheckingAllocator;
311    } else if (javaBitmap != nullptr) {
312        decodeAllocator = &recyclingAllocator;
313    } else if (willScale) {
314        // This will allocate pixels using a HeapAllocator, since there will be an extra
315        // scaling step that copies these pixels into Java memory.
316        decodeAllocator = &heapAllocator;
317    } else {
318        decodeAllocator = &javaAllocator;
319    }
320
321    // Set the decode colorType.  This is necessary because we can't always support
322    // the requested colorType.
323    SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
324
325    // Construct a color table for the decode if necessary
326    SkAutoTUnref<SkColorTable> colorTable(nullptr);
327    SkPMColor* colorPtr = nullptr;
328    int* colorCount = nullptr;
329    int maxColors = 256;
330    SkPMColor colors[256];
331    if (kIndex_8_SkColorType == decodeColorType) {
332        colorTable.reset(new SkColorTable(colors, maxColors));
333
334        // SkColorTable expects us to initialize all of the colors before creating an
335        // SkColorTable.  However, we are using SkBitmap with an Allocator to allocate
336        // memory for the decode, so we need to create the SkColorTable before decoding.
337        // It is safe for SkAndroidCodec to modify the colors because this SkBitmap is
338        // not being used elsewhere.
339        colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
340        colorCount = &maxColors;
341    }
342
343    // Set the alpha type for the decode.
344    SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
345
346    const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType,
347            alphaType);
348    SkImageInfo bitmapInfo = decodeInfo;
349    if (decodeColorType == kGray_8_SkColorType) {
350        // The legacy implementation of BitmapFactory used kAlpha8 for
351        // grayscale images (before kGray8 existed).  While the codec
352        // recognizes kGray8, we need to decode into a kAlpha8 bitmap
353        // in order to avoid a behavior change.
354        bitmapInfo = SkImageInfo::MakeA8(size.width(), size.height());
355    }
356    SkBitmap decodingBitmap;
357    if (!decodingBitmap.setInfo(bitmapInfo) ||
358            !decodingBitmap.tryAllocPixels(decodeAllocator, colorTable)) {
359        // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo()
360        // should only only fail if the calculated value for rowBytes is too
361        // large.
362        // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the
363        // native heap, or the recycled javaBitmap being too small to reuse.
364        return nullptr;
365    }
366
367    // Use SkAndroidCodec to perform the decode.
368    SkAndroidCodec::AndroidOptions codecOptions;
369    codecOptions.fZeroInitialized = (decodeAllocator == &javaAllocator) ?
370            SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized;
371    codecOptions.fColorPtr = colorPtr;
372    codecOptions.fColorCount = colorCount;
373    codecOptions.fSampleSize = sampleSize;
374    SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(),
375            decodingBitmap.rowBytes(), &codecOptions);
376    switch (result) {
377        case SkCodec::kSuccess:
378        case SkCodec::kIncompleteInput:
379            break;
380        default:
381            return nullObjectReturn("codec->getAndroidPixels() failed.");
382    }
383
384    int scaledWidth = size.width();
385    int scaledHeight = size.height();
386    if (willScale) {
387        scaledWidth = int(scaledWidth * scale + 0.5f);
388        scaledHeight = int(scaledHeight * scale + 0.5f);
389    }
390
391    jbyteArray ninePatchChunk = NULL;
392    if (peeker.mPatch != NULL) {
393        if (willScale) {
394            scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight);
395        }
396
397        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
398        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
399        if (ninePatchChunk == NULL) {
400            return nullObjectReturn("ninePatchChunk == null");
401        }
402
403        jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
404        if (array == NULL) {
405            return nullObjectReturn("primitive array == null");
406        }
407
408        memcpy(array, peeker.mPatch, peeker.mPatchSize);
409        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
410    }
411
412    jobject ninePatchInsets = NULL;
413    if (peeker.mHasInsets) {
414        ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
415                peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
416                peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
417                peeker.mOutlineRadius, peeker.mOutlineAlpha, scale);
418        if (ninePatchInsets == NULL) {
419            return nullObjectReturn("nine patch insets == null");
420        }
421        if (javaBitmap != NULL) {
422            env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
423        }
424    }
425
426    SkBitmap outputBitmap;
427    if (willScale) {
428        // This is weird so let me explain: we could use the scale parameter
429        // directly, but for historical reasons this is how the corresponding
430        // Dalvik code has always behaved. We simply recreate the behavior here.
431        // The result is slightly different from simply using scale because of
432        // the 0.5f rounding bias applied when computing the target image size
433        const float sx = scaledWidth / float(decodingBitmap.width());
434        const float sy = scaledHeight / float(decodingBitmap.height());
435
436        // Set the allocator for the outputBitmap.
437        SkBitmap::Allocator* outputAllocator;
438        if (javaBitmap != nullptr) {
439            outputAllocator = &recyclingAllocator;
440        } else {
441            outputAllocator = &javaAllocator;
442        }
443
444        SkColorType scaledColorType = colorTypeForScaledOutput(decodingBitmap.colorType());
445        // FIXME: If the alphaType is kUnpremul and the image has alpha, the
446        // colors may not be correct, since Skia does not yet support drawing
447        // to/from unpremultiplied bitmaps.
448        outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
449                scaledColorType, decodingBitmap.alphaType()));
450        if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) {
451            // This should only fail on OOM.  The recyclingAllocator should have
452            // enough memory since we check this before decoding using the
453            // scaleCheckingAllocator.
454            return nullObjectReturn("allocation failed for scaled bitmap");
455        }
456
457        SkPaint paint;
458        // kSrc_Mode instructs us to overwrite the unininitialized pixels in
459        // outputBitmap.  Otherwise we would blend by default, which is not
460        // what we want.
461        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
462        paint.setFilterQuality(kLow_SkFilterQuality);
463
464        SkCanvas canvas(outputBitmap);
465        canvas.scale(sx, sy);
466        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
467    } else {
468        outputBitmap.swap(decodingBitmap);
469    }
470
471    if (padding) {
472        if (peeker.mPatch != NULL) {
473            GraphicsJNI::set_jrect(env, padding,
474                    peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop,
475                    peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom);
476        } else {
477            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
478        }
479    }
480
481    // If we get here, the outputBitmap should have an installed pixelref.
482    if (outputBitmap.pixelRef() == NULL) {
483        return nullObjectReturn("Got null SkPixelRef");
484    }
485
486    if (!isMutable && javaBitmap == NULL) {
487        // promise we will never change our pixels (great for sharing and pictures)
488        outputBitmap.setImmutable();
489    }
490
491    bool isPremultiplied = !requireUnpremultiplied;
492    if (javaBitmap != nullptr) {
493        GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
494        outputBitmap.notifyPixelsChanged();
495        // If a java bitmap was passed in for reuse, pass it back
496        return javaBitmap;
497    }
498
499    int bitmapCreateFlags = 0x0;
500    if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
501    if (isPremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
502
503    // now create the java bitmap
504    return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
505            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
506}
507
508// Need to buffer enough input to be able to rewind as much as might be read by a decoder
509// trying to determine the stream's format. Currently the most is 64, read by
510// SkWebpCodec.
511// FIXME: Get this number from SkCodec
512#define BYTES_TO_BUFFER 64
513
514static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
515        jobject padding, jobject options) {
516
517    jobject bitmap = NULL;
518    std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
519
520    if (stream.get()) {
521        std::unique_ptr<SkStreamRewindable> bufferedStream(
522                SkFrontBufferedStream::Create(stream.release(), BYTES_TO_BUFFER));
523        SkASSERT(bufferedStream.get() != NULL);
524        bitmap = doDecode(env, bufferedStream.release(), padding, options);
525    }
526    return bitmap;
527}
528
529static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
530        jobject padding, jobject bitmapFactoryOptions) {
531
532    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
533
534    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
535
536    struct stat fdStat;
537    if (fstat(descriptor, &fdStat) == -1) {
538        doThrowIOE(env, "broken file descriptor");
539        return nullObjectReturn("fstat return -1");
540    }
541
542    // Restore the descriptor's offset on exiting this function. Even though
543    // we dup the descriptor, both the original and dup refer to the same open
544    // file description and changes to the file offset in one impact the other.
545    AutoFDSeek autoRestore(descriptor);
546
547    // Duplicate the descriptor here to prevent leaking memory. A leak occurs
548    // if we only close the file descriptor and not the file object it is used to
549    // create.  If we don't explicitly clean up the file (which in turn closes the
550    // descriptor) the buffers allocated internally by fseek will be leaked.
551    int dupDescriptor = dup(descriptor);
552
553    FILE* file = fdopen(dupDescriptor, "r");
554    if (file == NULL) {
555        // cleanup the duplicated descriptor since it will not be closed when the
556        // file is cleaned up (fclose).
557        close(dupDescriptor);
558        return nullObjectReturn("Could not open file");
559    }
560
561    std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file,
562            SkFILEStream::kCallerPasses_Ownership));
563
564    // Use a buffered stream. Although an SkFILEStream can be rewound, this
565    // ensures that SkImageDecoder::Factory never rewinds beyond the
566    // current position of the file descriptor.
567    std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream.release(),
568            BYTES_TO_BUFFER));
569
570    return doDecode(env, stream.release(), padding, bitmapFactoryOptions);
571}
572
573static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
574        jobject padding, jobject options) {
575
576    Asset* asset = reinterpret_cast<Asset*>(native_asset);
577    // since we know we'll be done with the asset when we return, we can
578    // just use a simple wrapper
579    std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));
580    return doDecode(env, stream.release(), padding, options);
581}
582
583static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
584        jint offset, jint length, jobject options) {
585
586    AutoJavaByteArray ar(env, byteArray);
587    std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, false));
588    return doDecode(env, stream.release(), NULL, options);
589}
590
591static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
592    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
593    return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
594}
595
596jobject decodeBitmap(JNIEnv* env, void* data, size_t size) {
597    SkMemoryStream stream(data, size);
598    return doDecode(env, &stream, NULL, NULL);
599}
600
601///////////////////////////////////////////////////////////////////////////////
602
603static const JNINativeMethod gMethods[] = {
604    {   "nativeDecodeStream",
605        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
606        (void*)nativeDecodeStream
607    },
608
609    {   "nativeDecodeFileDescriptor",
610        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
611        (void*)nativeDecodeFileDescriptor
612    },
613
614    {   "nativeDecodeAsset",
615        "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
616        (void*)nativeDecodeAsset
617    },
618
619    {   "nativeDecodeByteArray",
620        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
621        (void*)nativeDecodeByteArray
622    },
623
624    {   "nativeIsSeekable",
625        "(Ljava/io/FileDescriptor;)Z",
626        (void*)nativeIsSeekable
627    },
628};
629
630int register_android_graphics_BitmapFactory(JNIEnv* env) {
631    jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options");
632    gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap",
633            "Landroid/graphics/Bitmap;");
634    gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z");
635    gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I");
636    gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig",
637            "Landroid/graphics/Bitmap$Config;");
638    gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z");
639    gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z");
640    gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z");
641    gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class,
642            "inPreferQualityOverSpeed", "Z");
643    gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z");
644    gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I");
645    gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I");
646    gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I");
647    gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I");
648    gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I");
649    gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;");
650    gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z");
651
652    jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap");
653    gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets",
654            "Landroid/graphics/NinePatch$InsetStruct;");
655
656    gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
657        "android/graphics/NinePatch$InsetStruct"));
658    gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
659                                                        "(IIIIIIIIFIF)V");
660
661    return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory",
662                                         gMethods, NELEM(gMethods));
663}
664