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