blob: 254d9a4f8bde29b36acccea9f6795e66d7634110 [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"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080013
14#include <android_runtime/AndroidRuntime.h>
15#include <utils/Asset.h>
16#include <utils/ResourceTypes.h>
17#include <netinet/in.h>
18#include <sys/mman.h>
19
Joseph Wenf1f48bc2010-07-19 16:59:51 +080020jclass gOptions_class;
21jfieldID gOptions_justBoundsFieldID;
22jfieldID gOptions_sampleSizeFieldID;
23jfieldID gOptions_configFieldID;
24jfieldID gOptions_ditherFieldID;
25jfieldID gOptions_purgeableFieldID;
26jfieldID gOptions_shareableFieldID;
27jfieldID gOptions_nativeAllocFieldID;
28jfieldID gOptions_widthFieldID;
29jfieldID gOptions_heightFieldID;
30jfieldID gOptions_mimeFieldID;
31jfieldID gOptions_mCancelID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
33static jclass gFileDescriptor_class;
34static jfieldID gFileDescriptor_descriptor;
35
36#if 0
37 #define TRACE_BITMAP(code) code
38#else
39 #define TRACE_BITMAP(code)
40#endif
41
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042using namespace android;
43
44class NinePatchPeeker : public SkImageDecoder::Peeker {
Mike Reed39f10ec2010-03-24 10:08:50 -040045 SkImageDecoder* fHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046public:
Mike Reed39f10ec2010-03-24 10:08:50 -040047 NinePatchPeeker(SkImageDecoder* host) {
48 // the host lives longer than we do, so a raw ptr is safe
49 fHost = host;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 fPatchIsValid = false;
51 }
52
53 ~NinePatchPeeker() {
54 if (fPatchIsValid) {
55 free(fPatch);
56 }
57 }
58
59 bool fPatchIsValid;
60 Res_png_9patch* fPatch;
61
62 virtual bool peek(const char tag[], const void* data, size_t length) {
63 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
64 Res_png_9patch* patch = (Res_png_9patch*) data;
65 size_t patchSize = patch->serializedSize();
66 assert(length == patchSize);
67 // You have to copy the data because it is owned by the png reader
68 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
69 memcpy(patchNew, patch, patchSize);
70 // this relies on deserialization being done in place
71 Res_png_9patch::deserialize(patchNew);
72 patchNew->fileToDevice();
73 if (fPatchIsValid) {
74 free(fPatch);
75 }
76 fPatch = patchNew;
77 //printf("9patch: (%d,%d)-(%d,%d)\n",
78 // fPatch.sizeLeft, fPatch.sizeTop,
79 // fPatch.sizeRight, fPatch.sizeBottom);
80 fPatchIsValid = true;
Mike Reed39f10ec2010-03-24 10:08:50 -040081
82 // now update our host to force index or 32bit config
83 // 'cause we don't want 565 predithered, since as a 9patch, we know
84 // we will be stretched, and therefore we want to dither afterwards.
85 static const SkBitmap::Config gNo565Pref[] = {
86 SkBitmap::kIndex8_Config,
87 SkBitmap::kIndex8_Config,
88 SkBitmap::kARGB_8888_Config,
89 SkBitmap::kARGB_8888_Config,
90 SkBitmap::kARGB_8888_Config,
91 SkBitmap::kARGB_8888_Config,
92 };
93 fHost->setPrefConfigTable(gNo565Pref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 } else {
95 fPatch = NULL;
96 }
97 return true; // keep on decoding
98 }
99};
100
Wei-Ta Chen340ce752010-09-08 10:44:21 +0800101class AssetStreamAdaptor : public SkStream {
102public:
103 AssetStreamAdaptor(Asset* a) : fAsset(a) {}
104
105 virtual bool rewind() {
106 off_t pos = fAsset->seek(0, SEEK_SET);
107 if (pos == (off_t)-1) {
108 SkDebugf("----- fAsset->seek(rewind) failed\n");
109 return false;
110 }
111 return true;
112 }
113
114 virtual size_t read(void* buffer, size_t size) {
115 ssize_t amount;
116
117 if (NULL == buffer) {
118 if (0 == size) { // caller is asking us for our total length
119 return fAsset->getLength();
120 }
121 // asset->seek returns new total offset
122 // we want to return amount that was skipped
123
124 off_t oldOffset = fAsset->seek(0, SEEK_CUR);
125 if (-1 == oldOffset) {
126 SkDebugf("---- fAsset->seek(oldOffset) failed\n");
127 return 0;
128 }
129 off_t newOffset = fAsset->seek(size, SEEK_CUR);
130 if (-1 == newOffset) {
131 SkDebugf("---- fAsset->seek(%d) failed\n", size);
132 return 0;
133 }
134 amount = newOffset - oldOffset;
135 } else {
136 amount = fAsset->read(buffer, size);
137 if (amount <= 0) {
138 SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
139 }
140 }
141
142 if (amount < 0) {
143 amount = 0;
144 }
145 return amount;
146 }
147
148private:
149 Asset* fAsset;
150};
151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152///////////////////////////////////////////////////////////////////////////////
153
154static inline int32_t validOrNeg1(bool isValid, int32_t value) {
155// return isValid ? value : -1;
156 SkASSERT((int)isValid == 0 || (int)isValid == 1);
157 return ((int32_t)isValid - 1) | value;
158}
159
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800160jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 static const struct {
162 SkImageDecoder::Format fFormat;
163 const char* fMimeType;
164 } gMimeTypes[] = {
165 { SkImageDecoder::kBMP_Format, "image/bmp" },
166 { SkImageDecoder::kGIF_Format, "image/gif" },
167 { SkImageDecoder::kICO_Format, "image/x-ico" },
168 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
169 { SkImageDecoder::kPNG_Format, "image/png" },
170 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
171 };
172
173 const char* cstr = NULL;
174 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
175 if (gMimeTypes[i].fFormat == format) {
176 cstr = gMimeTypes[i].fMimeType;
177 break;
178 }
179 }
180
181 jstring jstr = 0;
182 if (NULL != cstr) {
183 jstr = env->NewStringUTF(cstr);
184 }
185 return jstr;
186}
187
Mike Reedc70e06b2009-04-24 11:09:12 -0400188static bool optionsPurgeable(JNIEnv* env, jobject options) {
189 return options != NULL &&
190 env->GetBooleanField(options, gOptions_purgeableFieldID);
191}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
Mike Reedc70e06b2009-04-24 11:09:12 -0400193static bool optionsShareable(JNIEnv* env, jobject options) {
194 return options != NULL &&
195 env->GetBooleanField(options, gOptions_shareableFieldID);
196}
197
Mike Reed1b22b972009-07-17 11:21:47 -0400198static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
199 return NULL == options ||
200 !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
201}
202
Wei-Ta Chen340ce752010-09-08 10:44:21 +0800203static jobject nullObjectReturn(const char msg[]) {
204 if (msg) {
205 SkDebugf("--- %s\n", msg);
206 }
207 return NULL;
208}
Mike Reedc70e06b2009-04-24 11:09:12 -0400209
210static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
Mike Reed17154412009-09-24 12:35:27 -0400211 int sampleSize, bool ditherImage) {
212 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400213 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700214 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400215 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
216 } else {
217 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
218 }
Mike Reed17154412009-09-24 12:35:27 -0400219 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400220 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700221 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400222 return pr;
223}
224
225// since we "may" create a purgeable imageref, we require the stream be ref'able
226// i.e. dynamically allocated, since its lifetime may exceed the current stack
227// frame.
228static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
Mike Reed36ad54a2010-03-10 10:28:10 -0500229 jobject options, bool allowPurgeable,
230 bool forcePurgeable = false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 int sampleSize = 1;
232 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
233 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
234 bool doDither = true;
Mike Reed36ad54a2010-03-10 10:28:10 -0500235 bool isPurgeable = forcePurgeable ||
236 (allowPurgeable && optionsPurgeable(env, options));
Mike Reed1b22b972009-07-17 11:21:47 -0400237 bool reportSizeToVM = optionsReportSizeToVM(env, options);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238
239 if (NULL != options) {
240 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
241 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
242 mode = SkImageDecoder::kDecodeBounds_Mode;
243 }
244 // initialize these, in case we fail later on
245 env->SetIntField(options, gOptions_widthFieldID, -1);
246 env->SetIntField(options, gOptions_heightFieldID, -1);
247 env->SetObjectField(options, gOptions_mimeFieldID, 0);
248
249 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
250 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
251 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
252 }
253
254 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
255 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400256 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 }
258
259 decoder->setSampleSize(sampleSize);
260 decoder->setDitherImage(doDither);
261
Mike Reed39f10ec2010-03-24 10:08:50 -0400262 NinePatchPeeker peeker(decoder);
Mike Reed1b22b972009-07-17 11:21:47 -0400263 JavaPixelAllocator javaAllocator(env, reportSizeToVM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 SkBitmap* bitmap = new SkBitmap;
265 Res_png_9patch dummy9Patch;
266
267 SkAutoTDelete<SkImageDecoder> add(decoder);
268 SkAutoTDelete<SkBitmap> adb(bitmap);
269
270 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400271 if (!isPurgeable) {
272 decoder->setAllocator(&javaAllocator);
273 }
274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 AutoDecoderCancel adc(options, decoder);
276
Ray Chen0a6a0e92009-04-02 01:31:49 -0700277 // To fix the race condition in case "requestCancelDecode"
278 // happens earlier than AutoDecoderCancel object is added
279 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400280 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
281 return nullObjectReturn("gOptions_mCancelID");;
Ray Chen0a6a0e92009-04-02 01:31:49 -0700282 }
283
Mike Reedc70e06b2009-04-24 11:09:12 -0400284 SkImageDecoder::Mode decodeMode = mode;
285 if (isPurgeable) {
286 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400288 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
289 return nullObjectReturn("decoder->decode returned false");
290 }
291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 // update options (if any)
293 if (NULL != options) {
294 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
295 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
296 // TODO: set the mimeType field with the data from the codec.
297 // but how to reuse a set of strings, rather than allocating new one
298 // each time?
299 env->SetObjectField(options, gOptions_mimeFieldID,
300 getMimeTypeString(env, decoder->getFormat()));
301 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400302
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 // if we're in justBounds mode, return now (skip the java bitmap)
304 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
305 return NULL;
306 }
307
308 jbyteArray ninePatchChunk = NULL;
309 if (peeker.fPatchIsValid) {
310 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
311 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
312 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400313 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
316 NULL);
317 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400318 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
320 peeker.fPatch->serialize(array);
321 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
322 }
323
324 // detach bitmap from its autotdeleter, since we want to own it now
325 adb.detach();
326
327 if (padding) {
328 if (peeker.fPatchIsValid) {
329 GraphicsJNI::set_jrect(env, padding,
330 peeker.fPatch->paddingLeft,
331 peeker.fPatch->paddingTop,
332 peeker.fPatch->paddingRight,
333 peeker.fPatch->paddingBottom);
334 } else {
335 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
336 }
337 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338
Mike Reedc70e06b2009-04-24 11:09:12 -0400339 SkPixelRef* pr;
340 if (isPurgeable) {
Mike Reed17154412009-09-24 12:35:27 -0400341 pr = installPixelRef(bitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400342 } else {
343 // if we get here, we're in kDecodePixels_Mode and will therefore
344 // already have a pixelref installed.
345 pr = bitmap->pixelRef();
346 }
347 // promise we will never change our pixels (great for sharing and pictures)
348 pr->setImmutable();
349 // now create the java bitmap
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
351}
352
353static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
354 jobject is, // InputStream
355 jbyteArray storage, // byte[]
356 jobject padding,
357 jobject options) { // BitmapFactory$Options
358 jobject bitmap = NULL;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800359 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360
361 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400362 // for now we don't allow purgeable with java inputstreams
363 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 stream->unref();
365 }
366 return bitmap;
367}
368
369static ssize_t getFDSize(int fd) {
370 off_t curr = ::lseek(fd, 0, SEEK_CUR);
371 if (curr < 0) {
372 return 0;
373 }
374 size_t size = ::lseek(fd, 0, SEEK_END);
375 ::lseek(fd, curr, SEEK_SET);
376 return size;
377}
378
Wei-Ta Chen340ce752010-09-08 10:44:21 +0800379/** Restore the file descriptor's offset in our destructor
380 */
381class AutoFDSeek {
382public:
383 AutoFDSeek(int fd) : fFD(fd) {
384 fCurr = ::lseek(fd, 0, SEEK_CUR);
385 }
386 ~AutoFDSeek() {
387 if (fCurr >= 0) {
388 ::lseek(fFD, fCurr, SEEK_SET);
389 }
390 }
391private:
392 int fFD;
393 off_t fCurr;
394};
395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
397 jobject fileDescriptor,
398 jobject padding,
399 jobject bitmapFactoryOptions) {
400 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
401
402 jint descriptor = env->GetIntField(fileDescriptor,
403 gFileDescriptor_descriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400404
405 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
406 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
407 bool weOwnTheFD = false;
408 if (isPurgeable && isShareable) {
409 int newFD = ::dup(descriptor);
410 if (-1 != newFD) {
411 weOwnTheFD = true;
412 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 }
414 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415
Mike Reedc70e06b2009-04-24 11:09:12 -0400416 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
417 SkAutoUnref aur(stream);
418 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 return NULL;
420 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400421
422 /* Restore our offset when we leave, so we can be called more than once
423 with the same descriptor. This is only required if we didn't dup the
424 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 */
426 AutoFDSeek as(descriptor);
427
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700428 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
429 shareable case.
430 */
431 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400432}
433
434/* make a deep copy of the asset, and return it as a stream, or NULL if there
435 was an error.
436 */
437static SkStream* copyAssetToStream(Asset* asset) {
438 // if we could "ref/reopen" the asset, we may not need to copy it here
439 off_t size = asset->seek(0, SEEK_SET);
440 if ((off_t)-1 == size) {
441 SkDebugf("---- copyAsset: asset rewind failed\n");
442 return NULL;
443 }
444
445 size = asset->getLength();
446 if (size <= 0) {
447 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
448 return NULL;
449 }
450
451 SkStream* stream = new SkMemoryStream(size);
452 void* data = const_cast<void*>(stream->getMemoryBase());
453 off_t len = asset->read(data, size);
454 if (len != size) {
455 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
456 delete stream;
457 stream = NULL;
458 }
459 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460}
461
462static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
463 jint native_asset, // Asset
464 jobject padding, // Rect
465 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400466 SkStream* stream;
467 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Romain Guy18ef37252010-08-19 15:10:54 -0700468 bool forcePurgeable = optionsPurgeable(env, options);
469 if (forcePurgeable) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400470 // if we could "ref/reopen" the asset, we may not need to copy it here
471 // and we could assume optionsShareable, since assets are always RO
472 stream = copyAssetToStream(asset);
473 if (NULL == stream) {
474 return NULL;
475 }
476 } else {
477 // since we know we'll be done with the asset when we return, we can
478 // just use a simple wrapper
479 stream = new AssetStreamAdaptor(asset);
480 }
481 SkAutoUnref aur(stream);
Mike Reed36ad54a2010-03-10 10:28:10 -0500482 return doDecode(env, stream, padding, options, true, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483}
484
485static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
486 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400487 /* If optionsShareable() we could decide to just wrap the java array and
488 share it, but that means adding a globalref to the java array object
489 and managing its lifetime. For now we just always copy the array's data
490 if optionsPurgeable().
491 */
492 AutoJavaByteArray ar(env, byteArray);
493 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
494 optionsPurgeable(env, options));
495 SkAutoUnref aur(stream);
496 return doDecode(env, stream, NULL, options, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497}
498
499static void nativeRequestCancel(JNIEnv*, jobject joptions) {
500 (void)AutoDecoderCancel::RequestCancel(joptions);
501}
502
503static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
504 jobject padding) {
505
506 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
507 if (array != NULL) {
508 size_t chunkSize = env->GetArrayLength(chunkObject);
509 void* storage = alloca(chunkSize);
510 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
511 memcpy(chunk, array, chunkSize);
512 android::Res_png_9patch::deserialize(chunk);
513
514 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
515 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
516 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
517 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
518
519 for (int i = 0; i < chunk->numXDivs; i++) {
520 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
521 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
522 chunk->xDivs[i]++;
523 }
524 }
525
526 for (int i = 0; i < chunk->numYDivs; i++) {
527 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
528 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
529 chunk->yDivs[i]++;
530 }
531 }
532
533 memcpy(array, chunk, chunkSize);
534
535 if (padding) {
536 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
537 chunk->paddingRight, chunk->paddingBottom);
538 }
539
540 env->ReleaseByteArrayElements(chunkObject, array, 0);
541 }
542 return chunkObject;
543}
544
Mike Reedab4a0c12010-01-26 10:13:53 -0500545static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
546 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
547
548 // these are the only default configs that make sense for codecs right now
549 static const SkBitmap::Config gValidDefConfig[] = {
550 SkBitmap::kRGB_565_Config,
551 SkBitmap::kARGB_8888_Config,
552 };
553
554 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
555 if (config == gValidDefConfig[i]) {
556 SkImageDecoder::SetDeviceConfig(config);
557 break;
558 }
559 }
560}
561
Wei-Ta Chen340ce752010-09-08 10:44:21 +0800562static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
563 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
564 int width, height;
565 if (NULL == decoder) {
566 doThrowIOE(env, "Image format not supported");
567 return nullObjectReturn("SkImageDecoder::Factory returned null");
568 }
569
570 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
571 decoder->setAllocator(javaAllocator);
572 JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
573 decoder->setReporter(javaMemoryReporter);
574 javaAllocator->unref();
575 javaMemoryReporter->unref();
576
577 if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
578 char msg[1024];
579 snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
580 doThrowIOE(env, msg);
581 return nullObjectReturn("decoder->buildTileIndex returned false");
582 }
583
584 SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
585
586 return GraphicsJNI::createLargeBitmap(env, bm);
587}
588
589static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
590 int offset, int length, jboolean isShareable) {
591 AutoJavaByteArray ar(env, byteArray);
592 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
593 SkAutoUnref aur(stream);
594 if (isShareable) {
595 aur.detach();
596 }
597 return doBuildTileIndex(env, stream, isShareable);
598}
599
600static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
601 jobject fileDescriptor, jboolean isShareable) {
602 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
603
604 jint descriptor = env->GetIntField(fileDescriptor,
605 gFileDescriptor_descriptor);
606 bool weOwnTheFD = false;
607
608 if (isShareable) {
609 int newFD = ::dup(descriptor);
610 if (-1 != newFD) {
611 weOwnTheFD = true;
612 descriptor = newFD;
613 }
614 }
615
616 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
617 SkAutoUnref aur(stream);
618 if (!stream->isValid()) {
619 return NULL;
620 }
621
622 if (isShareable) {
623 aur.detach();
624 }
625
626 /* Restore our offset when we leave, so we can be called more than once
627 with the same descriptor. This is only required if we didn't dup the
628 file descriptor, but it is OK to do it all the time.
629 */
630 AutoFDSeek as(descriptor);
631
632 return doBuildTileIndex(env, stream, isShareable);
633}
634
635static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
636 jobject is, // InputStream
637 jbyteArray storage, // byte[]
638 jboolean isShareable) {
639 jobject largeBitmap = NULL;
640 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
641
642 if (stream) {
643 // for now we don't allow shareable with java inputstreams
644 largeBitmap = doBuildTileIndex(env, stream, false);
645 stream->unref();
646 }
647 return largeBitmap;
648}
649
650static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
651 jint native_asset, // Asset
652 jboolean isShareable) {
653 SkStream* stream;
654 Asset* asset = reinterpret_cast<Asset*>(native_asset);
655 stream = new AssetStreamAdaptor(asset);
656 SkAutoUnref aur(stream);
657 if (isShareable) {
658 aur.detach();
659 }
660 return doBuildTileIndex(env, stream, isShareable);
661}
662
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663///////////////////////////////////////////////////////////////////////////////
664
665static JNINativeMethod gMethods[] = {
666 { "nativeDecodeStream",
667 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
668 (void*)nativeDecodeStream
669 },
670
671 { "nativeDecodeFileDescriptor",
672 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
673 (void*)nativeDecodeFileDescriptor
674 },
675
676 { "nativeDecodeAsset",
677 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
678 (void*)nativeDecodeAsset
679 },
680
681 { "nativeDecodeByteArray",
682 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
683 (void*)nativeDecodeByteArray
684 },
685
686 { "nativeScaleNinePatch",
687 "([BFLandroid/graphics/Rect;)[B",
688 (void*)nativeScaleNinePatch
Mike Reedab4a0c12010-01-26 10:13:53 -0500689 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690
Mike Reedab4a0c12010-01-26 10:13:53 -0500691 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
Wei-Ta Chen340ce752010-09-08 10:44:21 +0800692
693 { "nativeCreateLargeBitmap",
694 "([BIIZ)Landroid/graphics/LargeBitmap;",
695 (void*)nativeCreateLargeBitmapFromByteArray
696 },
697
698 { "nativeCreateLargeBitmap",
699 "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
700 (void*)nativeCreateLargeBitmapFromStream
701 },
702
703 { "nativeCreateLargeBitmap",
704 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
705 (void*)nativeCreateLargeBitmapFromFileDescriptor
706 },
707
708 { "nativeCreateLargeBitmap",
709 "(IZ)Landroid/graphics/LargeBitmap;",
710 (void*)nativeCreateLargeBitmapFromAsset
711 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712};
713
714static JNINativeMethod gOptionsMethods[] = {
715 { "requestCancel", "()V", (void*)nativeRequestCancel }
716};
717
718static jclass make_globalref(JNIEnv* env, const char classname[]) {
719 jclass c = env->FindClass(classname);
720 SkASSERT(c);
721 return (jclass)env->NewGlobalRef(c);
722}
723
724static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
725 const char fieldname[], const char type[]) {
726 jfieldID id = env->GetFieldID(clazz, fieldname, type);
727 SkASSERT(id);
728 return id;
729}
730
731#define kClassPathName "android/graphics/BitmapFactory"
732
733#define RETURN_ERR_IF_NULL(value) \
734 do { if (!(value)) { assert(0); return -1; } } while (false)
735
736int register_android_graphics_BitmapFactory(JNIEnv* env);
737int register_android_graphics_BitmapFactory(JNIEnv* env) {
738 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
739 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
740 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
741 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
742 "Landroid/graphics/Bitmap$Config;");
743 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
Mike Reedc70e06b2009-04-24 11:09:12 -0400744 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
745 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
Mike Reed1b22b972009-07-17 11:21:47 -0400746 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
748 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
749 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700750 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751
752 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
753 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
754
755 int ret = AndroidRuntime::registerNativeMethods(env,
756 "android/graphics/BitmapFactory$Options",
757 gOptionsMethods,
758 SK_ARRAY_COUNT(gOptionsMethods));
759 if (ret) {
760 return ret;
761 }
762 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
763 gMethods, SK_ARRAY_COUNT(gMethods));
764}