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