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