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