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