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
43 using namespace android;
44
45 class SkBitmapRegionDecoder {
46 public:
SkBitmapRegionDecoder(SkImageDecoder * decoder,int width,int height)47 SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
48 fDecoder = decoder;
49 fWidth = width;
50 fHeight = height;
51 }
~SkBitmapRegionDecoder()52 ~SkBitmapRegionDecoder() {
53 SkDELETE(fDecoder);
54 }
55
decodeRegion(SkBitmap * bitmap,const SkIRect & rect,SkColorType pref,int sampleSize)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
getDecoder() const62 SkImageDecoder* getDecoder() const { return fDecoder; }
getWidth() const63 int getWidth() const { return fWidth; }
getHeight() const64 int getHeight() const { return fHeight; }
65
66 private:
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.
createBitmapRegionDecoder(JNIEnv * env,SkStreamRewindable * stream)74 static 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
nativeNewInstanceFromByteArray(JNIEnv * env,jobject,jbyteArray byteArray,jint offset,jint length,jboolean isShareable)101 static 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
nativeNewInstanceFromFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor,jboolean isShareable)115 static 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
nativeNewInstanceFromStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage,jboolean isShareable)135 static 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
nativeNewInstanceFromAsset(JNIEnv * env,jobject clazz,jlong native_asset,jboolean isShareable)150 static 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 */
nativeDecodeRegion(JNIEnv * env,jobject,jlong brdHandle,jint start_x,jint start_y,jint width,jint height,jobject options)170 static 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;
216
217 if (tileBitmap != NULL) {
218 // Re-use bitmap.
219 GraphicsJNI::getSkBitmap(env, tileBitmap, &bitmap);
220 }
221
222 if (!brd->decodeRegion(&bitmap, region, prefColorType, sampleSize)) {
223 return nullObjectReturn("decoder->decodeRegion returned false");
224 }
225
226 // update options (if any)
227 if (NULL != options) {
228 env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
229 env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
230 // TODO: set the mimeType field with the data from the codec.
231 // but how to reuse a set of strings, rather than allocating new one
232 // each time?
233 env->SetObjectField(options, gOptions_mimeFieldID,
234 getMimeTypeString(env, decoder->getFormat()));
235 }
236
237 if (tileBitmap != NULL) {
238 bitmap.notifyPixelsChanged();
239 return tileBitmap;
240 }
241
242 JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
243
244 int bitmapCreateFlags = 0;
245 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
246 return GraphicsJNI::createBitmap(env, allocator->getStorageObjAndReset(),
247 bitmapCreateFlags);
248 }
249
nativeGetHeight(JNIEnv * env,jobject,jlong brdHandle)250 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
251 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
252 return static_cast<jint>(brd->getHeight());
253 }
254
nativeGetWidth(JNIEnv * env,jobject,jlong brdHandle)255 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
256 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
257 return static_cast<jint>(brd->getWidth());
258 }
259
nativeClean(JNIEnv * env,jobject,jlong brdHandle)260 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
261 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
262 delete brd;
263 }
264
265 ///////////////////////////////////////////////////////////////////////////////
266
267 static JNINativeMethod gBitmapRegionDecoderMethods[] = {
268 { "nativeDecodeRegion",
269 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
270 (void*)nativeDecodeRegion},
271
272 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
273
274 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
275
276 { "nativeClean", "(J)V", (void*)nativeClean},
277
278 { "nativeNewInstance",
279 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
280 (void*)nativeNewInstanceFromByteArray
281 },
282
283 { "nativeNewInstance",
284 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
285 (void*)nativeNewInstanceFromStream
286 },
287
288 { "nativeNewInstance",
289 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
290 (void*)nativeNewInstanceFromFileDescriptor
291 },
292
293 { "nativeNewInstance",
294 "(JZ)Landroid/graphics/BitmapRegionDecoder;",
295 (void*)nativeNewInstanceFromAsset
296 },
297 };
298
register_android_graphics_BitmapRegionDecoder(JNIEnv * env)299 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
300 {
301 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
302 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
303 }
304