blob: b9c93b8fa40b298c50826a4f3932d30dcec8591f [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"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004#include "SkImageDecoder.h"
Mike Reedc70e06b2009-04-24 11:09:12 -04005#include "SkImageRef_ashmem.h"
6#include "SkImageRef_GlobalPool.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007#include "SkPixelRef.h"
8#include "SkStream.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009#include "SkTemplates.h"
10#include "SkUtils.h"
11#include "CreateJavaOutputStreamAdaptor.h"
Joseph Wenf1f48bc2010-07-19 16:59:51 +080012#include "AutoDecodeCancel.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080013#include "Utils.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080014
15#include <android_runtime/AndroidRuntime.h>
16#include <utils/Asset.h>
17#include <utils/ResourceTypes.h>
18#include <netinet/in.h>
19#include <sys/mman.h>
Joseph Wen2dcfbef2010-09-10 10:15:09 +080020#include <sys/stat.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021
Joseph Wenf1f48bc2010-07-19 16:59:51 +080022jclass gOptions_class;
23jfieldID gOptions_justBoundsFieldID;
24jfieldID gOptions_sampleSizeFieldID;
25jfieldID gOptions_configFieldID;
26jfieldID gOptions_ditherFieldID;
27jfieldID gOptions_purgeableFieldID;
28jfieldID gOptions_shareableFieldID;
29jfieldID gOptions_nativeAllocFieldID;
Wei-Ta Chen953f9092010-12-03 14:06:18 -080030jfieldID gOptions_preferQualityOverSpeedFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080031jfieldID gOptions_widthFieldID;
32jfieldID gOptions_heightFieldID;
33jfieldID gOptions_mimeFieldID;
34jfieldID gOptions_mCancelID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36static jclass gFileDescriptor_class;
37static jfieldID gFileDescriptor_descriptor;
38
39#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
47class NinePatchPeeker : public SkImageDecoder::Peeker {
Mike Reed39f10ec2010-03-24 10:08:50 -040048 SkImageDecoder* fHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049public:
Mike Reed39f10ec2010-03-24 10:08:50 -040050 NinePatchPeeker(SkImageDecoder* host) {
51 // the host lives longer than we do, so a raw ptr is safe
52 fHost = host;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 fPatchIsValid = false;
54 }
55
56 ~NinePatchPeeker() {
57 if (fPatchIsValid) {
58 free(fPatch);
59 }
60 }
61
62 bool fPatchIsValid;
63 Res_png_9patch* fPatch;
64
65 virtual bool peek(const char tag[], const void* data, size_t length) {
66 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
67 Res_png_9patch* patch = (Res_png_9patch*) data;
68 size_t patchSize = patch->serializedSize();
69 assert(length == patchSize);
70 // You have to copy the data because it is owned by the png reader
71 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
72 memcpy(patchNew, patch, patchSize);
73 // this relies on deserialization being done in place
74 Res_png_9patch::deserialize(patchNew);
75 patchNew->fileToDevice();
76 if (fPatchIsValid) {
77 free(fPatch);
78 }
79 fPatch = patchNew;
80 //printf("9patch: (%d,%d)-(%d,%d)\n",
81 // fPatch.sizeLeft, fPatch.sizeTop,
82 // fPatch.sizeRight, fPatch.sizeBottom);
83 fPatchIsValid = true;
Mike Reed39f10ec2010-03-24 10:08:50 -040084
85 // now update our host to force index or 32bit config
86 // 'cause we don't want 565 predithered, since as a 9patch, we know
87 // we will be stretched, and therefore we want to dither afterwards.
88 static const SkBitmap::Config gNo565Pref[] = {
89 SkBitmap::kIndex8_Config,
90 SkBitmap::kIndex8_Config,
91 SkBitmap::kARGB_8888_Config,
92 SkBitmap::kARGB_8888_Config,
93 SkBitmap::kARGB_8888_Config,
94 SkBitmap::kARGB_8888_Config,
95 };
96 fHost->setPrefConfigTable(gNo565Pref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 } else {
98 fPatch = NULL;
99 }
100 return true; // keep on decoding
101 }
102};
103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104///////////////////////////////////////////////////////////////////////////////
105
106static inline int32_t validOrNeg1(bool isValid, int32_t value) {
107// return isValid ? value : -1;
108 SkASSERT((int)isValid == 0 || (int)isValid == 1);
109 return ((int32_t)isValid - 1) | value;
110}
111
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800112jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 static const struct {
114 SkImageDecoder::Format fFormat;
115 const char* fMimeType;
116 } gMimeTypes[] = {
117 { SkImageDecoder::kBMP_Format, "image/bmp" },
118 { SkImageDecoder::kGIF_Format, "image/gif" },
119 { SkImageDecoder::kICO_Format, "image/x-ico" },
120 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
121 { SkImageDecoder::kPNG_Format, "image/png" },
122 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
123 };
124
125 const char* cstr = NULL;
126 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
127 if (gMimeTypes[i].fFormat == format) {
128 cstr = gMimeTypes[i].fMimeType;
129 break;
130 }
131 }
132
133 jstring jstr = 0;
134 if (NULL != cstr) {
135 jstr = env->NewStringUTF(cstr);
136 }
137 return jstr;
138}
139
Mike Reedc70e06b2009-04-24 11:09:12 -0400140static bool optionsPurgeable(JNIEnv* env, jobject options) {
141 return options != NULL &&
142 env->GetBooleanField(options, gOptions_purgeableFieldID);
143}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144
Mike Reedc70e06b2009-04-24 11:09:12 -0400145static bool optionsShareable(JNIEnv* env, jobject options) {
146 return options != NULL &&
147 env->GetBooleanField(options, gOptions_shareableFieldID);
148}
149
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000150static bool optionsJustBounds(JNIEnv* env, jobject options) {
151 return options != NULL &&
152 env->GetBooleanField(options, gOptions_justBoundsFieldID);
153}
154
Mike Reed1b22b972009-07-17 11:21:47 -0400155static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
156 return NULL == options ||
157 !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
158}
159
Mike Reedc70e06b2009-04-24 11:09:12 -0400160
161static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
Mike Reed17154412009-09-24 12:35:27 -0400162 int sampleSize, bool ditherImage) {
163 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400164 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700165 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400166 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
167 } else {
168 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
169 }
Mike Reed17154412009-09-24 12:35:27 -0400170 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400171 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700172 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400173 return pr;
174}
175
176// since we "may" create a purgeable imageref, we require the stream be ref'able
177// i.e. dynamically allocated, since its lifetime may exceed the current stack
178// frame.
179static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
Mike Reed36ad54a2010-03-10 10:28:10 -0500180 jobject options, bool allowPurgeable,
181 bool forcePurgeable = false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 int sampleSize = 1;
183 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
184 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
185 bool doDither = true;
Mike Reed36ad54a2010-03-10 10:28:10 -0500186 bool isPurgeable = forcePurgeable ||
187 (allowPurgeable && optionsPurgeable(env, options));
Mike Reed1b22b972009-07-17 11:21:47 -0400188 bool reportSizeToVM = optionsReportSizeToVM(env, options);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800189 bool preferQualityOverSpeed = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190
191 if (NULL != options) {
192 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000193 if (optionsJustBounds(env, options)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 mode = SkImageDecoder::kDecodeBounds_Mode;
195 }
196 // initialize these, in case we fail later on
197 env->SetIntField(options, gOptions_widthFieldID, -1);
198 env->SetIntField(options, gOptions_heightFieldID, -1);
199 env->SetObjectField(options, gOptions_mimeFieldID, 0);
200
201 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
202 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
203 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800204 preferQualityOverSpeed = env->GetBooleanField(options,
205 gOptions_preferQualityOverSpeedFieldID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207
208 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
209 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400210 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 }
212
213 decoder->setSampleSize(sampleSize);
214 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800215 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216
Mike Reed39f10ec2010-03-24 10:08:50 -0400217 NinePatchPeeker peeker(decoder);
Mike Reed1b22b972009-07-17 11:21:47 -0400218 JavaPixelAllocator javaAllocator(env, reportSizeToVM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 SkBitmap* bitmap = new SkBitmap;
220 Res_png_9patch dummy9Patch;
221
222 SkAutoTDelete<SkImageDecoder> add(decoder);
223 SkAutoTDelete<SkBitmap> adb(bitmap);
224
225 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400226 if (!isPurgeable) {
227 decoder->setAllocator(&javaAllocator);
228 }
229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 AutoDecoderCancel adc(options, decoder);
231
Ray Chen0a6a0e92009-04-02 01:31:49 -0700232 // To fix the race condition in case "requestCancelDecode"
233 // happens earlier than AutoDecoderCancel object is added
234 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400235 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
236 return nullObjectReturn("gOptions_mCancelID");;
Ray Chen0a6a0e92009-04-02 01:31:49 -0700237 }
238
Mike Reedc70e06b2009-04-24 11:09:12 -0400239 SkImageDecoder::Mode decodeMode = mode;
240 if (isPurgeable) {
241 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400243 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
244 return nullObjectReturn("decoder->decode returned false");
245 }
246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 // update options (if any)
248 if (NULL != options) {
249 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
250 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
251 // TODO: set the mimeType field with the data from the codec.
252 // but how to reuse a set of strings, rather than allocating new one
253 // each time?
254 env->SetObjectField(options, gOptions_mimeFieldID,
255 getMimeTypeString(env, decoder->getFormat()));
256 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400257
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 // if we're in justBounds mode, return now (skip the java bitmap)
259 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
260 return NULL;
261 }
262
263 jbyteArray ninePatchChunk = NULL;
264 if (peeker.fPatchIsValid) {
265 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
266 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
267 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400268 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 }
270 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
271 NULL);
272 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400273 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275 peeker.fPatch->serialize(array);
276 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
277 }
278
279 // detach bitmap from its autotdeleter, since we want to own it now
280 adb.detach();
281
282 if (padding) {
283 if (peeker.fPatchIsValid) {
284 GraphicsJNI::set_jrect(env, padding,
285 peeker.fPatch->paddingLeft,
286 peeker.fPatch->paddingTop,
287 peeker.fPatch->paddingRight,
288 peeker.fPatch->paddingBottom);
289 } else {
290 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
291 }
292 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293
Mike Reedc70e06b2009-04-24 11:09:12 -0400294 SkPixelRef* pr;
295 if (isPurgeable) {
Mike Reed17154412009-09-24 12:35:27 -0400296 pr = installPixelRef(bitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400297 } else {
298 // if we get here, we're in kDecodePixels_Mode and will therefore
299 // already have a pixelref installed.
300 pr = bitmap->pixelRef();
301 }
302 // promise we will never change our pixels (great for sharing and pictures)
303 pr->setImmutable();
304 // now create the java bitmap
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
306}
307
308static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
309 jobject is, // InputStream
310 jbyteArray storage, // byte[]
311 jobject padding,
312 jobject options) { // BitmapFactory$Options
313 jobject bitmap = NULL;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800314 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315
316 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400317 // for now we don't allow purgeable with java inputstreams
318 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 stream->unref();
320 }
321 return bitmap;
322}
323
324static ssize_t getFDSize(int fd) {
Kenny Rootddb76c42010-11-24 12:56:06 -0800325 off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 if (curr < 0) {
327 return 0;
328 }
329 size_t size = ::lseek(fd, 0, SEEK_END);
Kenny Rootddb76c42010-11-24 12:56:06 -0800330 ::lseek64(fd, curr, SEEK_SET);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 return size;
332}
333
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
335 jobject fileDescriptor,
336 jobject padding,
337 jobject bitmapFactoryOptions) {
338 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
339
340 jint descriptor = env->GetIntField(fileDescriptor,
341 gFileDescriptor_descriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400342
343 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
344 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
345 bool weOwnTheFD = false;
346 if (isPurgeable && isShareable) {
347 int newFD = ::dup(descriptor);
348 if (-1 != newFD) {
349 weOwnTheFD = true;
350 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
352 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353
Mike Reedc70e06b2009-04-24 11:09:12 -0400354 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
355 SkAutoUnref aur(stream);
356 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 return NULL;
358 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400359
360 /* Restore our offset when we leave, so we can be called more than once
361 with the same descriptor. This is only required if we didn't dup the
362 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 */
364 AutoFDSeek as(descriptor);
365
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700366 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
367 shareable case.
368 */
369 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400370}
371
372/* make a deep copy of the asset, and return it as a stream, or NULL if there
373 was an error.
374 */
375static SkStream* copyAssetToStream(Asset* asset) {
376 // if we could "ref/reopen" the asset, we may not need to copy it here
Kenny Rootddb76c42010-11-24 12:56:06 -0800377 off64_t size = asset->seek(0, SEEK_SET);
378 if ((off64_t)-1 == size) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400379 SkDebugf("---- copyAsset: asset rewind failed\n");
380 return NULL;
381 }
382
383 size = asset->getLength();
384 if (size <= 0) {
385 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
386 return NULL;
387 }
388
389 SkStream* stream = new SkMemoryStream(size);
390 void* data = const_cast<void*>(stream->getMemoryBase());
Kenny Rootddb76c42010-11-24 12:56:06 -0800391 off64_t len = asset->read(data, size);
Mike Reedc70e06b2009-04-24 11:09:12 -0400392 if (len != size) {
393 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
394 delete stream;
395 stream = NULL;
396 }
397 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398}
399
400static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
401 jint native_asset, // Asset
402 jobject padding, // Rect
403 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400404 SkStream* stream;
405 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Romain Guy18ef37252010-08-19 15:10:54 -0700406 bool forcePurgeable = optionsPurgeable(env, options);
407 if (forcePurgeable) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400408 // if we could "ref/reopen" the asset, we may not need to copy it here
409 // and we could assume optionsShareable, since assets are always RO
410 stream = copyAssetToStream(asset);
411 if (NULL == stream) {
412 return NULL;
413 }
414 } else {
415 // since we know we'll be done with the asset when we return, we can
416 // just use a simple wrapper
417 stream = new AssetStreamAdaptor(asset);
418 }
419 SkAutoUnref aur(stream);
Mike Reed36ad54a2010-03-10 10:28:10 -0500420 return doDecode(env, stream, padding, options, true, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421}
422
423static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
424 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400425 /* If optionsShareable() we could decide to just wrap the java array and
426 share it, but that means adding a globalref to the java array object
427 and managing its lifetime. For now we just always copy the array's data
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000428 if optionsPurgeable(), unless we're just decoding bounds.
Mike Reedc70e06b2009-04-24 11:09:12 -0400429 */
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000430 bool purgeable = optionsPurgeable(env, options)
431 && !optionsJustBounds(env, options);
Mike Reedc70e06b2009-04-24 11:09:12 -0400432 AutoJavaByteArray ar(env, byteArray);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000433 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
Mike Reedc70e06b2009-04-24 11:09:12 -0400434 SkAutoUnref aur(stream);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000435 return doDecode(env, stream, NULL, options, purgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436}
437
438static void nativeRequestCancel(JNIEnv*, jobject joptions) {
439 (void)AutoDecoderCancel::RequestCancel(joptions);
440}
441
442static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
443 jobject padding) {
444
445 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
446 if (array != NULL) {
447 size_t chunkSize = env->GetArrayLength(chunkObject);
448 void* storage = alloca(chunkSize);
449 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
450 memcpy(chunk, array, chunkSize);
451 android::Res_png_9patch::deserialize(chunk);
452
453 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
454 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
455 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
456 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
457
458 for (int i = 0; i < chunk->numXDivs; i++) {
459 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
460 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
461 chunk->xDivs[i]++;
462 }
463 }
464
465 for (int i = 0; i < chunk->numYDivs; i++) {
466 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
467 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
468 chunk->yDivs[i]++;
469 }
470 }
471
472 memcpy(array, chunk, chunkSize);
473
474 if (padding) {
475 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
476 chunk->paddingRight, chunk->paddingBottom);
477 }
478
479 env->ReleaseByteArrayElements(chunkObject, array, 0);
480 }
481 return chunkObject;
482}
483
Mike Reedab4a0c12010-01-26 10:13:53 -0500484static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
485 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
486
487 // these are the only default configs that make sense for codecs right now
488 static const SkBitmap::Config gValidDefConfig[] = {
489 SkBitmap::kRGB_565_Config,
490 SkBitmap::kARGB_8888_Config,
491 };
492
493 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
494 if (config == gValidDefConfig[i]) {
495 SkImageDecoder::SetDeviceConfig(config);
496 break;
497 }
498 }
499}
500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501///////////////////////////////////////////////////////////////////////////////
502
503static JNINativeMethod gMethods[] = {
504 { "nativeDecodeStream",
505 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
506 (void*)nativeDecodeStream
507 },
508
509 { "nativeDecodeFileDescriptor",
510 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
511 (void*)nativeDecodeFileDescriptor
512 },
513
514 { "nativeDecodeAsset",
515 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
516 (void*)nativeDecodeAsset
517 },
518
519 { "nativeDecodeByteArray",
520 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
521 (void*)nativeDecodeByteArray
522 },
523
524 { "nativeScaleNinePatch",
525 "([BFLandroid/graphics/Rect;)[B",
526 (void*)nativeScaleNinePatch
Mike Reedab4a0c12010-01-26 10:13:53 -0500527 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528
Mike Reedab4a0c12010-01-26 10:13:53 -0500529 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530};
531
532static JNINativeMethod gOptionsMethods[] = {
533 { "requestCancel", "()V", (void*)nativeRequestCancel }
534};
535
536static jclass make_globalref(JNIEnv* env, const char classname[]) {
537 jclass c = env->FindClass(classname);
538 SkASSERT(c);
539 return (jclass)env->NewGlobalRef(c);
540}
541
542static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
543 const char fieldname[], const char type[]) {
544 jfieldID id = env->GetFieldID(clazz, fieldname, type);
545 SkASSERT(id);
546 return id;
547}
548
549#define kClassPathName "android/graphics/BitmapFactory"
550
551#define RETURN_ERR_IF_NULL(value) \
552 do { if (!(value)) { assert(0); return -1; } } while (false)
553
554int register_android_graphics_BitmapFactory(JNIEnv* env);
555int register_android_graphics_BitmapFactory(JNIEnv* env) {
556 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
557 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
558 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
559 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
560 "Landroid/graphics/Bitmap$Config;");
561 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
Mike Reedc70e06b2009-04-24 11:09:12 -0400562 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
563 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
Mike Reed1b22b972009-07-17 11:21:47 -0400564 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800565 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class,
566 "inPreferQualityOverSpeed", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
568 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
569 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700570 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571
572 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
573 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
574
575 int ret = AndroidRuntime::registerNativeMethods(env,
576 "android/graphics/BitmapFactory$Options",
577 gOptionsMethods,
578 SK_ARRAY_COUNT(gOptionsMethods));
579 if (ret) {
580 return ret;
581 }
582 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
583 gMethods, SK_ARRAY_COUNT(gMethods));
584}