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 "BitmapFactory.h"
20 #include "CreateJavaOutputStreamAdaptor.h"
21 #include "GraphicsJNI.h"
22 #include "Utils.h"
23
24 #include "SkBitmap.h"
25 #include "SkBitmapRegionDecoder.h"
26 #include "SkCodec.h"
27 #include "SkData.h"
28 #include "SkUtils.h"
29 #include "SkPixelRef.h"
30 #include "SkStream.h"
31
32 #include "android_nio_utils.h"
33 #include "android_util_Binder.h"
34 #include "core_jni_helpers.h"
35
36 #include <nativehelper/JNIHelp.h>
37 #include <androidfw/Asset.h>
38 #include <binder/Parcel.h>
39 #include <jni.h>
40 #include <sys/stat.h>
41
42 #include <memory>
43
44 using namespace android;
45
createBitmapRegionDecoder(JNIEnv * env,std::unique_ptr<SkStreamRewindable> stream)46 static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream) {
47 std::unique_ptr<SkBitmapRegionDecoder> brd(
48 SkBitmapRegionDecoder::Create(stream.release(),
49 SkBitmapRegionDecoder::kAndroidCodec_Strategy));
50 if (!brd) {
51 doThrowIOE(env, "Image format not supported");
52 return nullObjectReturn("CreateBitmapRegionDecoder returned null");
53 }
54
55 return GraphicsJNI::createBitmapRegionDecoder(env, brd.release());
56 }
57
nativeNewInstanceFromByteArray(JNIEnv * env,jobject,jbyteArray byteArray,jint offset,jint length,jboolean isShareable)58 static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
59 jint offset, jint length, jboolean isShareable) {
60 /* If isShareable we could decide to just wrap the java array and
61 share it, but that means adding a globalref to the java array object
62 For now we just always copy the array's data if isShareable.
63 */
64 AutoJavaByteArray ar(env, byteArray);
65 std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, true));
66
67 // the decoder owns the stream.
68 jobject brd = createBitmapRegionDecoder(env, std::move(stream));
69 return brd;
70 }
71
nativeNewInstanceFromFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor,jboolean isShareable)72 static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
73 jobject fileDescriptor, jboolean isShareable) {
74 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
75
76 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
77
78 struct stat fdStat;
79 if (fstat(descriptor, &fdStat) == -1) {
80 doThrowIOE(env, "broken file descriptor");
81 return nullObjectReturn("fstat return -1");
82 }
83
84 sk_sp<SkData> data(SkData::MakeFromFD(descriptor));
85 std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(std::move(data)));
86
87 // the decoder owns the stream.
88 jobject brd = createBitmapRegionDecoder(env, std::move(stream));
89 return brd;
90 }
91
nativeNewInstanceFromStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage,jboolean isShareable)92 static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
93 jobject is, // InputStream
94 jbyteArray storage, // byte[]
95 jboolean isShareable) {
96 jobject brd = NULL;
97 // for now we don't allow shareable with java inputstreams
98 std::unique_ptr<SkStreamRewindable> stream(CopyJavaInputStream(env, is, storage));
99
100 if (stream) {
101 // the decoder owns the stream.
102 brd = createBitmapRegionDecoder(env, std::move(stream));
103 }
104 return brd;
105 }
106
nativeNewInstanceFromAsset(JNIEnv * env,jobject clazz,jlong native_asset,jboolean isShareable)107 static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
108 jlong native_asset, // Asset
109 jboolean isShareable) {
110 Asset* asset = reinterpret_cast<Asset*>(native_asset);
111 std::unique_ptr<SkMemoryStream> stream(CopyAssetToStream(asset));
112 if (NULL == stream) {
113 return NULL;
114 }
115
116 // the decoder owns the stream.
117 jobject brd = createBitmapRegionDecoder(env, std::move(stream));
118 return brd;
119 }
120
121 /*
122 * nine patch not supported
123 * purgeable not supported
124 * reportSizeToVM not supported
125 */
nativeDecodeRegion(JNIEnv * env,jobject,jlong brdHandle,jint inputX,jint inputY,jint inputWidth,jint inputHeight,jobject options)126 static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
127 jint inputY, jint inputWidth, jint inputHeight, jobject options) {
128
129 // Set default options.
130 int sampleSize = 1;
131 SkColorType colorType = kN32_SkColorType;
132 bool requireUnpremul = false;
133 jobject javaBitmap = NULL;
134 bool isHardware = false;
135 sk_sp<SkColorSpace> colorSpace = nullptr;
136 // Update the default options with any options supplied by the client.
137 if (NULL != options) {
138 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
139 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
140 colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
141 jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
142 colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
143 isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
144 requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
145 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
146 // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will
147 // ignore the values of these fields.
148
149 // Initialize these fields to indicate a failure. If the decode succeeds, we
150 // will update them later on.
151 env->SetIntField(options, gOptions_widthFieldID, -1);
152 env->SetIntField(options, gOptions_heightFieldID, -1);
153 env->SetObjectField(options, gOptions_mimeFieldID, 0);
154 env->SetObjectField(options, gOptions_outConfigFieldID, 0);
155 env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0);
156 }
157
158 // Recycle a bitmap if possible.
159 android::Bitmap* recycledBitmap = nullptr;
160 size_t recycledBytes = 0;
161 if (javaBitmap) {
162 recycledBitmap = &bitmap::toBitmap(env, javaBitmap);
163 if (recycledBitmap->isImmutable()) {
164 ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
165 }
166 recycledBytes = bitmap::getBitmapAllocationByteCount(env, javaBitmap);
167 }
168
169 SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
170 SkColorType decodeColorType = brd->computeOutputColorType(colorType);
171
172 // Set up the pixel allocator
173 SkBRDAllocator* allocator = nullptr;
174 RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
175 HeapAllocator heapAlloc;
176 if (javaBitmap) {
177 allocator = &recycleAlloc;
178 // We are required to match the color type of the recycled bitmap.
179 decodeColorType = recycledBitmap->info().colorType();
180 } else {
181 allocator = &heapAlloc;
182 }
183
184 sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace(
185 decodeColorType, colorSpace);
186
187 // Decode the region.
188 SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
189 SkBitmap bitmap;
190 if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize,
191 decodeColorType, requireUnpremul, decodeColorSpace)) {
192 return nullObjectReturn("Failed to decode region.");
193 }
194
195 // If the client provided options, indicate that the decode was successful.
196 if (NULL != options) {
197 env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
198 env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
199
200 env->SetObjectField(options, gOptions_mimeFieldID,
201 encodedFormatToString(env, (SkEncodedImageFormat)brd->getEncodedFormat()));
202 if (env->ExceptionCheck()) {
203 return nullObjectReturn("OOM in encodedFormatToString()");
204 }
205
206 jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
207 if (isHardware) {
208 configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
209 }
210 jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
211 gBitmapConfig_nativeToConfigMethodID, configID);
212 env->SetObjectField(options, gOptions_outConfigFieldID, config);
213
214 env->SetObjectField(options, gOptions_outColorSpaceFieldID,
215 GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
216 }
217
218 // If we may have reused a bitmap, we need to indicate that the pixels have changed.
219 if (javaBitmap) {
220 recycleAlloc.copyIfNecessary();
221 bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
222 return javaBitmap;
223 }
224
225 int bitmapCreateFlags = 0;
226 if (!requireUnpremul) {
227 bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
228 }
229 if (isHardware) {
230 sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap);
231 return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
232 }
233 return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
234 }
235
nativeGetHeight(JNIEnv * env,jobject,jlong brdHandle)236 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
237 SkBitmapRegionDecoder* brd =
238 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
239 return static_cast<jint>(brd->height());
240 }
241
nativeGetWidth(JNIEnv * env,jobject,jlong brdHandle)242 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
243 SkBitmapRegionDecoder* brd =
244 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
245 return static_cast<jint>(brd->width());
246 }
247
nativeClean(JNIEnv * env,jobject,jlong brdHandle)248 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
249 SkBitmapRegionDecoder* brd =
250 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
251 delete brd;
252 }
253
254 ///////////////////////////////////////////////////////////////////////////////
255
256 static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
257 { "nativeDecodeRegion",
258 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
259 (void*)nativeDecodeRegion},
260
261 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
262
263 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
264
265 { "nativeClean", "(J)V", (void*)nativeClean},
266
267 { "nativeNewInstance",
268 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
269 (void*)nativeNewInstanceFromByteArray
270 },
271
272 { "nativeNewInstance",
273 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
274 (void*)nativeNewInstanceFromStream
275 },
276
277 { "nativeNewInstance",
278 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
279 (void*)nativeNewInstanceFromFileDescriptor
280 },
281
282 { "nativeNewInstance",
283 "(JZ)Landroid/graphics/BitmapRegionDecoder;",
284 (void*)nativeNewInstanceFromAsset
285 },
286 };
287
register_android_graphics_BitmapRegionDecoder(JNIEnv * env)288 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
289 {
290 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
291 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
292 }
293