BitmapRegionDecoder.cpp revision f5b43bdc6231df8b646d5bbb215e639230f37260
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "BitmapRegionDecoder"
18
19#include "SkBitmap.h"
20#include "SkData.h"
21#include "SkImageEncoder.h"
22#include "GraphicsJNI.h"
23#include "SkUtils.h"
24#include "SkTemplates.h"
25#include "SkPixelRef.h"
26#include "SkStream.h"
27#include "BitmapFactory.h"
28#include "AutoDecodeCancel.h"
29#include "CreateJavaOutputStreamAdaptor.h"
30#include "Utils.h"
31#include "JNIHelp.h"
32
33#include <android_runtime/AndroidRuntime.h>
34#include "android_util_Binder.h"
35#include "android_nio_utils.h"
36#include "CreateJavaOutputStreamAdaptor.h"
37
38#include <binder/Parcel.h>
39#include <jni.h>
40#include <androidfw/Asset.h>
41#include <sys/stat.h>
42
43#if 0
44    #define TRACE_BITMAP(code)  code
45#else
46    #define TRACE_BITMAP(code)
47#endif
48
49using namespace android;
50
51class SkBitmapRegionDecoder {
52public:
53    SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
54        fDecoder = decoder;
55        fWidth = width;
56        fHeight = height;
57    }
58    ~SkBitmapRegionDecoder() {
59        SkDELETE(fDecoder);
60    }
61
62    bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
63                      SkBitmap::Config pref, int sampleSize) {
64        fDecoder->setSampleSize(sampleSize);
65        return fDecoder->decodeRegion(bitmap, rect, pref);
66    }
67
68    SkImageDecoder* getDecoder() const { return fDecoder; }
69    int getWidth() const { return fWidth; }
70    int getHeight() const { return fHeight; }
71
72private:
73    SkImageDecoder* fDecoder;
74    int fWidth;
75    int fHeight;
76};
77
78static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
79    size_t bufferSize = 4096;
80    size_t streamLen = 0;
81    size_t len;
82    char* data = (char*)sk_malloc_throw(bufferSize);
83
84    while ((len = stream->read(data + streamLen,
85                    bufferSize - streamLen)) != 0) {
86        streamLen += len;
87        if (streamLen == bufferSize) {
88            bufferSize *= 2;
89            data = (char*)sk_realloc_throw(data, bufferSize);
90        }
91    }
92    data = (char*)sk_realloc_throw(data, streamLen);
93
94    SkMemoryStream* streamMem = new SkMemoryStream();
95    streamMem->setMemoryOwned(data, streamLen);
96    return streamMem;
97}
98
99static jobject createBitmapRegionDecoder(JNIEnv* env, SkStream* stream) {
100    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
101    int width, height;
102    if (NULL == decoder) {
103        doThrowIOE(env, "Image format not supported");
104        return nullObjectReturn("SkImageDecoder::Factory returned null");
105    }
106
107    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
108    decoder->setAllocator(javaAllocator);
109    javaAllocator->unref();
110
111    if (!decoder->buildTileIndex(stream, &width, &height)) {
112        char msg[100];
113        snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
114                decoder->getFormatName());
115        doThrowIOE(env, msg);
116        SkDELETE(decoder);
117        return nullObjectReturn("decoder->buildTileIndex returned false");
118    }
119
120    SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
121    return GraphicsJNI::createBitmapRegionDecoder(env, bm);
122}
123
124static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
125                                     int offset, int length, jboolean isShareable) {
126    /*  If isShareable we could decide to just wrap the java array and
127        share it, but that means adding a globalref to the java array object
128        For now we just always copy the array's data if isShareable.
129     */
130    AutoJavaByteArray ar(env, byteArray);
131    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
132
133    jobject brd = createBitmapRegionDecoder(env, stream);
134    SkSafeUnref(stream); // the decoder now holds a reference
135    return brd;
136}
137
138static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
139                                          jobject fileDescriptor, jboolean isShareable) {
140    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
141
142    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
143
144    struct stat fdStat;
145    if (fstat(descriptor, &fdStat) == -1) {
146        doThrowIOE(env, "broken file descriptor");
147        return nullObjectReturn("fstat return -1");
148    }
149
150    SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
151    SkMemoryStream* stream = new SkMemoryStream(data);
152
153    jobject brd = createBitmapRegionDecoder(env, stream);
154    SkSafeUnref(stream); // the decoder now holds a reference
155    return brd;
156}
157
158static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
159                                  jobject is,       // InputStream
160                                  jbyteArray storage, // byte[]
161                                  jboolean isShareable) {
162    jobject brd = NULL;
163    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
164
165    if (stream) {
166        // for now we don't allow shareable with java inputstreams
167        SkMemoryStream* mStream = buildSkMemoryStream(stream);
168        brd = createBitmapRegionDecoder(env, mStream);
169        SkSafeUnref(mStream); // the decoder now holds a reference
170        stream->unref();
171    }
172    return brd;
173}
174
175static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
176                                 jint native_asset, // Asset
177                                 jboolean isShareable) {
178    SkStream* stream, *assStream;
179    Asset* asset = reinterpret_cast<Asset*>(native_asset);
180    assStream = new AssetStreamAdaptor(asset);
181    stream = buildSkMemoryStream(assStream);
182    assStream->unref();
183
184    jobject brd = createBitmapRegionDecoder(env, stream);
185    SkSafeUnref(stream); // the decoder now holds a reference
186    return brd;
187}
188
189/*
190 * nine patch not supported
191 *
192 * purgeable not supported
193 * reportSizeToVM not supported
194 */
195static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
196                                int start_x, int start_y, int width, int height, jobject options) {
197    jobject tileBitmap = NULL;
198    SkImageDecoder *decoder = brd->getDecoder();
199    int sampleSize = 1;
200    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
201    bool doDither = true;
202    bool preferQualityOverSpeed = false;
203    bool requireUnpremultiplied = false;
204
205    if (NULL != options) {
206        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
207        // initialize these, in case we fail later on
208        env->SetIntField(options, gOptions_widthFieldID, -1);
209        env->SetIntField(options, gOptions_heightFieldID, -1);
210        env->SetObjectField(options, gOptions_mimeFieldID, 0);
211
212        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
213        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
214        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
215        preferQualityOverSpeed = env->GetBooleanField(options,
216                gOptions_preferQualityOverSpeedFieldID);
217        // Get the bitmap for re-use if it exists.
218        tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
219        requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
220    }
221
222    decoder->setDitherImage(doDither);
223    decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
224    decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
225    AutoDecoderCancel adc(options, decoder);
226
227    // To fix the race condition in case "requestCancelDecode"
228    // happens earlier than AutoDecoderCancel object is added
229    // to the gAutoDecoderCancelMutex linked list.
230    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
231        return nullObjectReturn("gOptions_mCancelID");;
232    }
233
234    SkIRect region;
235    region.fLeft = start_x;
236    region.fTop = start_y;
237    region.fRight = start_x + width;
238    region.fBottom = start_y + height;
239    SkBitmap* bitmap = NULL;
240    SkAutoTDelete<SkBitmap> adb;
241
242    if (tileBitmap != NULL) {
243        // Re-use bitmap.
244        bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap);
245    }
246    if (bitmap == NULL) {
247        bitmap = new SkBitmap;
248        adb.reset(bitmap);
249    }
250
251    if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
252        return nullObjectReturn("decoder->decodeRegion returned false");
253    }
254
255    // update options (if any)
256    if (NULL != options) {
257        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
258        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
259        // TODO: set the mimeType field with the data from the codec.
260        // but how to reuse a set of strings, rather than allocating new one
261        // each time?
262        env->SetObjectField(options, gOptions_mimeFieldID,
263                            getMimeTypeString(env, decoder->getFormat()));
264    }
265
266    if (tileBitmap != NULL) {
267        return tileBitmap;
268    }
269
270    // detach bitmap from its autodeleter, since we want to own it now
271    adb.detach();
272
273    JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
274    jbyteArray buff = allocator->getStorageObjAndReset();
275
276    int bitmapCreateFlags = 0;
277    if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
278    return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
279}
280
281static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
282    return brd->getHeight();
283}
284
285static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
286    return brd->getWidth();
287}
288
289static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
290    delete brd;
291}
292
293///////////////////////////////////////////////////////////////////////////////
294
295#include <android_runtime/AndroidRuntime.h>
296
297static JNINativeMethod gBitmapRegionDecoderMethods[] = {
298    {   "nativeDecodeRegion",
299        "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
300        (void*)nativeDecodeRegion},
301
302    {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
303
304    {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
305
306    {   "nativeClean", "(I)V", (void*)nativeClean},
307
308    {   "nativeNewInstance",
309        "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
310        (void*)nativeNewInstanceFromByteArray
311    },
312
313    {   "nativeNewInstance",
314        "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
315        (void*)nativeNewInstanceFromStream
316    },
317
318    {   "nativeNewInstance",
319        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
320        (void*)nativeNewInstanceFromFileDescriptor
321    },
322
323    {   "nativeNewInstance",
324        "(IZ)Landroid/graphics/BitmapRegionDecoder;",
325        (void*)nativeNewInstanceFromAsset
326    },
327};
328
329#define kClassPathName  "android/graphics/BitmapRegionDecoder"
330
331int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
332{
333    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
334            gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
335}
336