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