BitmapFactory.cpp revision afce5a4008c494f6384e1b6b2fb4f13d33e917cb
1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#define LOG_TAG "BitmapFactory"
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "BitmapFactory.h"
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "NinePatchPeeker.h"
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkImageDecoder.h"
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkImageRef_ashmem.h"
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkImageRef_GlobalPool.h"
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkPixelRef.h"
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkStream.h"
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkTemplates.h"
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "SkUtils.h"
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "CreateJavaOutputStreamAdaptor.h"
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "AutoDecodeCancel.h"
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "Utils.h"
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "JNIHelp.h"
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <android_runtime/AndroidRuntime.h>
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <androidfw/Asset.h>
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <androidfw/ResourceTypes.h>
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <netinet/in.h>
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <sys/mman.h>
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <sys/stat.h>
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_justBoundsFieldID;
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_sampleSizeFieldID;
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_configFieldID;
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_mutableFieldID;
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_ditherFieldID;
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_purgeableFieldID;
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_shareableFieldID;
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_preferQualityOverSpeedFieldID;
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_widthFieldID;
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_heightFieldID;
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_mimeFieldID;
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_mCancelID;
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gOptions_bitmapFieldID;
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gBitmap_nativeBitmapFieldID;
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam LesinskijfieldID gBitmap_layoutBoundsFieldID;
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#if 0
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    #define TRACE_BITMAP(code)  code
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#else
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    #define TRACE_BITMAP(code)
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#endif
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiusing namespace android;
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic inline int32_t validOrNeg1(bool isValid, int32_t value) {
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//    return isValid ? value : -1;
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    SkASSERT((int)isValid == 0 || (int)isValid == 1);
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return ((int32_t)isValid - 1) | value;
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskijstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    static const struct {
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        SkImageDecoder::Format fFormat;
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        const char*            fMimeType;
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } gMimeTypes[] = {
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        { SkImageDecoder::kBMP_Format,  "image/bmp" },
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        { SkImageDecoder::kGIF_Format,  "image/gif" },
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        { SkImageDecoder::kICO_Format,  "image/x-ico" },
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        { SkImageDecoder::kJPEG_Format, "image/jpeg" },
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        { SkImageDecoder::kPNG_Format,  "image/png" },
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    };
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const char* cstr = NULL;
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (gMimeTypes[i].fFormat == format) {
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            cstr = gMimeTypes[i].fMimeType;
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            break;
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    jstring jstr = 0;
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (NULL != cstr) {
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        jstr = env->NewStringUTF(cstr);
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return jstr;
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic bool optionsPurgeable(JNIEnv* env, jobject options) {
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
85
86static bool optionsShareable(JNIEnv* env, jobject options) {
87    return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
88}
89
90static bool optionsJustBounds(JNIEnv* env, jobject options) {
91    return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
92}
93
94static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
95    chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
96    chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
97    chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
98    chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
99
100    for (int i = 0; i < chunk->numXDivs; i++) {
101        chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
102        if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
103            chunk->xDivs[i]++;
104        }
105    }
106
107    for (int i = 0; i < chunk->numYDivs; i++) {
108        chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
109        if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
110            chunk->yDivs[i]++;
111        }
112    }
113}
114
115static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
116        jobject padding) {
117
118    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
119    if (array != NULL) {
120        size_t chunkSize = env->GetArrayLength(chunkObject);
121        void* storage = alloca(chunkSize);
122        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
123        memcpy(chunk, array, chunkSize);
124        android::Res_png_9patch::deserialize(chunk);
125
126        scaleNinePatchChunk(chunk, scale);
127        memcpy(array, chunk, chunkSize);
128
129        if (padding) {
130            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
131                    chunk->paddingRight, chunk->paddingBottom);
132        }
133
134        env->ReleaseByteArrayElements(chunkObject, array, 0);
135    }
136    return chunkObject;
137}
138
139static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
140        int sampleSize, bool ditherImage) {
141
142    SkImageRef* pr;
143    // only use ashmem for large images, since mmaps come at a price
144    if (bitmap->getSize() >= 32 * 1024) {
145        pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
146    } else {
147        pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
148    }
149    pr->setDitherImage(ditherImage);
150    bitmap->setPixelRef(pr)->unref();
151    pr->isOpaque(bitmap);
152    return pr;
153}
154
155// since we "may" create a purgeable imageref, we require the stream be ref'able
156// i.e. dynamically allocated, since its lifetime may exceed the current stack
157// frame.
158static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
159        jobject options, bool allowPurgeable, bool forcePurgeable = false,
160        bool applyScale = false, float scale = 1.0f) {
161
162    int sampleSize = 1;
163
164    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
165    SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
166
167    bool doDither = true;
168    bool isMutable = false;
169    bool willScale = applyScale && scale != 1.0f;
170    bool isPurgeable = !willScale &&
171            (forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)));
172    bool preferQualityOverSpeed = false;
173
174    jobject javaBitmap = NULL;
175
176    if (options != NULL) {
177        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
178        if (optionsJustBounds(env, options)) {
179            mode = SkImageDecoder::kDecodeBounds_Mode;
180        }
181
182        // initialize these, in case we fail later on
183        env->SetIntField(options, gOptions_widthFieldID, -1);
184        env->SetIntField(options, gOptions_heightFieldID, -1);
185        env->SetObjectField(options, gOptions_mimeFieldID, 0);
186
187        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
188        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
189        isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
190        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
191        preferQualityOverSpeed = env->GetBooleanField(options,
192                gOptions_preferQualityOverSpeedFieldID);
193        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
194    }
195
196    if (willScale && javaBitmap != NULL) {
197        return nullObjectReturn("Cannot pre-scale a reused bitmap");
198    }
199
200    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
201    if (decoder == NULL) {
202        return nullObjectReturn("SkImageDecoder::Factory returned null");
203    }
204
205    decoder->setSampleSize(sampleSize);
206    decoder->setDitherImage(doDither);
207    decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
208
209    NinePatchPeeker peeker(decoder);
210    JavaPixelAllocator javaAllocator(env);
211
212    SkBitmap* bitmap;
213    bool useExistingBitmap = false;
214    if (javaBitmap == NULL) {
215        bitmap = new SkBitmap;
216    } else {
217        if (sampleSize != 1) {
218            return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1");
219        }
220
221        bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
222        // only reuse the provided bitmap if it is immutable
223        if (!bitmap->isImmutable()) {
224            useExistingBitmap = true;
225            // config of supplied bitmap overrules config set in options
226            prefConfig = bitmap->getConfig();
227        } else {
228            ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
229            bitmap = new SkBitmap;
230        }
231    }
232
233    SkAutoTDelete<SkImageDecoder> add(decoder);
234    SkAutoTDelete<SkBitmap> adb(bitmap, !useExistingBitmap);
235
236    decoder->setPeeker(&peeker);
237    if (!isPurgeable) {
238        decoder->setAllocator(&javaAllocator);
239    }
240
241    AutoDecoderCancel adc(options, decoder);
242
243    // To fix the race condition in case "requestCancelDecode"
244    // happens earlier than AutoDecoderCancel object is added
245    // to the gAutoDecoderCancelMutex linked list.
246    if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
247        return nullObjectReturn("gOptions_mCancelID");
248    }
249
250    SkImageDecoder::Mode decodeMode = mode;
251    if (isPurgeable) {
252        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
253    }
254
255    SkBitmap* decoded;
256    if (willScale) {
257        decoded = new SkBitmap;
258    } else {
259        decoded = bitmap;
260    }
261    SkAutoTDelete<SkBitmap> adb2(willScale ? decoded : NULL);
262
263    if (!decoder->decode(stream, decoded, prefConfig, decodeMode, javaBitmap != NULL)) {
264        return nullObjectReturn("decoder->decode returned false");
265    }
266
267    int scaledWidth = decoded->width();
268    int scaledHeight = decoded->height();
269
270    if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
271        scaledWidth = int(scaledWidth * scale + 0.5f);
272        scaledHeight = int(scaledHeight * scale + 0.5f);
273    }
274
275    // update options (if any)
276    if (options != NULL) {
277        env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
278        env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
279        env->SetObjectField(options, gOptions_mimeFieldID,
280                getMimeTypeString(env, decoder->getFormat()));
281    }
282
283    // if we're in justBounds mode, return now (skip the java bitmap)
284    if (mode == SkImageDecoder::kDecodeBounds_Mode) {
285        return NULL;
286    }
287
288    jbyteArray ninePatchChunk = NULL;
289    if (peeker.fPatch != NULL) {
290        if (willScale) {
291            scaleNinePatchChunk(peeker.fPatch, scale);
292        }
293
294        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
295        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
296        if (ninePatchChunk == NULL) {
297            return nullObjectReturn("ninePatchChunk == null");
298        }
299
300        jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
301        if (array == NULL) {
302            return nullObjectReturn("primitive array == null");
303        }
304
305        peeker.fPatch->serialize(array);
306        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
307    }
308
309    jintArray layoutBounds = NULL;
310    if (peeker.fLayoutBounds != NULL) {
311        layoutBounds = env->NewIntArray(4);
312        if (layoutBounds == NULL) {
313            return nullObjectReturn("layoutBounds == null");
314        }
315
316        jint scaledBounds[4];
317        if (willScale) {
318            for (int i=0; i<4; i++) {
319                scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f);
320            }
321        } else {
322            memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds));
323        }
324        env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds);
325        if (javaBitmap != NULL) {
326            env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
327        }
328    }
329
330    if (willScale) {
331        // This is weird so let me explain: we could use the scale parameter
332        // directly, but for historical reasons this is how the corresponding
333        // Dalvik code has always behaved. We simply recreate the behavior here.
334        // The result is slightly different from simply using scale because of
335        // the 0.5f rounding bias applied when computing the target image size
336        const float sx = scaledWidth / float(decoded->width());
337        const float sy = scaledHeight / float(decoded->height());
338
339        SkBitmap::Config config = decoded->config();
340        switch (config) {
341            case SkBitmap::kNo_Config:
342            case SkBitmap::kIndex8_Config:
343            case SkBitmap::kRLE_Index8_Config:
344                config = SkBitmap::kARGB_8888_Config;
345                break;
346            default:
347                break;
348        }
349
350        bitmap->setConfig(config, scaledWidth, scaledHeight);
351        bitmap->setIsOpaque(decoded->isOpaque());
352        if (!bitmap->allocPixels(&javaAllocator, NULL)) {
353            return nullObjectReturn("allocation failed for scaled bitmap");
354        }
355        bitmap->eraseColor(0);
356
357        SkPaint paint;
358        paint.setFilterBitmap(true);
359
360        SkCanvas canvas(*bitmap);
361        canvas.scale(sx, sy);
362        canvas.drawBitmap(*decoded, 0.0f, 0.0f, &paint);
363    }
364
365    if (padding) {
366        if (peeker.fPatch != NULL) {
367            GraphicsJNI::set_jrect(env, padding,
368                    peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
369                    peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
370        } else {
371            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
372        }
373    }
374
375    SkPixelRef* pr;
376    if (isPurgeable) {
377        pr = installPixelRef(bitmap, stream, sampleSize, doDither);
378    } else {
379        // if we get here, we're in kDecodePixels_Mode and will therefore
380        // already have a pixelref installed.
381        pr = bitmap->pixelRef();
382    }
383    if (pr == NULL) {
384        return nullObjectReturn("Got null SkPixelRef");
385    }
386
387    if (!isMutable && !useExistingBitmap) {
388        // promise we will never change our pixels (great for sharing and pictures)
389        pr->setImmutable();
390    }
391
392    // detach bitmap from its autodeleter, since we want to own it now
393    adb.detach();
394
395    if (useExistingBitmap) {
396        // If a java bitmap was passed in for reuse, pass it back
397        return javaBitmap;
398    }
399    // now create the java bitmap
400    return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
401            isMutable, ninePatchChunk, layoutBounds, -1);
402}
403
404static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
405        jobject padding, jobject options, jboolean applyScale, jfloat scale) {
406
407    jobject bitmap = NULL;
408    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
409
410    if (stream) {
411        // for now we don't allow purgeable with java inputstreams
412        bitmap = doDecode(env, stream, padding, options, false, false, applyScale, scale);
413        stream->unref();
414    }
415    return bitmap;
416}
417
418static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
419        jobject padding, jobject options) {
420
421    return nativeDecodeStreamScaled(env, clazz, is, storage, padding, options, false, 1.0f);
422}
423
424static ssize_t getFDSize(int fd) {
425    off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
426    if (curr < 0) {
427        return 0;
428    }
429    size_t size = ::lseek(fd, 0, SEEK_END);
430    ::lseek64(fd, curr, SEEK_SET);
431    return size;
432}
433
434static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
435        jobject padding, jobject bitmapFactoryOptions) {
436
437    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
438
439    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
440
441    bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
442    bool isShareable = optionsShareable(env, bitmapFactoryOptions);
443    bool weOwnTheFD = false;
444    if (isPurgeable && isShareable) {
445        int newFD = ::dup(descriptor);
446        if (-1 != newFD) {
447            weOwnTheFD = true;
448            descriptor = newFD;
449        }
450    }
451
452    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
453    SkAutoUnref aur(stream);
454    if (!stream->isValid()) {
455        return NULL;
456    }
457
458    /* Restore our offset when we leave, so we can be called more than once
459       with the same descriptor. This is only required if we didn't dup the
460       file descriptor, but it is OK to do it all the time.
461    */
462    AutoFDSeek as(descriptor);
463
464    /* Allow purgeable iff we own the FD, i.e., in the puregeable and
465       shareable case.
466    */
467    return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
468}
469
470/*  make a deep copy of the asset, and return it as a stream, or NULL if there
471    was an error.
472 */
473static SkStream* copyAssetToStream(Asset* asset) {
474    // if we could "ref/reopen" the asset, we may not need to copy it here
475    off64_t size = asset->seek(0, SEEK_SET);
476    if ((off64_t)-1 == size) {
477        SkDebugf("---- copyAsset: asset rewind failed\n");
478        return NULL;
479    }
480
481    size = asset->getLength();
482    if (size <= 0) {
483        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
484        return NULL;
485    }
486
487    SkStream* stream = new SkMemoryStream(size);
488    void* data = const_cast<void*>(stream->getMemoryBase());
489    off64_t len = asset->read(data, size);
490    if (len != size) {
491        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
492        delete stream;
493        stream = NULL;
494    }
495    return stream;
496}
497
498static jobject nativeDecodeAssetScaled(JNIEnv* env, jobject clazz, jint native_asset,
499        jobject padding, jobject options, jboolean applyScale, jfloat scale) {
500
501    SkStream* stream;
502    Asset* asset = reinterpret_cast<Asset*>(native_asset);
503    bool forcePurgeable = optionsPurgeable(env, options);
504    if (forcePurgeable) {
505        // if we could "ref/reopen" the asset, we may not need to copy it here
506        // and we could assume optionsShareable, since assets are always RO
507        stream = copyAssetToStream(asset);
508        if (stream == NULL) {
509            return NULL;
510        }
511    } else {
512        // since we know we'll be done with the asset when we return, we can
513        // just use a simple wrapper
514        stream = new AssetStreamAdaptor(asset);
515    }
516    SkAutoUnref aur(stream);
517    return doDecode(env, stream, padding, options, true, forcePurgeable, applyScale, scale);
518}
519
520static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset,
521        jobject padding, jobject options) {
522
523    return nativeDecodeAssetScaled(env, clazz, native_asset, padding, options, false, 1.0f);
524}
525
526static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
527        int offset, int length, jobject options) {
528
529    /*  If optionsShareable() we could decide to just wrap the java array and
530        share it, but that means adding a globalref to the java array object
531        and managing its lifetime. For now we just always copy the array's data
532        if optionsPurgeable(), unless we're just decoding bounds.
533     */
534    bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
535    AutoJavaByteArray ar(env, byteArray);
536    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
537    SkAutoUnref aur(stream);
538    return doDecode(env, stream, NULL, options, purgeable);
539}
540
541static void nativeRequestCancel(JNIEnv*, jobject joptions) {
542    (void)AutoDecoderCancel::RequestCancel(joptions);
543}
544
545static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
546    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
547    return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
548}
549
550///////////////////////////////////////////////////////////////////////////////
551
552static JNINativeMethod gMethods[] = {
553    {   "nativeDecodeStream",
554        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
555        (void*)nativeDecodeStream
556    },
557    {   "nativeDecodeStream",
558        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;",
559        (void*)nativeDecodeStreamScaled
560    },
561
562    {   "nativeDecodeFileDescriptor",
563        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
564        (void*)nativeDecodeFileDescriptor
565    },
566
567    {   "nativeDecodeAsset",
568        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
569        (void*)nativeDecodeAsset
570    },
571
572    {   "nativeDecodeAsset",
573        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;",
574        (void*)nativeDecodeAssetScaled
575    },
576
577    {   "nativeDecodeByteArray",
578        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
579        (void*)nativeDecodeByteArray
580    },
581
582    {   "nativeScaleNinePatch",
583        "([BFLandroid/graphics/Rect;)[B",
584        (void*)nativeScaleNinePatch
585    },
586
587    {   "nativeIsSeekable",
588        "(Ljava/io/FileDescriptor;)Z",
589        (void*)nativeIsSeekable
590    },
591};
592
593static JNINativeMethod gOptionsMethods[] = {
594    {   "requestCancel", "()V", (void*)nativeRequestCancel }
595};
596
597static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
598                                const char fieldname[], const char type[]) {
599    jfieldID id = env->GetFieldID(clazz, fieldname, type);
600    SkASSERT(id);
601    return id;
602}
603
604int register_android_graphics_BitmapFactory(JNIEnv* env) {
605    jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
606    SkASSERT(options_class);
607    gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
608        "Landroid/graphics/Bitmap;");
609    gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
610    gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
611    gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
612            "Landroid/graphics/Bitmap$Config;");
613    gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
614    gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
615    gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
616    gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
617    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
618            "inPreferQualityOverSpeed", "Z");
619    gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
620    gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
621    gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
622    gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
623
624    jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
625    SkASSERT(bitmap_class);
626    gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
627    gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
628    int ret = AndroidRuntime::registerNativeMethods(env,
629                                    "android/graphics/BitmapFactory$Options",
630                                    gOptionsMethods,
631                                    SK_ARRAY_COUNT(gOptionsMethods));
632    if (ret) {
633        return ret;
634    }
635    return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
636                                         gMethods, SK_ARRAY_COUNT(gMethods));
637}
638