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