blob: 258ffa5ae6e11684f02f240951a86e4a193390b7 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001#define LOG_TAG "BitmapFactory"
2
Joseph Wenf1f48bc2010-07-19 16:59:51 +08003#include "BitmapFactory.h"
Leon Scrogginsa06d86a2011-03-02 16:56:54 -05004#include "NinePatchPeeker.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005#include "SkImageDecoder.h"
Mike Reedc70e06b2009-04-24 11:09:12 -04006#include "SkImageRef_ashmem.h"
7#include "SkImageRef_GlobalPool.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008#include "SkPixelRef.h"
9#include "SkStream.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080010#include "SkTemplates.h"
11#include "SkUtils.h"
12#include "CreateJavaOutputStreamAdaptor.h"
Joseph Wenf1f48bc2010-07-19 16:59:51 +080013#include "AutoDecodeCancel.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080014#include "Utils.h"
Elliott Hughesa3804cf2011-04-11 16:50:19 -070015#include "JNIHelp.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080016
17#include <android_runtime/AndroidRuntime.h>
18#include <utils/Asset.h>
19#include <utils/ResourceTypes.h>
20#include <netinet/in.h>
21#include <sys/mman.h>
Joseph Wen2dcfbef2010-09-10 10:15:09 +080022#include <sys/stat.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
Joseph Wenf1f48bc2010-07-19 16:59:51 +080024jfieldID gOptions_justBoundsFieldID;
25jfieldID gOptions_sampleSizeFieldID;
26jfieldID gOptions_configFieldID;
Romain Guy23610982011-01-17 12:51:55 -080027jfieldID gOptions_mutableFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080028jfieldID gOptions_ditherFieldID;
29jfieldID gOptions_purgeableFieldID;
30jfieldID gOptions_shareableFieldID;
Wei-Ta Chen953f9092010-12-03 14:06:18 -080031jfieldID gOptions_preferQualityOverSpeedFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080032jfieldID gOptions_widthFieldID;
33jfieldID gOptions_heightFieldID;
34jfieldID gOptions_mimeFieldID;
35jfieldID gOptions_mCancelID;
Chet Haase37f74ca2010-12-08 17:56:36 -080036jfieldID gOptions_bitmapFieldID;
Chet Haase37f74ca2010-12-08 17:56:36 -080037jfieldID gBitmap_nativeBitmapFieldID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039#if 0
40 #define TRACE_BITMAP(code) code
41#else
42 #define TRACE_BITMAP(code)
43#endif
44
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045using namespace android;
46
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047static inline int32_t validOrNeg1(bool isValid, int32_t value) {
48// return isValid ? value : -1;
49 SkASSERT((int)isValid == 0 || (int)isValid == 1);
50 return ((int32_t)isValid - 1) | value;
51}
52
Joseph Wenf1f48bc2010-07-19 16:59:51 +080053jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 static const struct {
55 SkImageDecoder::Format fFormat;
56 const char* fMimeType;
57 } gMimeTypes[] = {
58 { SkImageDecoder::kBMP_Format, "image/bmp" },
59 { SkImageDecoder::kGIF_Format, "image/gif" },
60 { SkImageDecoder::kICO_Format, "image/x-ico" },
61 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
62 { SkImageDecoder::kPNG_Format, "image/png" },
63 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
64 };
Elliott Hughesa3804cf2011-04-11 16:50:19 -070065
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 const char* cstr = NULL;
67 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
68 if (gMimeTypes[i].fFormat == format) {
69 cstr = gMimeTypes[i].fMimeType;
70 break;
71 }
72 }
73
74 jstring jstr = 0;
75 if (NULL != cstr) {
76 jstr = env->NewStringUTF(cstr);
77 }
78 return jstr;
79}
80
Mike Reedc70e06b2009-04-24 11:09:12 -040081static bool optionsPurgeable(JNIEnv* env, jobject options) {
82 return options != NULL &&
83 env->GetBooleanField(options, gOptions_purgeableFieldID);
84}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085
Mike Reedc70e06b2009-04-24 11:09:12 -040086static bool optionsShareable(JNIEnv* env, jobject options) {
87 return options != NULL &&
88 env->GetBooleanField(options, gOptions_shareableFieldID);
89}
90
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +000091static bool optionsJustBounds(JNIEnv* env, jobject options) {
92 return options != NULL &&
93 env->GetBooleanField(options, gOptions_justBoundsFieldID);
94}
95
Mike Reedc70e06b2009-04-24 11:09:12 -040096static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
Mike Reed17154412009-09-24 12:35:27 -040097 int sampleSize, bool ditherImage) {
98 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -040099 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700100 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400101 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
102 } else {
103 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
104 }
Mike Reed17154412009-09-24 12:35:27 -0400105 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400106 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700107 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400108 return pr;
109}
110
111// since we "may" create a purgeable imageref, we require the stream be ref'able
112// i.e. dynamically allocated, since its lifetime may exceed the current stack
113// frame.
114static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
Mike Reed36ad54a2010-03-10 10:28:10 -0500115 jobject options, bool allowPurgeable,
116 bool forcePurgeable = false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 int sampleSize = 1;
118 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
119 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
120 bool doDither = true;
Romain Guy23610982011-01-17 12:51:55 -0800121 bool isMutable = false;
Mike Reed36ad54a2010-03-10 10:28:10 -0500122 bool isPurgeable = forcePurgeable ||
123 (allowPurgeable && optionsPurgeable(env, options));
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800124 bool preferQualityOverSpeed = false;
Chet Haase37f74ca2010-12-08 17:56:36 -0800125 jobject javaBitmap = NULL;
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 if (NULL != options) {
128 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000129 if (optionsJustBounds(env, options)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 mode = SkImageDecoder::kDecodeBounds_Mode;
131 }
132 // initialize these, in case we fail later on
133 env->SetIntField(options, gOptions_widthFieldID, -1);
134 env->SetIntField(options, gOptions_heightFieldID, -1);
135 env->SetObjectField(options, gOptions_mimeFieldID, 0);
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
138 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
Romain Guy23610982011-01-17 12:51:55 -0800139 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800141 preferQualityOverSpeed = env->GetBooleanField(options,
142 gOptions_preferQualityOverSpeedFieldID);
Chet Haase37f74ca2010-12-08 17:56:36 -0800143 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 }
145
146 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
147 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400148 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 decoder->setSampleSize(sampleSize);
152 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800153 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154
Mike Reed39f10ec2010-03-24 10:08:50 -0400155 NinePatchPeeker peeker(decoder);
Carl Shapiro2118b252010-12-17 18:03:38 -0800156 JavaPixelAllocator javaAllocator(env);
Chet Haase37f74ca2010-12-08 17:56:36 -0800157 SkBitmap* bitmap;
158 if (javaBitmap == NULL) {
159 bitmap = new SkBitmap;
160 } else {
Chet Haasedecc8cd2010-12-10 16:44:41 -0800161 if (sampleSize != 1) {
162 return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1");
163 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800164 bitmap = (SkBitmap *) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
165 // config of supplied bitmap overrules config set in options
166 prefConfig = bitmap->getConfig();
167 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 Res_png_9patch dummy9Patch;
169
170 SkAutoTDelete<SkImageDecoder> add(decoder);
Chet Haasedecc8cd2010-12-10 16:44:41 -0800171 SkAutoTDelete<SkBitmap> adb(bitmap, (javaBitmap == NULL));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172
173 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400174 if (!isPurgeable) {
175 decoder->setAllocator(&javaAllocator);
176 }
177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 AutoDecoderCancel adc(options, decoder);
179
Ray Chen0a6a0e92009-04-02 01:31:49 -0700180 // To fix the race condition in case "requestCancelDecode"
181 // happens earlier than AutoDecoderCancel object is added
182 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400183 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
Chet Haase37f74ca2010-12-08 17:56:36 -0800184 return nullObjectReturn("gOptions_mCancelID");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700185 }
186
Mike Reedc70e06b2009-04-24 11:09:12 -0400187 SkImageDecoder::Mode decodeMode = mode;
188 if (isPurgeable) {
189 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800191 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode, javaBitmap != NULL)) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400192 return nullObjectReturn("decoder->decode returned false");
193 }
194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 // update options (if any)
196 if (NULL != options) {
197 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
198 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
199 // TODO: set the mimeType field with the data from the codec.
200 // but how to reuse a set of strings, rather than allocating new one
201 // each time?
202 env->SetObjectField(options, gOptions_mimeFieldID,
203 getMimeTypeString(env, decoder->getFormat()));
204 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 // if we're in justBounds mode, return now (skip the java bitmap)
207 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
208 return NULL;
209 }
210
211 jbyteArray ninePatchChunk = NULL;
212 if (peeker.fPatchIsValid) {
213 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
214 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
215 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400216 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 }
218 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
219 NULL);
220 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400221 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
223 peeker.fPatch->serialize(array);
224 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
225 }
226
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800227 // detach bitmap from its autodeleter, since we want to own it now
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 adb.detach();
229
230 if (padding) {
231 if (peeker.fPatchIsValid) {
232 GraphicsJNI::set_jrect(env, padding,
233 peeker.fPatch->paddingLeft,
234 peeker.fPatch->paddingTop,
235 peeker.fPatch->paddingRight,
236 peeker.fPatch->paddingBottom);
237 } else {
238 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
239 }
240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241
Mike Reedc70e06b2009-04-24 11:09:12 -0400242 SkPixelRef* pr;
243 if (isPurgeable) {
Mike Reed17154412009-09-24 12:35:27 -0400244 pr = installPixelRef(bitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400245 } else {
246 // if we get here, we're in kDecodePixels_Mode and will therefore
247 // already have a pixelref installed.
248 pr = bitmap->pixelRef();
249 }
Romain Guy23610982011-01-17 12:51:55 -0800250
251 if (!isMutable) {
252 // promise we will never change our pixels (great for sharing and pictures)
253 pr->setImmutable();
254 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800255
256 if (javaBitmap != NULL) {
257 // If a java bitmap was passed in for reuse, pass it back
258 return javaBitmap;
259 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400260 // now create the java bitmap
Romain Guy23610982011-01-17 12:51:55 -0800261 return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
262 isMutable, ninePatchChunk);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263}
264
265static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
266 jobject is, // InputStream
267 jbyteArray storage, // byte[]
268 jobject padding,
269 jobject options) { // BitmapFactory$Options
270 jobject bitmap = NULL;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800271 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272
273 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400274 // for now we don't allow purgeable with java inputstreams
275 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 stream->unref();
277 }
278 return bitmap;
279}
280
281static ssize_t getFDSize(int fd) {
Kenny Rootddb76c42010-11-24 12:56:06 -0800282 off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 if (curr < 0) {
284 return 0;
285 }
286 size_t size = ::lseek(fd, 0, SEEK_END);
Kenny Rootddb76c42010-11-24 12:56:06 -0800287 ::lseek64(fd, curr, SEEK_SET);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 return size;
289}
290
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
292 jobject fileDescriptor,
293 jobject padding,
294 jobject bitmapFactoryOptions) {
295 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
296
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700297 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400298
299 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
300 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
301 bool weOwnTheFD = false;
302 if (isPurgeable && isShareable) {
303 int newFD = ::dup(descriptor);
304 if (-1 != newFD) {
305 weOwnTheFD = true;
306 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 }
308 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309
Mike Reedc70e06b2009-04-24 11:09:12 -0400310 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
311 SkAutoUnref aur(stream);
312 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 return NULL;
314 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400315
316 /* Restore our offset when we leave, so we can be called more than once
317 with the same descriptor. This is only required if we didn't dup the
318 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 */
320 AutoFDSeek as(descriptor);
321
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700322 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
323 shareable case.
324 */
325 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400326}
327
328/* make a deep copy of the asset, and return it as a stream, or NULL if there
329 was an error.
330 */
331static SkStream* copyAssetToStream(Asset* asset) {
332 // if we could "ref/reopen" the asset, we may not need to copy it here
Kenny Rootddb76c42010-11-24 12:56:06 -0800333 off64_t size = asset->seek(0, SEEK_SET);
334 if ((off64_t)-1 == size) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400335 SkDebugf("---- copyAsset: asset rewind failed\n");
336 return NULL;
337 }
338
339 size = asset->getLength();
340 if (size <= 0) {
341 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
342 return NULL;
343 }
344
345 SkStream* stream = new SkMemoryStream(size);
346 void* data = const_cast<void*>(stream->getMemoryBase());
Kenny Rootddb76c42010-11-24 12:56:06 -0800347 off64_t len = asset->read(data, size);
Mike Reedc70e06b2009-04-24 11:09:12 -0400348 if (len != size) {
349 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
350 delete stream;
351 stream = NULL;
352 }
353 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354}
355
356static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
357 jint native_asset, // Asset
358 jobject padding, // Rect
359 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400360 SkStream* stream;
361 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Romain Guy18ef37252010-08-19 15:10:54 -0700362 bool forcePurgeable = optionsPurgeable(env, options);
363 if (forcePurgeable) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400364 // if we could "ref/reopen" the asset, we may not need to copy it here
365 // and we could assume optionsShareable, since assets are always RO
366 stream = copyAssetToStream(asset);
367 if (NULL == stream) {
368 return NULL;
369 }
370 } else {
371 // since we know we'll be done with the asset when we return, we can
372 // just use a simple wrapper
373 stream = new AssetStreamAdaptor(asset);
374 }
375 SkAutoUnref aur(stream);
Mike Reed36ad54a2010-03-10 10:28:10 -0500376 return doDecode(env, stream, padding, options, true, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377}
378
379static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
380 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400381 /* If optionsShareable() we could decide to just wrap the java array and
382 share it, but that means adding a globalref to the java array object
383 and managing its lifetime. For now we just always copy the array's data
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000384 if optionsPurgeable(), unless we're just decoding bounds.
Mike Reedc70e06b2009-04-24 11:09:12 -0400385 */
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000386 bool purgeable = optionsPurgeable(env, options)
387 && !optionsJustBounds(env, options);
Mike Reedc70e06b2009-04-24 11:09:12 -0400388 AutoJavaByteArray ar(env, byteArray);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000389 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
Mike Reedc70e06b2009-04-24 11:09:12 -0400390 SkAutoUnref aur(stream);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000391 return doDecode(env, stream, NULL, options, purgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392}
393
394static void nativeRequestCancel(JNIEnv*, jobject joptions) {
395 (void)AutoDecoderCancel::RequestCancel(joptions);
396}
397
398static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
399 jobject padding) {
400
401 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
402 if (array != NULL) {
403 size_t chunkSize = env->GetArrayLength(chunkObject);
404 void* storage = alloca(chunkSize);
405 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
406 memcpy(chunk, array, chunkSize);
407 android::Res_png_9patch::deserialize(chunk);
408
409 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
410 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
411 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
412 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
413
414 for (int i = 0; i < chunk->numXDivs; i++) {
415 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
416 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
417 chunk->xDivs[i]++;
418 }
419 }
420
421 for (int i = 0; i < chunk->numYDivs; i++) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700422 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
424 chunk->yDivs[i]++;
425 }
426 }
427
428 memcpy(array, chunk, chunkSize);
429
430 if (padding) {
431 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
432 chunk->paddingRight, chunk->paddingBottom);
433 }
434
435 env->ReleaseByteArrayElements(chunkObject, array, 0);
436 }
437 return chunkObject;
438}
439
Mike Reedab4a0c12010-01-26 10:13:53 -0500440static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
441 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
442
443 // these are the only default configs that make sense for codecs right now
444 static const SkBitmap::Config gValidDefConfig[] = {
445 SkBitmap::kRGB_565_Config,
446 SkBitmap::kARGB_8888_Config,
447 };
448
449 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
450 if (config == gValidDefConfig[i]) {
451 SkImageDecoder::SetDeviceConfig(config);
452 break;
453 }
454 }
455}
456
Owen Lina9d0d472011-01-18 17:39:15 +0800457static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700458 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Owen Lina9d0d472011-01-18 17:39:15 +0800459 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
460}
461
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462///////////////////////////////////////////////////////////////////////////////
463
464static JNINativeMethod gMethods[] = {
465 { "nativeDecodeStream",
466 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
467 (void*)nativeDecodeStream
468 },
469
470 { "nativeDecodeFileDescriptor",
471 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
472 (void*)nativeDecodeFileDescriptor
473 },
474
475 { "nativeDecodeAsset",
476 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
477 (void*)nativeDecodeAsset
478 },
479
480 { "nativeDecodeByteArray",
481 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
482 (void*)nativeDecodeByteArray
483 },
484
485 { "nativeScaleNinePatch",
486 "([BFLandroid/graphics/Rect;)[B",
487 (void*)nativeScaleNinePatch
Mike Reedab4a0c12010-01-26 10:13:53 -0500488 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489
Mike Reedab4a0c12010-01-26 10:13:53 -0500490 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
Owen Lina9d0d472011-01-18 17:39:15 +0800491
492 { "nativeIsSeekable",
493 "(Ljava/io/FileDescriptor;)Z",
494 (void*)nativeIsSeekable
495 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496};
497
498static JNINativeMethod gOptionsMethods[] = {
499 { "requestCancel", "()V", (void*)nativeRequestCancel }
500};
501
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
503 const char fieldname[], const char type[]) {
504 jfieldID id = env->GetFieldID(clazz, fieldname, type);
505 SkASSERT(id);
506 return id;
507}
508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509int register_android_graphics_BitmapFactory(JNIEnv* env) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700510 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
511 SkASSERT(options_class);
512 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
Chet Haase37f74ca2010-12-08 17:56:36 -0800513 "Landroid/graphics/Bitmap;");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700514 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
515 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
516 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 "Landroid/graphics/Bitmap$Config;");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700518 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
519 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
520 gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
521 gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
522 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800523 "inPreferQualityOverSpeed", "Z");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700524 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
525 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
526 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
527 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700529 jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
530 SkASSERT(bitmap_class);
531 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532
533 int ret = AndroidRuntime::registerNativeMethods(env,
534 "android/graphics/BitmapFactory$Options",
535 gOptionsMethods,
536 SK_ARRAY_COUNT(gOptionsMethods));
537 if (ret) {
538 return ret;
539 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700540 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 gMethods, SK_ARRAY_COUNT(gMethods));
542}