blob: e7d2422eeb9b61aabbabffe684f3e31760757927 [file] [log] [blame]
Wei-Ta Chen6b849e22010-09-07 17:32:18 +08001/*
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"
Derek Sollenberger5827cb52013-07-26 14:58:06 -040020#include "SkData.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080021#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"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080029#include "CreateJavaOutputStreamAdaptor.h"
30#include "Utils.h"
Elliott Hughesa3804cf2011-04-11 16:50:19 -070031#include "JNIHelp.h"
Owen Linf970c2e2012-04-25 18:49:09 +080032#include "SkTScopedPtr.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080033
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>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080041#include <androidfw/Asset.h>
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080042#include <sys/stat.h>
43
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080044#if 0
45 #define TRACE_BITMAP(code) code
46#else
47 #define TRACE_BITMAP(code)
48#endif
49
50using namespace android;
51
Derek Sollenberger5827cb52013-07-26 14:58:06 -040052class 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
Derek Sollenberger5827cb52013-07-26 14:58:06 -040079static jobject createBitmapRegionDecoder(JNIEnv* env, SkStream* stream) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080080 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
Patrick Dubroye4ac2d62010-12-01 11:23:13 -080087 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080088 decoder->setAllocator(javaAllocator);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080089 javaAllocator->unref();
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080090
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);
Derek Sollenberger5827cb52013-07-26 14:58:06 -040096 SkDELETE(decoder);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080097 return nullObjectReturn("decoder->buildTileIndex returned false");
98 }
99
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400100 SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800101 return GraphicsJNI::createBitmapRegionDecoder(env, bm);
102}
103
104static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
Ashok Bhatb091d472014-01-08 14:32:49 +0000105 jint offset, jint length, jboolean isShareable) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800106 /* 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);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400112
113 jobject brd = createBitmapRegionDecoder(env, stream);
114 SkSafeUnref(stream); // the decoder now holds a reference
115 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800116}
117
118static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
119 jobject fileDescriptor, jboolean isShareable) {
120 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
121
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700122 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400123
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800124 struct stat fdStat;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800125 if (fstat(descriptor, &fdStat) == -1) {
126 doThrowIOE(env, "broken file descriptor");
127 return nullObjectReturn("fstat return -1");
128 }
129
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400130 SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
131 SkMemoryStream* stream = new SkMemoryStream(data);
Wei-Ta Chen58c1579c2010-10-21 20:56:28 -0700132
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400133 jobject brd = createBitmapRegionDecoder(env, stream);
134 SkSafeUnref(stream); // the decoder now holds a reference
135 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800136}
137
138static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
139 jobject is, // InputStream
140 jbyteArray storage, // byte[]
141 jboolean isShareable) {
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400142 jobject brd = NULL;
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400143 // for now we don't allow shareable with java inputstreams
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400144 SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800145
146 if (stream) {
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400147 brd = createBitmapRegionDecoder(env, stream);
148 stream->unref(); // the decoder now holds a reference
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800149 }
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400150 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800151}
152
153static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
Ashok Bhatb091d472014-01-08 14:32:49 +0000154 jlong native_asset, // Asset
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800155 jboolean isShareable) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800156 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400157 SkAutoTUnref<SkMemoryStream> stream(CopyAssetToStream(asset));
158 if (NULL == stream.get()) {
159 return NULL;
160 }
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400161
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400162 jobject brd = createBitmapRegionDecoder(env, stream.get());
163 // The decoder now holds a reference to stream.
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400164 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800165}
166
167/*
168 * nine patch not supported
169 *
170 * purgeable not supported
171 * reportSizeToVM not supported
172 */
Ashok Bhatb091d472014-01-08 14:32:49 +0000173static 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);
Owen Linf970c2e2012-04-25 18:49:09 +0800176 jobject tileBitmap = NULL;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800177 SkImageDecoder *decoder = brd->getDecoder();
178 int sampleSize = 1;
179 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
180 bool doDither = true;
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800181 bool preferQualityOverSpeed = false;
Chris Craik1abf5d62013-08-16 12:47:03 -0700182 bool requireUnpremultiplied = false;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800183
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);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800194 preferQualityOverSpeed = env->GetBooleanField(options,
195 gOptions_preferQualityOverSpeedFieldID);
Owen Linf970c2e2012-04-25 18:49:09 +0800196 // Get the bitmap for re-use if it exists.
197 tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
Leon Scroggins III2cc409a2013-08-26 14:27:15 -0400198 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800199 }
200
201 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800202 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
Chris Craik1abf5d62013-08-16 12:47:03 -0700203 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
204 AutoDecoderCancel adc(options, decoder);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800205
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;
Owen Linf970c2e2012-04-25 18:49:09 +0800218 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 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800229
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
Owen Linf970c2e2012-04-25 18:49:09 +0800245 if (tileBitmap != NULL) {
246 return tileBitmap;
247 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800248
Owen Linf970c2e2012-04-25 18:49:09 +0800249 // detach bitmap from its autodeleter, since we want to own it now
250 adb.release();
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800251
Patrick Dubroyafde46e2010-12-15 11:52:01 -0800252 JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
253 jbyteArray buff = allocator->getStorageObjAndReset();
Chris Craik1abf5d62013-08-16 12:47:03 -0700254
255 int bitmapCreateFlags = 0;
256 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
257 return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800258}
259
Ashok Bhatb091d472014-01-08 14:32:49 +0000260static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
261 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
262 return static_cast<jint>(brd->getHeight());
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800263}
264
Ashok Bhatb091d472014-01-08 14:32:49 +0000265static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
266 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
267 return static_cast<jint>(brd->getWidth());
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800268}
269
Ashok Bhatb091d472014-01-08 14:32:49 +0000270static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
271 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800272 delete brd;
273}
274
275///////////////////////////////////////////////////////////////////////////////
276
277#include <android_runtime/AndroidRuntime.h>
278
279static JNINativeMethod gBitmapRegionDecoderMethods[] = {
280 { "nativeDecodeRegion",
Ashok Bhatb091d472014-01-08 14:32:49 +0000281 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800282 (void*)nativeDecodeRegion},
283
Ashok Bhatb091d472014-01-08 14:32:49 +0000284 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800285
Ashok Bhatb091d472014-01-08 14:32:49 +0000286 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800287
Ashok Bhatb091d472014-01-08 14:32:49 +0000288 { "nativeClean", "(J)V", (void*)nativeClean},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800289
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",
Ashok Bhatb091d472014-01-08 14:32:49 +0000306 "(JZ)Landroid/graphics/BitmapRegionDecoder;",
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800307 (void*)nativeNewInstanceFromAsset
308 },
309};
310
311#define kClassPathName "android/graphics/BitmapRegionDecoder"
312
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800313int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
314{
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800315 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
316 gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
317}