• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
141     if (isHardware) {
142         if (decodeColorType == kRGBA_F16_SkColorType &&
143             !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
144             decodeColorType = kN32_SkColorType;
145         }
146         if (decodeColorType == kRGBA_1010102_SkColorType &&
147             !uirenderer::HardwareBitmapUploader::has1010102Support()) {
148             decodeColorType = kN32_SkColorType;
149         }
150     }
151 
152     // Set up the pixel allocator
153     skia::BRDAllocator* allocator = nullptr;
154     RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
155     HeapAllocator heapAlloc;
156     if (javaBitmap) {
157         allocator = &recycleAlloc;
158         // We are required to match the color type of the recycled bitmap.
159         decodeColorType = recycledBitmap->info().colorType();
160     } else {
161         allocator = &heapAlloc;
162     }
163 
164     sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace(
165             decodeColorType, colorSpace);
166 
167     // Decode the region.
168     SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
169     SkBitmap bitmap;
170     if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize,
171             decodeColorType, requireUnpremul, decodeColorSpace)) {
172         return nullObjectReturn("Failed to decode region.");
173     }
174 
175     // If the client provided options, indicate that the decode was successful.
176     if (NULL != options) {
177         env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
178         env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
179 
180         env->SetObjectField(options, gOptions_mimeFieldID,
181                 getMimeTypeAsJavaString(env, brd->getEncodedFormat()));
182         if (env->ExceptionCheck()) {
183             return nullObjectReturn("OOM in encodedFormatToString()");
184         }
185 
186         jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
187         if (isHardware) {
188             configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
189         }
190         jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
191                 gBitmapConfig_nativeToConfigMethodID, configID);
192         env->SetObjectField(options, gOptions_outConfigFieldID, config);
193 
194         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
195                 GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
196     }
197 
198     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
199     if (javaBitmap) {
200         recycleAlloc.copyIfNecessary();
201         bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
202         return javaBitmap;
203     }
204 
205     int bitmapCreateFlags = 0;
206     if (!requireUnpremul) {
207         bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
208     }
209     if (isHardware) {
210         sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap);
211         return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
212     }
213     return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
214 }
215 
nativeGetHeight(JNIEnv * env,jobject,jlong brdHandle)216 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
217     auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
218     return static_cast<jint>(brd->height());
219 }
220 
nativeGetWidth(JNIEnv * env,jobject,jlong brdHandle)221 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
222     auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
223     return static_cast<jint>(brd->width());
224 }
225 
nativeClean(JNIEnv * env,jobject,jlong brdHandle)226 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
227     auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
228     delete brd;
229 }
230 
231 ///////////////////////////////////////////////////////////////////////////////
232 
233 static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
234     {   "nativeDecodeRegion",
235         "(JIIIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
236         (void*)nativeDecodeRegion},
237 
238     {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
239 
240     {   "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
241 
242     {   "nativeClean", "(J)V", (void*)nativeClean},
243 
244     {   "nativeNewInstance",
245         "([BII)Landroid/graphics/BitmapRegionDecoder;",
246         (void*)nativeNewInstanceFromByteArray
247     },
248 
249     {   "nativeNewInstance",
250         "(Ljava/io/InputStream;[B)Landroid/graphics/BitmapRegionDecoder;",
251         (void*)nativeNewInstanceFromStream
252     },
253 
254     {   "nativeNewInstance",
255         "(Ljava/io/FileDescriptor;)Landroid/graphics/BitmapRegionDecoder;",
256         (void*)nativeNewInstanceFromFileDescriptor
257     },
258 
259     {   "nativeNewInstance",
260         "(J)Landroid/graphics/BitmapRegionDecoder;",
261         (void*)nativeNewInstanceFromAsset
262     },
263 };
264 
register_android_graphics_BitmapRegionDecoder(JNIEnv * env)265 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
266 {
267     return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
268             gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
269 }
270