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
42using namespace android;
43
44// Takes ownership of the SkStreamRewindable. For consistency, deletes stream even
45// when returning null.
46static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) {
47    SkAutoTDelete<SkBitmapRegionDecoder> brd(
48            SkBitmapRegionDecoder::Create(stream, SkBitmapRegionDecoder::kAndroidCodec_Strategy));
49    if (NULL == brd) {
50        doThrowIOE(env, "Image format not supported");
51        return nullObjectReturn("CreateBitmapRegionDecoder returned null");
52    }
53
54    return GraphicsJNI::createBitmapRegionDecoder(env, brd.detach());
55}
56
57static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
58                                     jint offset, jint length, jboolean isShareable) {
59    /*  If isShareable we could decide to just wrap the java array and
60        share it, but that means adding a globalref to the java array object
61        For now we just always copy the array's data if isShareable.
62     */
63    AutoJavaByteArray ar(env, byteArray);
64    SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
65
66    // the decoder owns the stream.
67    jobject brd = createBitmapRegionDecoder(env, stream);
68    return brd;
69}
70
71static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
72                                          jobject fileDescriptor, jboolean isShareable) {
73    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
74
75    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
76
77    struct stat fdStat;
78    if (fstat(descriptor, &fdStat) == -1) {
79        doThrowIOE(env, "broken file descriptor");
80        return nullObjectReturn("fstat return -1");
81    }
82
83    SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
84    SkMemoryStream* stream = new SkMemoryStream(data);
85
86    // the decoder owns the stream.
87    jobject brd = createBitmapRegionDecoder(env, stream);
88    return brd;
89}
90
91static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
92                                  jobject is,       // InputStream
93                                  jbyteArray storage, // byte[]
94                                  jboolean isShareable) {
95    jobject brd = NULL;
96    // for now we don't allow shareable with java inputstreams
97    SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
98
99    if (stream) {
100        // the decoder owns the stream.
101        brd = createBitmapRegionDecoder(env, stream);
102    }
103    return brd;
104}
105
106static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
107                                 jlong native_asset, // Asset
108                                 jboolean isShareable) {
109    Asset* asset = reinterpret_cast<Asset*>(native_asset);
110    SkMemoryStream* stream = CopyAssetToStream(asset);
111    if (NULL == stream) {
112        return NULL;
113    }
114
115    // the decoder owns the stream.
116    jobject brd = createBitmapRegionDecoder(env, stream);
117    return brd;
118}
119
120/*
121 * nine patch not supported
122 * purgeable not supported
123 * reportSizeToVM not supported
124 */
125static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
126        jint inputY, jint inputWidth, jint inputHeight, jobject options) {
127
128    // Set default options.
129    int sampleSize = 1;
130    SkColorType colorType = kN32_SkColorType;
131    bool requireUnpremul = false;
132    jobject javaBitmap = NULL;
133
134    // Update the default options with any options supplied by the client.
135    if (NULL != options) {
136        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
137        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
138        colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
139        requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
140        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
141        // The Java options of ditherMode and preferQualityOverSpeed are deprecated.  We will
142        // ignore the values of these fields.
143
144        // Initialize these fields to indicate a failure.  If the decode succeeds, we
145        // will update them later on.
146        env->SetIntField(options, gOptions_widthFieldID, -1);
147        env->SetIntField(options, gOptions_heightFieldID, -1);
148        env->SetObjectField(options, gOptions_mimeFieldID, 0);
149    }
150
151    // Recycle a bitmap if possible.
152    android::Bitmap* recycledBitmap = nullptr;
153    size_t recycledBytes = 0;
154    if (javaBitmap) {
155        recycledBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
156        if (recycledBitmap->peekAtPixelRef()->isImmutable()) {
157            ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
158        }
159        recycledBytes = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
160    }
161
162    // Set up the pixel allocator
163    SkBRDAllocator* allocator = nullptr;
164    RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
165    JavaPixelAllocator javaAlloc(env);
166    if (javaBitmap) {
167        allocator = &recycleAlloc;
168        // We are required to match the color type of the recycled bitmap.
169        colorType = recycledBitmap->info().colorType();
170    } else {
171        allocator = &javaAlloc;
172    }
173
174    // Decode the region.
175    SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
176    SkBitmapRegionDecoder* brd =
177            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
178    SkBitmap bitmap;
179    if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, colorType, requireUnpremul)) {
180        return nullObjectReturn("Failed to decode region.");
181    }
182
183    // If the client provided options, indicate that the decode was successful.
184    if (NULL != options) {
185        env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
186        env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
187        env->SetObjectField(options, gOptions_mimeFieldID,
188                encodedFormatToString(env, brd->getEncodedFormat()));
189        if (env->ExceptionCheck()) {
190            return nullObjectReturn("OOM in encodedFormatToString()");
191        }
192    }
193
194    // If we may have reused a bitmap, we need to indicate that the pixels have changed.
195    if (javaBitmap) {
196        recycleAlloc.copyIfNecessary();
197        return javaBitmap;
198    }
199
200    int bitmapCreateFlags = 0;
201    if (!requireUnpremul) {
202        bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
203    }
204    return GraphicsJNI::createBitmap(env, javaAlloc.getStorageObjAndReset(), bitmapCreateFlags);
205}
206
207static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
208    SkBitmapRegionDecoder* brd =
209            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
210    return static_cast<jint>(brd->height());
211}
212
213static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
214    SkBitmapRegionDecoder* brd =
215            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
216    return static_cast<jint>(brd->width());
217}
218
219static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
220    SkBitmapRegionDecoder* brd =
221            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
222    delete brd;
223}
224
225///////////////////////////////////////////////////////////////////////////////
226
227static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
228    {   "nativeDecodeRegion",
229        "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
230        (void*)nativeDecodeRegion},
231
232    {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
233
234    {   "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
235
236    {   "nativeClean", "(J)V", (void*)nativeClean},
237
238    {   "nativeNewInstance",
239        "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
240        (void*)nativeNewInstanceFromByteArray
241    },
242
243    {   "nativeNewInstance",
244        "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
245        (void*)nativeNewInstanceFromStream
246    },
247
248    {   "nativeNewInstance",
249        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
250        (void*)nativeNewInstanceFromFileDescriptor
251    },
252
253    {   "nativeNewInstance",
254        "(JZ)Landroid/graphics/BitmapRegionDecoder;",
255        (void*)nativeNewInstanceFromAsset
256    },
257};
258
259int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
260{
261    return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
262            gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
263}
264