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