blob: 098643b1eda3291328f7085ef76474b3e5667c58 [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"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080032
33#include <android_runtime/AndroidRuntime.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>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080040#include <androidfw/Asset.h>
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080041#include <sys/stat.h>
42
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080043#if 0
44 #define TRACE_BITMAP(code) code
45#else
46 #define TRACE_BITMAP(code)
47#endif
48
49using namespace android;
50
Derek Sollenberger5827cb52013-07-26 14:58:06 -040051class SkBitmapRegionDecoder {
52public:
53 SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
54 fDecoder = decoder;
55 fWidth = width;
56 fHeight = height;
57 }
58 ~SkBitmapRegionDecoder() {
59 SkDELETE(fDecoder);
60 }
61
62 bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
63 SkBitmap::Config pref, int sampleSize) {
64 fDecoder->setSampleSize(sampleSize);
65 return fDecoder->decodeRegion(bitmap, rect, pref);
66 }
67
68 SkImageDecoder* getDecoder() const { return fDecoder; }
69 int getWidth() const { return fWidth; }
70 int getHeight() const { return fHeight; }
71
72private:
73 SkImageDecoder* fDecoder;
74 int fWidth;
75 int fHeight;
76};
77
Derek Sollenbergered79ff02013-11-20 14:50:51 -050078static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080079 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
80 int width, height;
81 if (NULL == decoder) {
82 doThrowIOE(env, "Image format not supported");
83 return nullObjectReturn("SkImageDecoder::Factory returned null");
84 }
85
Patrick Dubroye4ac2d62010-12-01 11:23:13 -080086 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080087 decoder->setAllocator(javaAllocator);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080088 javaAllocator->unref();
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080089
90 if (!decoder->buildTileIndex(stream, &width, &height)) {
91 char msg[100];
92 snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
93 decoder->getFormatName());
94 doThrowIOE(env, msg);
Derek Sollenberger5827cb52013-07-26 14:58:06 -040095 SkDELETE(decoder);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080096 return nullObjectReturn("decoder->buildTileIndex returned false");
97 }
98
Derek Sollenberger5827cb52013-07-26 14:58:06 -040099 SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800100 return GraphicsJNI::createBitmapRegionDecoder(env, bm);
101}
102
103static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
Ashok Bhatb091d472014-01-08 14:32:49 +0000104 jint offset, jint length, jboolean isShareable) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800105 /* If isShareable we could decide to just wrap the java array and
106 share it, but that means adding a globalref to the java array object
107 For now we just always copy the array's data if isShareable.
108 */
109 AutoJavaByteArray ar(env, byteArray);
Derek Sollenbergered79ff02013-11-20 14:50:51 -0500110 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400111
112 jobject brd = createBitmapRegionDecoder(env, stream);
113 SkSafeUnref(stream); // the decoder now holds a reference
114 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800115}
116
117static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
118 jobject fileDescriptor, jboolean isShareable) {
119 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
120
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700121 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400122
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800123 struct stat fdStat;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800124 if (fstat(descriptor, &fdStat) == -1) {
125 doThrowIOE(env, "broken file descriptor");
126 return nullObjectReturn("fstat return -1");
127 }
128
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400129 SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
130 SkMemoryStream* stream = new SkMemoryStream(data);
Wei-Ta Chen58c1579c2010-10-21 20:56:28 -0700131
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400132 jobject brd = createBitmapRegionDecoder(env, stream);
133 SkSafeUnref(stream); // the decoder now holds a reference
134 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800135}
136
137static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
138 jobject is, // InputStream
139 jbyteArray storage, // byte[]
140 jboolean isShareable) {
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400141 jobject brd = NULL;
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400142 // for now we don't allow shareable with java inputstreams
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400143 SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800144
145 if (stream) {
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400146 brd = createBitmapRegionDecoder(env, stream);
147 stream->unref(); // the decoder now holds a reference
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800148 }
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400149 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800150}
151
152static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
Ashok Bhatb091d472014-01-08 14:32:49 +0000153 jlong native_asset, // Asset
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800154 jboolean isShareable) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800155 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400156 SkAutoTUnref<SkMemoryStream> stream(CopyAssetToStream(asset));
157 if (NULL == stream.get()) {
158 return NULL;
159 }
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400160
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400161 jobject brd = createBitmapRegionDecoder(env, stream.get());
162 // The decoder now holds a reference to stream.
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400163 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800164}
165
166/*
167 * nine patch not supported
168 *
169 * purgeable not supported
170 * reportSizeToVM not supported
171 */
Ashok Bhatb091d472014-01-08 14:32:49 +0000172static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle,
173 jint start_x, jint start_y, jint width, jint height, jobject options) {
174 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
Owen Linf970c2e2012-04-25 18:49:09 +0800175 jobject tileBitmap = NULL;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800176 SkImageDecoder *decoder = brd->getDecoder();
177 int sampleSize = 1;
178 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
179 bool doDither = true;
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800180 bool preferQualityOverSpeed = false;
Chris Craik1abf5d62013-08-16 12:47:03 -0700181 bool requireUnpremultiplied = false;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800182
183 if (NULL != options) {
184 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
185 // initialize these, in case we fail later on
186 env->SetIntField(options, gOptions_widthFieldID, -1);
187 env->SetIntField(options, gOptions_heightFieldID, -1);
188 env->SetObjectField(options, gOptions_mimeFieldID, 0);
189
190 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
191 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
192 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800193 preferQualityOverSpeed = env->GetBooleanField(options,
194 gOptions_preferQualityOverSpeedFieldID);
Owen Linf970c2e2012-04-25 18:49:09 +0800195 // Get the bitmap for re-use if it exists.
196 tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
Leon Scroggins III2cc409a2013-08-26 14:27:15 -0400197 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800198 }
199
200 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800201 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
Chris Craik1abf5d62013-08-16 12:47:03 -0700202 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
203 AutoDecoderCancel adc(options, decoder);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800204
205 // To fix the race condition in case "requestCancelDecode"
206 // happens earlier than AutoDecoderCancel object is added
207 // to the gAutoDecoderCancelMutex linked list.
208 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
209 return nullObjectReturn("gOptions_mCancelID");;
210 }
211
212 SkIRect region;
213 region.fLeft = start_x;
214 region.fTop = start_y;
215 region.fRight = start_x + width;
216 region.fBottom = start_y + height;
Owen Linf970c2e2012-04-25 18:49:09 +0800217 SkBitmap* bitmap = NULL;
Mike Kleinf5b43bd2013-08-29 11:15:06 -0400218 SkAutoTDelete<SkBitmap> adb;
Owen Linf970c2e2012-04-25 18:49:09 +0800219
220 if (tileBitmap != NULL) {
221 // Re-use bitmap.
222 bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap);
223 }
224 if (bitmap == NULL) {
225 bitmap = new SkBitmap;
226 adb.reset(bitmap);
227 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800228
229 if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
230 return nullObjectReturn("decoder->decodeRegion returned false");
231 }
232
233 // update options (if any)
234 if (NULL != options) {
235 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
236 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
237 // TODO: set the mimeType field with the data from the codec.
238 // but how to reuse a set of strings, rather than allocating new one
239 // each time?
240 env->SetObjectField(options, gOptions_mimeFieldID,
241 getMimeTypeString(env, decoder->getFormat()));
242 }
243
Owen Linf970c2e2012-04-25 18:49:09 +0800244 if (tileBitmap != NULL) {
245 return tileBitmap;
246 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800247
Owen Linf970c2e2012-04-25 18:49:09 +0800248 // detach bitmap from its autodeleter, since we want to own it now
Mike Kleinf5b43bd2013-08-29 11:15:06 -0400249 adb.detach();
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800250
Patrick Dubroyafde46e2010-12-15 11:52:01 -0800251 JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
252 jbyteArray buff = allocator->getStorageObjAndReset();
Chris Craik1abf5d62013-08-16 12:47:03 -0700253
254 int bitmapCreateFlags = 0;
255 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
256 return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800257}
258
Ashok Bhatb091d472014-01-08 14:32:49 +0000259static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
260 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
261 return static_cast<jint>(brd->getHeight());
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800262}
263
Ashok Bhatb091d472014-01-08 14:32:49 +0000264static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
265 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
266 return static_cast<jint>(brd->getWidth());
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800267}
268
Ashok Bhatb091d472014-01-08 14:32:49 +0000269static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
270 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800271 delete brd;
272}
273
274///////////////////////////////////////////////////////////////////////////////
275
276#include <android_runtime/AndroidRuntime.h>
277
278static JNINativeMethod gBitmapRegionDecoderMethods[] = {
279 { "nativeDecodeRegion",
Ashok Bhatb091d472014-01-08 14:32:49 +0000280 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800281 (void*)nativeDecodeRegion},
282
Ashok Bhatb091d472014-01-08 14:32:49 +0000283 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800284
Ashok Bhatb091d472014-01-08 14:32:49 +0000285 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800286
Ashok Bhatb091d472014-01-08 14:32:49 +0000287 { "nativeClean", "(J)V", (void*)nativeClean},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800288
289 { "nativeNewInstance",
290 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
291 (void*)nativeNewInstanceFromByteArray
292 },
293
294 { "nativeNewInstance",
295 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
296 (void*)nativeNewInstanceFromStream
297 },
298
299 { "nativeNewInstance",
300 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
301 (void*)nativeNewInstanceFromFileDescriptor
302 },
303
304 { "nativeNewInstance",
Ashok Bhatb091d472014-01-08 14:32:49 +0000305 "(JZ)Landroid/graphics/BitmapRegionDecoder;",
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800306 (void*)nativeNewInstanceFromAsset
307 },
308};
309
310#define kClassPathName "android/graphics/BitmapRegionDecoder"
311
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800312int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
313{
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800314 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
315 gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
316}