blob: ee3e2094267f4788e9d07a9eaf5e7da12849e266 [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"
20#include "SkImageEncoder.h"
21#include "GraphicsJNI.h"
22#include "SkUtils.h"
23#include "SkTemplates.h"
24#include "SkPixelRef.h"
25#include "SkStream.h"
26#include "BitmapFactory.h"
27#include "AutoDecodeCancel.h"
28#include "SkBitmapRegionDecoder.h"
29#include "CreateJavaOutputStreamAdaptor.h"
30#include "Utils.h"
31
32#include <android_runtime/AndroidRuntime.h>
33#include "android_util_Binder.h"
34#include "android_nio_utils.h"
35#include "CreateJavaOutputStreamAdaptor.h"
36
37#include <binder/Parcel.h>
38#include <jni.h>
39#include <utils/Asset.h>
40#include <sys/stat.h>
41
42static jclass gFileDescriptor_class;
43static jfieldID gFileDescriptor_descriptor;
44
45#if 0
46 #define TRACE_BITMAP(code) code
47#else
48 #define TRACE_BITMAP(code)
49#endif
50
51using namespace android;
52
53static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
54 size_t bufferSize = 4096;
55 size_t streamLen = 0;
56 size_t len;
57 char* data = (char*)sk_malloc_throw(bufferSize);
58
59 while ((len = stream->read(data + streamLen,
60 bufferSize - streamLen)) != 0) {
61 streamLen += len;
62 if (streamLen == bufferSize) {
63 bufferSize *= 2;
64 data = (char*)sk_realloc_throw(data, bufferSize);
65 }
66 }
67 data = (char*)sk_realloc_throw(data, streamLen);
Wei-Ta Chen58c1579c2010-10-21 20:56:28 -070068
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080069 SkMemoryStream* streamMem = new SkMemoryStream();
70 streamMem->setMemoryOwned(data, streamLen);
71 return streamMem;
72}
73
74static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
75 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
76 int width, height;
77 if (NULL == decoder) {
78 doThrowIOE(env, "Image format not supported");
79 return nullObjectReturn("SkImageDecoder::Factory returned null");
80 }
81
Patrick Dubroye4ac2d62010-12-01 11:23:13 -080082 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080083 decoder->setAllocator(javaAllocator);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080084 javaAllocator->unref();
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080085
86 if (!decoder->buildTileIndex(stream, &width, &height)) {
87 char msg[100];
88 snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
89 decoder->getFormatName());
90 doThrowIOE(env, msg);
91 return nullObjectReturn("decoder->buildTileIndex returned false");
92 }
93
Chih-Chung Changb1a04d52011-03-08 18:47:23 +080094 SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, stream, width, height);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080095
96 return GraphicsJNI::createBitmapRegionDecoder(env, bm);
97}
98
99static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
100 int offset, int length, jboolean isShareable) {
101 /* If isShareable we could decide to just wrap the java array and
102 share it, but that means adding a globalref to the java array object
103 For now we just always copy the array's data if isShareable.
104 */
105 AutoJavaByteArray ar(env, byteArray);
106 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
107 return doBuildTileIndex(env, stream);
108}
109
110static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
111 jobject fileDescriptor, jboolean isShareable) {
112 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
113
114 jint descriptor = env->GetIntField(fileDescriptor,
115 gFileDescriptor_descriptor);
116 SkStream *stream = NULL;
117 struct stat fdStat;
118 int newFD;
119 if (fstat(descriptor, &fdStat) == -1) {
120 doThrowIOE(env, "broken file descriptor");
121 return nullObjectReturn("fstat return -1");
122 }
123
124 if (isShareable &&
125 S_ISREG(fdStat.st_mode) &&
126 (newFD = ::dup(descriptor)) != -1) {
127 SkFDStream* fdStream = new SkFDStream(newFD, true);
128 if (!fdStream->isValid()) {
129 fdStream->unref();
130 return NULL;
131 }
132 stream = fdStream;
133 } else {
Wei-Ta Chen58c1579c2010-10-21 20:56:28 -0700134 /* Restore our offset when we leave, so we can be called more than once
135 with the same descriptor. This is only required if we didn't dup the
136 file descriptor, but it is OK to do it all the time.
137 */
138 AutoFDSeek as(descriptor);
139
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800140 SkFDStream* fdStream = new SkFDStream(descriptor, false);
141 if (!fdStream->isValid()) {
142 fdStream->unref();
143 return NULL;
144 }
145 stream = buildSkMemoryStream(fdStream);
146 fdStream->unref();
147 }
148
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800149 return doBuildTileIndex(env, stream);
150}
151
152static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
153 jobject is, // InputStream
154 jbyteArray storage, // byte[]
155 jboolean isShareable) {
156 jobject largeBitmap = NULL;
157 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
158
159 if (stream) {
160 // for now we don't allow shareable with java inputstreams
161 SkMemoryStream *mStream = buildSkMemoryStream(stream);
162 largeBitmap = doBuildTileIndex(env, mStream);
163 stream->unref();
164 }
165 return largeBitmap;
166}
167
168static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
169 jint native_asset, // Asset
170 jboolean isShareable) {
171 SkStream* stream, *assStream;
172 Asset* asset = reinterpret_cast<Asset*>(native_asset);
173 assStream = new AssetStreamAdaptor(asset);
174 stream = buildSkMemoryStream(assStream);
175 assStream->unref();
176 return doBuildTileIndex(env, stream);
177}
178
179/*
180 * nine patch not supported
181 *
182 * purgeable not supported
183 * reportSizeToVM not supported
184 */
185static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
186 int start_x, int start_y, int width, int height, jobject options) {
187 SkImageDecoder *decoder = brd->getDecoder();
188 int sampleSize = 1;
189 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
190 bool doDither = true;
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800191 bool preferQualityOverSpeed = false;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800192
193 if (NULL != options) {
194 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
195 // initialize these, in case we fail later on
196 env->SetIntField(options, gOptions_widthFieldID, -1);
197 env->SetIntField(options, gOptions_heightFieldID, -1);
198 env->SetObjectField(options, gOptions_mimeFieldID, 0);
199
200 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
201 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
202 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800203 preferQualityOverSpeed = env->GetBooleanField(options,
204 gOptions_preferQualityOverSpeedFieldID);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800205 }
206
207 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800208 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800209 SkBitmap* bitmap = new SkBitmap;
210 SkAutoTDelete<SkBitmap> adb(bitmap);
211 AutoDecoderCancel adc(options, decoder);
212
213 // To fix the race condition in case "requestCancelDecode"
214 // happens earlier than AutoDecoderCancel object is added
215 // to the gAutoDecoderCancelMutex linked list.
216 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
217 return nullObjectReturn("gOptions_mCancelID");;
218 }
219
220 SkIRect region;
221 region.fLeft = start_x;
222 region.fTop = start_y;
223 region.fRight = start_x + width;
224 region.fBottom = start_y + height;
225
226 if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
227 return nullObjectReturn("decoder->decodeRegion returned false");
228 }
229
230 // update options (if any)
231 if (NULL != options) {
232 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
233 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
234 // TODO: set the mimeType field with the data from the codec.
235 // but how to reuse a set of strings, rather than allocating new one
236 // each time?
237 env->SetObjectField(options, gOptions_mimeFieldID,
238 getMimeTypeString(env, decoder->getFormat()));
239 }
240
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800241 // detach bitmap from its autodeleter, since we want to own it now
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800242 adb.detach();
243
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800244 SkPixelRef* pr = bitmap->pixelRef();
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800245 // promise we will never change our pixels (great for sharing and pictures)
246 pr->setImmutable();
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800247
Patrick Dubroyafde46e2010-12-15 11:52:01 -0800248 JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
249 jbyteArray buff = allocator->getStorageObjAndReset();
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800250 return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800251}
252
253static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
254 return brd->getHeight();
255}
256
257static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
258 return brd->getWidth();
259}
260
261static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
262 delete brd;
263}
264
265///////////////////////////////////////////////////////////////////////////////
266
267#include <android_runtime/AndroidRuntime.h>
268
269static JNINativeMethod gBitmapRegionDecoderMethods[] = {
270 { "nativeDecodeRegion",
271 "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
272 (void*)nativeDecodeRegion},
273
274 { "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
275
276 { "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
277
278 { "nativeClean", "(I)V", (void*)nativeClean},
279
280 { "nativeNewInstance",
281 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
282 (void*)nativeNewInstanceFromByteArray
283 },
284
285 { "nativeNewInstance",
286 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
287 (void*)nativeNewInstanceFromStream
288 },
289
290 { "nativeNewInstance",
291 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
292 (void*)nativeNewInstanceFromFileDescriptor
293 },
294
295 { "nativeNewInstance",
296 "(IZ)Landroid/graphics/BitmapRegionDecoder;",
297 (void*)nativeNewInstanceFromAsset
298 },
299};
300
301#define kClassPathName "android/graphics/BitmapRegionDecoder"
302
Owen Lin0ab98512010-09-29 23:08:49 +0800303static jclass make_globalref(JNIEnv* env, const char classname[]) {
304 jclass c = env->FindClass(classname);
305 SkASSERT(c);
306 return (jclass)env->NewGlobalRef(c);
307}
308
309static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
310 const char fieldname[], const char type[]) {
311 jfieldID id = env->GetFieldID(clazz, fieldname, type);
312 SkASSERT(id);
313 return id;
314}
315
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800316int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
317int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
318{
Owen Lin0ab98512010-09-29 23:08:49 +0800319
320 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
321 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800322 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
323 gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
324}