blob: 3525d0753cd2dc23a99d0fbd8dd596780fe4bb0e [file] [log] [blame]
Wei-Ta Chen6b849e22010-09-07 17:32:18 +08001/*
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 "SkBitmap.h"
Derek Sollenberger5827cb52013-07-26 14:58:06 -040020#include "SkData.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080021#include "SkImageEncoder.h"
22#include "GraphicsJNI.h"
23#include "SkUtils.h"
24#include "SkTemplates.h"
25#include "SkPixelRef.h"
26#include "SkStream.h"
27#include "BitmapFactory.h"
28#include "AutoDecodeCancel.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080029#include "CreateJavaOutputStreamAdaptor.h"
30#include "Utils.h"
Elliott Hughesa3804cf2011-04-11 16:50:19 -070031#include "JNIHelp.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080032
Andreas Gampeed6b9df2014-11-20 22:02:20 -080033#include "core_jni_helpers.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080034#include "android_util_Binder.h"
35#include "android_nio_utils.h"
36#include "CreateJavaOutputStreamAdaptor.h"
37
38#include <binder/Parcel.h>
39#include <jni.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080040#include <androidfw/Asset.h>
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080041#include <sys/stat.h>
42
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080043using namespace android;
44
Derek Sollenberger5827cb52013-07-26 14:58:06 -040045class SkBitmapRegionDecoder {
46public:
47 SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
48 fDecoder = decoder;
49 fWidth = width;
50 fHeight = height;
51 }
52 ~SkBitmapRegionDecoder() {
53 SkDELETE(fDecoder);
54 }
55
56 bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
Mike Reed42a1d082014-07-07 18:06:18 -040057 SkColorType pref, int sampleSize) {
Derek Sollenberger5827cb52013-07-26 14:58:06 -040058 fDecoder->setSampleSize(sampleSize);
Leon Scrogginscc11f152014-03-31 16:52:13 -040059 return fDecoder->decodeSubset(bitmap, rect, pref);
Derek Sollenberger5827cb52013-07-26 14:58:06 -040060 }
61
62 SkImageDecoder* getDecoder() const { return fDecoder; }
63 int getWidth() const { return fWidth; }
64 int getHeight() const { return fHeight; }
65
66private:
67 SkImageDecoder* fDecoder;
68 int fWidth;
69 int fHeight;
70};
71
Leon Scroggins III34497892015-01-20 15:52:43 -050072// Takes ownership of the SkStreamRewindable. For consistency, deletes stream even
73// when returning null.
Derek Sollenbergered79ff02013-11-20 14:50:51 -050074static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080075 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
76 int width, height;
77 if (NULL == decoder) {
Leon Scroggins III34497892015-01-20 15:52:43 -050078 SkDELETE(stream);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080079 doThrowIOE(env, "Image format not supported");
80 return nullObjectReturn("SkImageDecoder::Factory returned null");
81 }
82
Patrick Dubroye4ac2d62010-12-01 11:23:13 -080083 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080084 decoder->setAllocator(javaAllocator);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080085 javaAllocator->unref();
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080086
Leon Scroggins III34497892015-01-20 15:52:43 -050087 // This call passes ownership of stream to the decoder, or deletes on failure.
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080088 if (!decoder->buildTileIndex(stream, &width, &height)) {
89 char msg[100];
90 snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
91 decoder->getFormatName());
92 doThrowIOE(env, msg);
Derek Sollenberger5827cb52013-07-26 14:58:06 -040093 SkDELETE(decoder);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080094 return nullObjectReturn("decoder->buildTileIndex returned false");
95 }
96
Derek Sollenberger5827cb52013-07-26 14:58:06 -040097 SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080098 return GraphicsJNI::createBitmapRegionDecoder(env, bm);
99}
100
101static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
Ashok Bhatb091d472014-01-08 14:32:49 +0000102 jint offset, jint length, jboolean isShareable) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800103 /* If isShareable we could decide to just wrap the java array and
104 share it, but that means adding a globalref to the java array object
105 For now we just always copy the array's data if isShareable.
106 */
107 AutoJavaByteArray ar(env, byteArray);
Derek Sollenbergered79ff02013-11-20 14:50:51 -0500108 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400109
Leon Scroggins III34497892015-01-20 15:52:43 -0500110 // the decoder owns the stream.
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400111 jobject brd = createBitmapRegionDecoder(env, stream);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400112 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800113}
114
115static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
116 jobject fileDescriptor, jboolean isShareable) {
117 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
118
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700119 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400120
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800121 struct stat fdStat;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800122 if (fstat(descriptor, &fdStat) == -1) {
123 doThrowIOE(env, "broken file descriptor");
124 return nullObjectReturn("fstat return -1");
125 }
126
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400127 SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
128 SkMemoryStream* stream = new SkMemoryStream(data);
Wei-Ta Chen58c1579c2010-10-21 20:56:28 -0700129
Leon Scroggins III34497892015-01-20 15:52:43 -0500130 // the decoder owns the stream.
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400131 jobject brd = createBitmapRegionDecoder(env, stream);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400132 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800133}
134
135static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
136 jobject is, // InputStream
137 jbyteArray storage, // byte[]
138 jboolean isShareable) {
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400139 jobject brd = NULL;
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400140 // for now we don't allow shareable with java inputstreams
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400141 SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800142
143 if (stream) {
Leon Scroggins III34497892015-01-20 15:52:43 -0500144 // the decoder owns the stream.
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400145 brd = createBitmapRegionDecoder(env, stream);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800146 }
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400147 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800148}
149
150static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
Ashok Bhatb091d472014-01-08 14:32:49 +0000151 jlong native_asset, // Asset
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800152 jboolean isShareable) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800153 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Leon Scroggins III34497892015-01-20 15:52:43 -0500154 SkMemoryStream* stream = CopyAssetToStream(asset);
155 if (NULL == stream) {
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400156 return NULL;
157 }
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400158
Leon Scroggins III34497892015-01-20 15:52:43 -0500159 // the decoder owns the stream.
160 jobject brd = createBitmapRegionDecoder(env, stream);
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400161 return brd;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800162}
163
164/*
165 * nine patch not supported
166 *
167 * purgeable not supported
168 * reportSizeToVM not supported
169 */
Ashok Bhatb091d472014-01-08 14:32:49 +0000170static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle,
171 jint start_x, jint start_y, jint width, jint height, jobject options) {
172 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
Owen Linf970c2e2012-04-25 18:49:09 +0800173 jobject tileBitmap = NULL;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800174 SkImageDecoder *decoder = brd->getDecoder();
175 int sampleSize = 1;
Mike Reed42a1d082014-07-07 18:06:18 -0400176 SkColorType prefColorType = kUnknown_SkColorType;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800177 bool doDither = true;
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800178 bool preferQualityOverSpeed = false;
Chris Craik1abf5d62013-08-16 12:47:03 -0700179 bool requireUnpremultiplied = false;
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800180
181 if (NULL != options) {
182 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
183 // initialize these, in case we fail later on
184 env->SetIntField(options, gOptions_widthFieldID, -1);
185 env->SetIntField(options, gOptions_heightFieldID, -1);
186 env->SetObjectField(options, gOptions_mimeFieldID, 0);
187
188 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
Mike Reed42a1d082014-07-07 18:06:18 -0400189 prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800190 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800191 preferQualityOverSpeed = env->GetBooleanField(options,
192 gOptions_preferQualityOverSpeedFieldID);
Owen Linf970c2e2012-04-25 18:49:09 +0800193 // Get the bitmap for re-use if it exists.
194 tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
Leon Scroggins III2cc409a2013-08-26 14:27:15 -0400195 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800196 }
197
198 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800199 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
Chris Craik1abf5d62013-08-16 12:47:03 -0700200 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
201 AutoDecoderCancel adc(options, decoder);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800202
203 // To fix the race condition in case "requestCancelDecode"
204 // happens earlier than AutoDecoderCancel object is added
205 // to the gAutoDecoderCancelMutex linked list.
206 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
207 return nullObjectReturn("gOptions_mCancelID");;
208 }
209
210 SkIRect region;
211 region.fLeft = start_x;
212 region.fTop = start_y;
213 region.fRight = start_x + width;
214 region.fBottom = start_y + height;
Owen Linf970c2e2012-04-25 18:49:09 +0800215 SkBitmap* bitmap = NULL;
Mike Kleinf5b43bd2013-08-29 11:15:06 -0400216 SkAutoTDelete<SkBitmap> adb;
Owen Linf970c2e2012-04-25 18:49:09 +0800217
218 if (tileBitmap != NULL) {
219 // Re-use bitmap.
John Reckedc22fb2015-04-20 22:06:31 +0000220 bitmap = GraphicsJNI::getSkBitmap(env, tileBitmap);
Owen Linf970c2e2012-04-25 18:49:09 +0800221 }
222 if (bitmap == NULL) {
223 bitmap = new SkBitmap;
224 adb.reset(bitmap);
225 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800226
Mike Reed42a1d082014-07-07 18:06:18 -0400227 if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800228 return nullObjectReturn("decoder->decodeRegion returned false");
229 }
230
231 // update options (if any)
232 if (NULL != options) {
233 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
234 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
235 // TODO: set the mimeType field with the data from the codec.
236 // but how to reuse a set of strings, rather than allocating new one
237 // each time?
238 env->SetObjectField(options, gOptions_mimeFieldID,
239 getMimeTypeString(env, decoder->getFormat()));
240 }
241
Owen Linf970c2e2012-04-25 18:49:09 +0800242 if (tileBitmap != NULL) {
Chris Craik3b477132015-01-14 16:52:20 -0800243 bitmap->notifyPixelsChanged();
Owen Linf970c2e2012-04-25 18:49:09 +0800244 return tileBitmap;
245 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800246
Owen Linf970c2e2012-04-25 18:49:09 +0800247 // detach bitmap from its autodeleter, since we want to own it now
Mike Kleinf5b43bd2013-08-29 11:15:06 -0400248 adb.detach();
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800249
Patrick Dubroyafde46e2010-12-15 11:52:01 -0800250 JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
251 jbyteArray buff = allocator->getStorageObjAndReset();
Chris Craik1abf5d62013-08-16 12:47:03 -0700252
253 int bitmapCreateFlags = 0;
254 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
255 return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800256}
257
Ashok Bhatb091d472014-01-08 14:32:49 +0000258static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
259 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
260 return static_cast<jint>(brd->getHeight());
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800261}
262
Ashok Bhatb091d472014-01-08 14:32:49 +0000263static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
264 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
265 return static_cast<jint>(brd->getWidth());
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800266}
267
Ashok Bhatb091d472014-01-08 14:32:49 +0000268static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
269 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800270 delete brd;
271}
272
273///////////////////////////////////////////////////////////////////////////////
274
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800275static JNINativeMethod gBitmapRegionDecoderMethods[] = {
276 { "nativeDecodeRegion",
Ashok Bhatb091d472014-01-08 14:32:49 +0000277 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800278 (void*)nativeDecodeRegion},
279
Ashok Bhatb091d472014-01-08 14:32:49 +0000280 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800281
Ashok Bhatb091d472014-01-08 14:32:49 +0000282 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800283
Ashok Bhatb091d472014-01-08 14:32:49 +0000284 { "nativeClean", "(J)V", (void*)nativeClean},
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800285
286 { "nativeNewInstance",
287 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
288 (void*)nativeNewInstanceFromByteArray
289 },
290
291 { "nativeNewInstance",
292 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
293 (void*)nativeNewInstanceFromStream
294 },
295
296 { "nativeNewInstance",
297 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
298 (void*)nativeNewInstanceFromFileDescriptor
299 },
300
301 { "nativeNewInstance",
Ashok Bhatb091d472014-01-08 14:32:49 +0000302 "(JZ)Landroid/graphics/BitmapRegionDecoder;",
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800303 (void*)nativeNewInstanceFromAsset
304 },
305};
306
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800307int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
308{
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800309 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
310 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800311}