blob: b41bad0d66e8f703474d49499d15f75a1c90b752 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001#define LOG_TAG "BitmapFactory"
2
3#include "SkImageDecoder.h"
Mike Reedc70e06b2009-04-24 11:09:12 -04004#include "SkImageRef_ashmem.h"
5#include "SkImageRef_GlobalPool.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006#include "SkPixelRef.h"
7#include "SkStream.h"
8#include "GraphicsJNI.h"
9#include "SkTemplates.h"
10#include "SkUtils.h"
11#include "CreateJavaOutputStreamAdaptor.h"
12
13#include <android_runtime/AndroidRuntime.h>
14#include <utils/Asset.h>
15#include <utils/ResourceTypes.h>
16#include <netinet/in.h>
17#include <sys/mman.h>
18
19static jclass gOptions_class;
20static jfieldID gOptions_justBoundsFieldID;
21static jfieldID gOptions_sampleSizeFieldID;
22static jfieldID gOptions_configFieldID;
23static jfieldID gOptions_ditherFieldID;
Mike Reedc70e06b2009-04-24 11:09:12 -040024static jfieldID gOptions_purgeableFieldID;
25static jfieldID gOptions_shareableFieldID;
Mike Reed1b22b972009-07-17 11:21:47 -040026static jfieldID gOptions_nativeAllocFieldID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027static jfieldID gOptions_widthFieldID;
28static jfieldID gOptions_heightFieldID;
29static jfieldID gOptions_mimeFieldID;
Ray Chen0a6a0e92009-04-02 01:31:49 -070030static jfieldID gOptions_mCancelID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
32static jclass gFileDescriptor_class;
33static jfieldID gFileDescriptor_descriptor;
34
35#if 0
36 #define TRACE_BITMAP(code) code
37#else
38 #define TRACE_BITMAP(code)
39#endif
40
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041///////////////////////////////////////////////////////////////////////////////
42
43class AutoDecoderCancel {
44public:
45 AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
46 ~AutoDecoderCancel();
47
48 static bool RequestCancel(jobject options);
49
50private:
51 AutoDecoderCancel* fNext;
52 AutoDecoderCancel* fPrev;
53 jobject fJOptions; // java options object
54 SkImageDecoder* fDecoder;
55
56#ifdef SK_DEBUG
57 static void Validate();
58#else
59 static void Validate() {}
60#endif
61};
62
63static SkMutex gAutoDecoderCancelMutex;
64static AutoDecoderCancel* gAutoDecoderCancel;
65#ifdef SK_DEBUG
66 static int gAutoDecoderCancelCount;
67#endif
68
69AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
70 SkImageDecoder* decoder) {
71 fJOptions = joptions;
72 fDecoder = decoder;
73
74 if (NULL != joptions) {
75 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
76
77 // Add us as the head of the list
78 fPrev = NULL;
79 fNext = gAutoDecoderCancel;
80 if (gAutoDecoderCancel) {
81 gAutoDecoderCancel->fPrev = this;
82 }
83 gAutoDecoderCancel = this;
84
85 SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
86 Validate();
87 }
88}
89
90AutoDecoderCancel::~AutoDecoderCancel() {
91 if (NULL != fJOptions) {
92 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
93
94 // take us out of the dllist
95 AutoDecoderCancel* prev = fPrev;
96 AutoDecoderCancel* next = fNext;
97
98 if (prev) {
99 SkASSERT(prev->fNext == this);
100 prev->fNext = next;
101 } else {
102 SkASSERT(gAutoDecoderCancel == this);
103 gAutoDecoderCancel = next;
104 }
105 if (next) {
106 SkASSERT(next->fPrev == this);
107 next->fPrev = prev;
108 }
109
110 SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
111 Validate();
112 }
113}
114
115bool AutoDecoderCancel::RequestCancel(jobject joptions) {
116 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
117
118 Validate();
119
120 AutoDecoderCancel* pair = gAutoDecoderCancel;
121 while (pair != NULL) {
122 if (pair->fJOptions == joptions) {
123 pair->fDecoder->cancelDecode();
124 return true;
125 }
126 pair = pair->fNext;
127 }
128 return false;
129}
130
131#ifdef SK_DEBUG
132// can only call this inside a lock on gAutoDecoderCancelMutex
133void AutoDecoderCancel::Validate() {
134 const int gCount = gAutoDecoderCancelCount;
135
136 if (gCount == 0) {
137 SkASSERT(gAutoDecoderCancel == NULL);
138 } else {
139 SkASSERT(gCount > 0);
140
141 AutoDecoderCancel* curr = gAutoDecoderCancel;
142 SkASSERT(curr);
143 SkASSERT(curr->fPrev == NULL);
144
145 int count = 0;
146 while (curr) {
147 count += 1;
148 SkASSERT(count <= gCount);
149 if (curr->fPrev) {
150 SkASSERT(curr->fPrev->fNext == curr);
151 }
152 if (curr->fNext) {
153 SkASSERT(curr->fNext->fPrev == curr);
154 }
155 curr = curr->fNext;
156 }
157 SkASSERT(count == gCount);
158 }
159}
160#endif
161
162///////////////////////////////////////////////////////////////////////////////
163
164using namespace android;
165
166class NinePatchPeeker : public SkImageDecoder::Peeker {
Mike Reed39f10ec2010-03-24 10:08:50 -0400167 SkImageDecoder* fHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168public:
Mike Reed39f10ec2010-03-24 10:08:50 -0400169 NinePatchPeeker(SkImageDecoder* host) {
170 // the host lives longer than we do, so a raw ptr is safe
171 fHost = host;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 fPatchIsValid = false;
173 }
174
175 ~NinePatchPeeker() {
176 if (fPatchIsValid) {
177 free(fPatch);
178 }
179 }
180
181 bool fPatchIsValid;
182 Res_png_9patch* fPatch;
183
184 virtual bool peek(const char tag[], const void* data, size_t length) {
185 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
186 Res_png_9patch* patch = (Res_png_9patch*) data;
187 size_t patchSize = patch->serializedSize();
188 assert(length == patchSize);
189 // You have to copy the data because it is owned by the png reader
190 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
191 memcpy(patchNew, patch, patchSize);
192 // this relies on deserialization being done in place
193 Res_png_9patch::deserialize(patchNew);
194 patchNew->fileToDevice();
195 if (fPatchIsValid) {
196 free(fPatch);
197 }
198 fPatch = patchNew;
199 //printf("9patch: (%d,%d)-(%d,%d)\n",
200 // fPatch.sizeLeft, fPatch.sizeTop,
201 // fPatch.sizeRight, fPatch.sizeBottom);
202 fPatchIsValid = true;
Mike Reed39f10ec2010-03-24 10:08:50 -0400203
204 // now update our host to force index or 32bit config
205 // 'cause we don't want 565 predithered, since as a 9patch, we know
206 // we will be stretched, and therefore we want to dither afterwards.
207 static const SkBitmap::Config gNo565Pref[] = {
208 SkBitmap::kIndex8_Config,
209 SkBitmap::kIndex8_Config,
210 SkBitmap::kARGB_8888_Config,
211 SkBitmap::kARGB_8888_Config,
212 SkBitmap::kARGB_8888_Config,
213 SkBitmap::kARGB_8888_Config,
214 };
215 fHost->setPrefConfigTable(gNo565Pref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 } else {
217 fPatch = NULL;
218 }
219 return true; // keep on decoding
220 }
221};
222
223class AssetStreamAdaptor : public SkStream {
224public:
225 AssetStreamAdaptor(Asset* a) : fAsset(a) {}
226
Ray Chen0a6a0e92009-04-02 01:31:49 -0700227 virtual bool rewind() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 off_t pos = fAsset->seek(0, SEEK_SET);
Mike Reedc70e06b2009-04-24 11:09:12 -0400229 if (pos == (off_t)-1) {
230 SkDebugf("----- fAsset->seek(rewind) failed\n");
231 return false;
232 }
233 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 }
235
Ray Chen0a6a0e92009-04-02 01:31:49 -0700236 virtual size_t read(void* buffer, size_t size) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 ssize_t amount;
238
239 if (NULL == buffer) {
240 if (0 == size) { // caller is asking us for our total length
241 return fAsset->getLength();
242 }
243 // asset->seek returns new total offset
244 // we want to return amount that was skipped
245
246 off_t oldOffset = fAsset->seek(0, SEEK_CUR);
247 if (-1 == oldOffset) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400248 SkDebugf("---- fAsset->seek(oldOffset) failed\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 return 0;
250 }
251 off_t newOffset = fAsset->seek(size, SEEK_CUR);
252 if (-1 == newOffset) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400253 SkDebugf("---- fAsset->seek(%d) failed\n", size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 return 0;
255 }
256 amount = newOffset - oldOffset;
257 } else {
258 amount = fAsset->read(buffer, size);
Mike Reedc70e06b2009-04-24 11:09:12 -0400259 if (amount <= 0) {
260 SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
261 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 }
263
264 if (amount < 0) {
265 amount = 0;
266 }
267 return amount;
268 }
269
270private:
271 Asset* fAsset;
272};
273
274///////////////////////////////////////////////////////////////////////////////
275
276static inline int32_t validOrNeg1(bool isValid, int32_t value) {
277// return isValid ? value : -1;
278 SkASSERT((int)isValid == 0 || (int)isValid == 1);
279 return ((int32_t)isValid - 1) | value;
280}
281
282static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
283 static const struct {
284 SkImageDecoder::Format fFormat;
285 const char* fMimeType;
286 } gMimeTypes[] = {
287 { SkImageDecoder::kBMP_Format, "image/bmp" },
288 { SkImageDecoder::kGIF_Format, "image/gif" },
289 { SkImageDecoder::kICO_Format, "image/x-ico" },
290 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
291 { SkImageDecoder::kPNG_Format, "image/png" },
292 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
293 };
294
295 const char* cstr = NULL;
296 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
297 if (gMimeTypes[i].fFormat == format) {
298 cstr = gMimeTypes[i].fMimeType;
299 break;
300 }
301 }
302
303 jstring jstr = 0;
304 if (NULL != cstr) {
305 jstr = env->NewStringUTF(cstr);
306 }
307 return jstr;
308}
309
Mike Reedc70e06b2009-04-24 11:09:12 -0400310static bool optionsPurgeable(JNIEnv* env, jobject options) {
311 return options != NULL &&
312 env->GetBooleanField(options, gOptions_purgeableFieldID);
313}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314
Mike Reedc70e06b2009-04-24 11:09:12 -0400315static bool optionsShareable(JNIEnv* env, jobject options) {
316 return options != NULL &&
317 env->GetBooleanField(options, gOptions_shareableFieldID);
318}
319
Mike Reed1b22b972009-07-17 11:21:47 -0400320static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
321 return NULL == options ||
322 !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
323}
324
Mike Reedc70e06b2009-04-24 11:09:12 -0400325static jobject nullObjectReturn(const char msg[]) {
326 if (msg) {
327 SkDebugf("--- %s\n", msg);
328 }
329 return NULL;
330}
331
332static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
Mike Reed17154412009-09-24 12:35:27 -0400333 int sampleSize, bool ditherImage) {
334 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400335 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700336 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400337 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
338 } else {
339 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
340 }
Mike Reed17154412009-09-24 12:35:27 -0400341 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400342 bitmap->setPixelRef(pr)->unref();
343 return pr;
344}
345
346// since we "may" create a purgeable imageref, we require the stream be ref'able
347// i.e. dynamically allocated, since its lifetime may exceed the current stack
348// frame.
349static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
Mike Reed36ad54a2010-03-10 10:28:10 -0500350 jobject options, bool allowPurgeable,
351 bool forcePurgeable = false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 int sampleSize = 1;
353 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
354 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
355 bool doDither = true;
Mike Reed36ad54a2010-03-10 10:28:10 -0500356 bool isPurgeable = forcePurgeable ||
357 (allowPurgeable && optionsPurgeable(env, options));
Mike Reed1b22b972009-07-17 11:21:47 -0400358 bool reportSizeToVM = optionsReportSizeToVM(env, options);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359
360 if (NULL != options) {
361 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
362 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
363 mode = SkImageDecoder::kDecodeBounds_Mode;
364 }
365 // initialize these, in case we fail later on
366 env->SetIntField(options, gOptions_widthFieldID, -1);
367 env->SetIntField(options, gOptions_heightFieldID, -1);
368 env->SetObjectField(options, gOptions_mimeFieldID, 0);
369
370 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
371 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
372 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
373 }
374
375 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
376 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400377 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 }
379
380 decoder->setSampleSize(sampleSize);
381 decoder->setDitherImage(doDither);
382
Mike Reed39f10ec2010-03-24 10:08:50 -0400383 NinePatchPeeker peeker(decoder);
Mike Reed1b22b972009-07-17 11:21:47 -0400384 JavaPixelAllocator javaAllocator(env, reportSizeToVM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 SkBitmap* bitmap = new SkBitmap;
386 Res_png_9patch dummy9Patch;
387
388 SkAutoTDelete<SkImageDecoder> add(decoder);
389 SkAutoTDelete<SkBitmap> adb(bitmap);
390
391 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400392 if (!isPurgeable) {
393 decoder->setAllocator(&javaAllocator);
394 }
395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 AutoDecoderCancel adc(options, decoder);
397
Ray Chen0a6a0e92009-04-02 01:31:49 -0700398 // To fix the race condition in case "requestCancelDecode"
399 // happens earlier than AutoDecoderCancel object is added
400 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400401 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
402 return nullObjectReturn("gOptions_mCancelID");;
Ray Chen0a6a0e92009-04-02 01:31:49 -0700403 }
404
Mike Reedc70e06b2009-04-24 11:09:12 -0400405 SkImageDecoder::Mode decodeMode = mode;
406 if (isPurgeable) {
407 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400409 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
410 return nullObjectReturn("decoder->decode returned false");
411 }
412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 // update options (if any)
414 if (NULL != options) {
415 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
416 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
417 // TODO: set the mimeType field with the data from the codec.
418 // but how to reuse a set of strings, rather than allocating new one
419 // each time?
420 env->SetObjectField(options, gOptions_mimeFieldID,
421 getMimeTypeString(env, decoder->getFormat()));
422 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 // if we're in justBounds mode, return now (skip the java bitmap)
425 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
426 return NULL;
427 }
428
429 jbyteArray ninePatchChunk = NULL;
430 if (peeker.fPatchIsValid) {
431 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
432 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
433 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400434 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 }
436 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
437 NULL);
438 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400439 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 }
441 peeker.fPatch->serialize(array);
442 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
443 }
444
445 // detach bitmap from its autotdeleter, since we want to own it now
446 adb.detach();
447
448 if (padding) {
449 if (peeker.fPatchIsValid) {
450 GraphicsJNI::set_jrect(env, padding,
451 peeker.fPatch->paddingLeft,
452 peeker.fPatch->paddingTop,
453 peeker.fPatch->paddingRight,
454 peeker.fPatch->paddingBottom);
455 } else {
456 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
457 }
458 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459
Mike Reedc70e06b2009-04-24 11:09:12 -0400460 SkPixelRef* pr;
461 if (isPurgeable) {
Mike Reed17154412009-09-24 12:35:27 -0400462 pr = installPixelRef(bitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400463 } else {
464 // if we get here, we're in kDecodePixels_Mode and will therefore
465 // already have a pixelref installed.
466 pr = bitmap->pixelRef();
467 }
468 // promise we will never change our pixels (great for sharing and pictures)
469 pr->setImmutable();
470 // now create the java bitmap
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
472}
473
474static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
475 jobject is, // InputStream
476 jbyteArray storage, // byte[]
477 jobject padding,
478 jobject options) { // BitmapFactory$Options
479 jobject bitmap = NULL;
480 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
481
482 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400483 // for now we don't allow purgeable with java inputstreams
484 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 stream->unref();
486 }
487 return bitmap;
488}
489
490static ssize_t getFDSize(int fd) {
491 off_t curr = ::lseek(fd, 0, SEEK_CUR);
492 if (curr < 0) {
493 return 0;
494 }
495 size_t size = ::lseek(fd, 0, SEEK_END);
496 ::lseek(fd, curr, SEEK_SET);
497 return size;
498}
499
500/** Restore the file descriptor's offset in our destructor
501 */
502class AutoFDSeek {
503public:
504 AutoFDSeek(int fd) : fFD(fd) {
505 fCurr = ::lseek(fd, 0, SEEK_CUR);
506 }
507 ~AutoFDSeek() {
508 if (fCurr >= 0) {
509 ::lseek(fFD, fCurr, SEEK_SET);
510 }
511 }
512private:
513 int fFD;
514 off_t fCurr;
515};
516
517static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
518 jobject fileDescriptor,
519 jobject padding,
520 jobject bitmapFactoryOptions) {
521 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
522
523 jint descriptor = env->GetIntField(fileDescriptor,
524 gFileDescriptor_descriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400525
526 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
527 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
528 bool weOwnTheFD = false;
529 if (isPurgeable && isShareable) {
530 int newFD = ::dup(descriptor);
531 if (-1 != newFD) {
532 weOwnTheFD = true;
533 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 }
535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536
Mike Reedc70e06b2009-04-24 11:09:12 -0400537 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
538 SkAutoUnref aur(stream);
539 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 return NULL;
541 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400542
543 /* Restore our offset when we leave, so we can be called more than once
544 with the same descriptor. This is only required if we didn't dup the
545 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 */
547 AutoFDSeek as(descriptor);
548
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700549 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
550 shareable case.
551 */
552 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400553}
554
555/* make a deep copy of the asset, and return it as a stream, or NULL if there
556 was an error.
557 */
558static SkStream* copyAssetToStream(Asset* asset) {
559 // if we could "ref/reopen" the asset, we may not need to copy it here
560 off_t size = asset->seek(0, SEEK_SET);
561 if ((off_t)-1 == size) {
562 SkDebugf("---- copyAsset: asset rewind failed\n");
563 return NULL;
564 }
565
566 size = asset->getLength();
567 if (size <= 0) {
568 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
569 return NULL;
570 }
571
572 SkStream* stream = new SkMemoryStream(size);
573 void* data = const_cast<void*>(stream->getMemoryBase());
574 off_t len = asset->read(data, size);
575 if (len != size) {
576 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
577 delete stream;
578 stream = NULL;
579 }
580 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581}
582
583static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
584 jint native_asset, // Asset
585 jobject padding, // Rect
586 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400587 SkStream* stream;
588 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Mike Reed36ad54a2010-03-10 10:28:10 -0500589 // assets can always be rebuilt, so force this
590 bool forcePurgeable = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591
Mike Reed36ad54a2010-03-10 10:28:10 -0500592 if (forcePurgeable || optionsPurgeable(env, options)) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400593 // if we could "ref/reopen" the asset, we may not need to copy it here
594 // and we could assume optionsShareable, since assets are always RO
595 stream = copyAssetToStream(asset);
596 if (NULL == stream) {
597 return NULL;
598 }
599 } else {
600 // since we know we'll be done with the asset when we return, we can
601 // just use a simple wrapper
602 stream = new AssetStreamAdaptor(asset);
603 }
604 SkAutoUnref aur(stream);
Mike Reed36ad54a2010-03-10 10:28:10 -0500605 return doDecode(env, stream, padding, options, true, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606}
607
608static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
609 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400610 /* If optionsShareable() we could decide to just wrap the java array and
611 share it, but that means adding a globalref to the java array object
612 and managing its lifetime. For now we just always copy the array's data
613 if optionsPurgeable().
614 */
615 AutoJavaByteArray ar(env, byteArray);
616 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
617 optionsPurgeable(env, options));
618 SkAutoUnref aur(stream);
619 return doDecode(env, stream, NULL, options, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620}
621
622static void nativeRequestCancel(JNIEnv*, jobject joptions) {
623 (void)AutoDecoderCancel::RequestCancel(joptions);
624}
625
626static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
627 jobject padding) {
628
629 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
630 if (array != NULL) {
631 size_t chunkSize = env->GetArrayLength(chunkObject);
632 void* storage = alloca(chunkSize);
633 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
634 memcpy(chunk, array, chunkSize);
635 android::Res_png_9patch::deserialize(chunk);
636
637 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
638 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
639 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
640 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
641
642 for (int i = 0; i < chunk->numXDivs; i++) {
643 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
644 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
645 chunk->xDivs[i]++;
646 }
647 }
648
649 for (int i = 0; i < chunk->numYDivs; i++) {
650 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
651 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
652 chunk->yDivs[i]++;
653 }
654 }
655
656 memcpy(array, chunk, chunkSize);
657
658 if (padding) {
659 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
660 chunk->paddingRight, chunk->paddingBottom);
661 }
662
663 env->ReleaseByteArrayElements(chunkObject, array, 0);
664 }
665 return chunkObject;
666}
667
Mike Reedab4a0c12010-01-26 10:13:53 -0500668static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
669 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
670
671 // these are the only default configs that make sense for codecs right now
672 static const SkBitmap::Config gValidDefConfig[] = {
673 SkBitmap::kRGB_565_Config,
674 SkBitmap::kARGB_8888_Config,
675 };
676
677 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
678 if (config == gValidDefConfig[i]) {
679 SkImageDecoder::SetDeviceConfig(config);
680 break;
681 }
682 }
683}
684
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685///////////////////////////////////////////////////////////////////////////////
686
687static JNINativeMethod gMethods[] = {
688 { "nativeDecodeStream",
689 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
690 (void*)nativeDecodeStream
691 },
692
693 { "nativeDecodeFileDescriptor",
694 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
695 (void*)nativeDecodeFileDescriptor
696 },
697
698 { "nativeDecodeAsset",
699 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
700 (void*)nativeDecodeAsset
701 },
702
703 { "nativeDecodeByteArray",
704 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
705 (void*)nativeDecodeByteArray
706 },
707
708 { "nativeScaleNinePatch",
709 "([BFLandroid/graphics/Rect;)[B",
710 (void*)nativeScaleNinePatch
Mike Reedab4a0c12010-01-26 10:13:53 -0500711 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712
Mike Reedab4a0c12010-01-26 10:13:53 -0500713 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714};
715
716static JNINativeMethod gOptionsMethods[] = {
717 { "requestCancel", "()V", (void*)nativeRequestCancel }
718};
719
720static jclass make_globalref(JNIEnv* env, const char classname[]) {
721 jclass c = env->FindClass(classname);
722 SkASSERT(c);
723 return (jclass)env->NewGlobalRef(c);
724}
725
726static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
727 const char fieldname[], const char type[]) {
728 jfieldID id = env->GetFieldID(clazz, fieldname, type);
729 SkASSERT(id);
730 return id;
731}
732
733#define kClassPathName "android/graphics/BitmapFactory"
734
735#define RETURN_ERR_IF_NULL(value) \
736 do { if (!(value)) { assert(0); return -1; } } while (false)
737
738int register_android_graphics_BitmapFactory(JNIEnv* env);
739int register_android_graphics_BitmapFactory(JNIEnv* env) {
740 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
741 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
742 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
743 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
744 "Landroid/graphics/Bitmap$Config;");
745 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
Mike Reedc70e06b2009-04-24 11:09:12 -0400746 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
747 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
Mike Reed1b22b972009-07-17 11:21:47 -0400748 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
750 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
751 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700752 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753
754 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
755 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
756
757 int ret = AndroidRuntime::registerNativeMethods(env,
758 "android/graphics/BitmapFactory$Options",
759 gOptionsMethods,
760 SK_ARRAY_COUNT(gOptionsMethods));
761 if (ret) {
762 return ret;
763 }
764 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
765 gMethods, SK_ARRAY_COUNT(gMethods));
766}