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 <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
44using namespace android;
45
46static 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
58static 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
72static 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
92static 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
107static 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 */
126static 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    SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
159
160    SkColorType decodeColorType = brd->computeOutputColorType(colorType);
161    sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace(
162            decodeColorType, colorSpace);
163
164    // Recycle a bitmap if possible.
165    android::Bitmap* recycledBitmap = nullptr;
166    size_t recycledBytes = 0;
167    if (javaBitmap) {
168        recycledBitmap = &bitmap::toBitmap(env, javaBitmap);
169        if (recycledBitmap->isImmutable()) {
170            ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
171        }
172        recycledBytes = bitmap::getBitmapAllocationByteCount(env, javaBitmap);
173    }
174
175    // Set up the pixel allocator
176    SkBRDAllocator* allocator = nullptr;
177    RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
178    HeapAllocator heapAlloc;
179    if (javaBitmap) {
180        allocator = &recycleAlloc;
181        // We are required to match the color type of the recycled bitmap.
182        decodeColorType = recycledBitmap->info().colorType();
183    } else {
184        allocator = &heapAlloc;
185    }
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
236static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
237    SkBitmapRegionDecoder* brd =
238            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
239    return static_cast<jint>(brd->height());
240}
241
242static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
243    SkBitmapRegionDecoder* brd =
244            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
245    return static_cast<jint>(brd->width());
246}
247
248static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
249    SkBitmapRegionDecoder* brd =
250            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
251    delete brd;
252}
253
254///////////////////////////////////////////////////////////////////////////////
255
256static 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
288int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
289{
290    return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
291            gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
292}
293