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