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