blob: 137707fa93bf5a74b8d293199b4ebf6786361b51 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026static jfieldID gOptions_widthFieldID;
27static jfieldID gOptions_heightFieldID;
28static jfieldID gOptions_mimeFieldID;
Ray Chen0a6a0e92009-04-02 01:31:49 -070029static jfieldID gOptions_mCancelID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
31static jclass gFileDescriptor_class;
32static jfieldID gFileDescriptor_descriptor;
33
34#if 0
35 #define TRACE_BITMAP(code) code
36#else
37 #define TRACE_BITMAP(code)
38#endif
39
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040///////////////////////////////////////////////////////////////////////////////
41
42class AutoDecoderCancel {
43public:
44 AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
45 ~AutoDecoderCancel();
46
47 static bool RequestCancel(jobject options);
48
49private:
50 AutoDecoderCancel* fNext;
51 AutoDecoderCancel* fPrev;
52 jobject fJOptions; // java options object
53 SkImageDecoder* fDecoder;
54
55#ifdef SK_DEBUG
56 static void Validate();
57#else
58 static void Validate() {}
59#endif
60};
61
62static SkMutex gAutoDecoderCancelMutex;
63static AutoDecoderCancel* gAutoDecoderCancel;
64#ifdef SK_DEBUG
65 static int gAutoDecoderCancelCount;
66#endif
67
68AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
69 SkImageDecoder* decoder) {
70 fJOptions = joptions;
71 fDecoder = decoder;
72
73 if (NULL != joptions) {
74 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
75
76 // Add us as the head of the list
77 fPrev = NULL;
78 fNext = gAutoDecoderCancel;
79 if (gAutoDecoderCancel) {
80 gAutoDecoderCancel->fPrev = this;
81 }
82 gAutoDecoderCancel = this;
83
84 SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
85 Validate();
86 }
87}
88
89AutoDecoderCancel::~AutoDecoderCancel() {
90 if (NULL != fJOptions) {
91 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
92
93 // take us out of the dllist
94 AutoDecoderCancel* prev = fPrev;
95 AutoDecoderCancel* next = fNext;
96
97 if (prev) {
98 SkASSERT(prev->fNext == this);
99 prev->fNext = next;
100 } else {
101 SkASSERT(gAutoDecoderCancel == this);
102 gAutoDecoderCancel = next;
103 }
104 if (next) {
105 SkASSERT(next->fPrev == this);
106 next->fPrev = prev;
107 }
108
109 SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
110 Validate();
111 }
112}
113
114bool AutoDecoderCancel::RequestCancel(jobject joptions) {
115 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
116
117 Validate();
118
119 AutoDecoderCancel* pair = gAutoDecoderCancel;
120 while (pair != NULL) {
121 if (pair->fJOptions == joptions) {
122 pair->fDecoder->cancelDecode();
123 return true;
124 }
125 pair = pair->fNext;
126 }
127 return false;
128}
129
130#ifdef SK_DEBUG
131// can only call this inside a lock on gAutoDecoderCancelMutex
132void AutoDecoderCancel::Validate() {
133 const int gCount = gAutoDecoderCancelCount;
134
135 if (gCount == 0) {
136 SkASSERT(gAutoDecoderCancel == NULL);
137 } else {
138 SkASSERT(gCount > 0);
139
140 AutoDecoderCancel* curr = gAutoDecoderCancel;
141 SkASSERT(curr);
142 SkASSERT(curr->fPrev == NULL);
143
144 int count = 0;
145 while (curr) {
146 count += 1;
147 SkASSERT(count <= gCount);
148 if (curr->fPrev) {
149 SkASSERT(curr->fPrev->fNext == curr);
150 }
151 if (curr->fNext) {
152 SkASSERT(curr->fNext->fPrev == curr);
153 }
154 curr = curr->fNext;
155 }
156 SkASSERT(count == gCount);
157 }
158}
159#endif
160
161///////////////////////////////////////////////////////////////////////////////
162
163using namespace android;
164
165class NinePatchPeeker : public SkImageDecoder::Peeker {
166public:
167 NinePatchPeeker() {
168 fPatchIsValid = false;
169 }
170
171 ~NinePatchPeeker() {
172 if (fPatchIsValid) {
173 free(fPatch);
174 }
175 }
176
177 bool fPatchIsValid;
178 Res_png_9patch* fPatch;
179
180 virtual bool peek(const char tag[], const void* data, size_t length) {
181 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
182 Res_png_9patch* patch = (Res_png_9patch*) data;
183 size_t patchSize = patch->serializedSize();
184 assert(length == patchSize);
185 // You have to copy the data because it is owned by the png reader
186 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
187 memcpy(patchNew, patch, patchSize);
188 // this relies on deserialization being done in place
189 Res_png_9patch::deserialize(patchNew);
190 patchNew->fileToDevice();
191 if (fPatchIsValid) {
192 free(fPatch);
193 }
194 fPatch = patchNew;
195 //printf("9patch: (%d,%d)-(%d,%d)\n",
196 // fPatch.sizeLeft, fPatch.sizeTop,
197 // fPatch.sizeRight, fPatch.sizeBottom);
198 fPatchIsValid = true;
199 } else {
200 fPatch = NULL;
201 }
202 return true; // keep on decoding
203 }
204};
205
206class AssetStreamAdaptor : public SkStream {
207public:
208 AssetStreamAdaptor(Asset* a) : fAsset(a) {}
209
Ray Chen0a6a0e92009-04-02 01:31:49 -0700210 virtual bool rewind() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 off_t pos = fAsset->seek(0, SEEK_SET);
Mike Reedc70e06b2009-04-24 11:09:12 -0400212 if (pos == (off_t)-1) {
213 SkDebugf("----- fAsset->seek(rewind) failed\n");
214 return false;
215 }
216 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 }
218
Ray Chen0a6a0e92009-04-02 01:31:49 -0700219 virtual size_t read(void* buffer, size_t size) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 ssize_t amount;
221
222 if (NULL == buffer) {
223 if (0 == size) { // caller is asking us for our total length
224 return fAsset->getLength();
225 }
226 // asset->seek returns new total offset
227 // we want to return amount that was skipped
228
229 off_t oldOffset = fAsset->seek(0, SEEK_CUR);
230 if (-1 == oldOffset) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400231 SkDebugf("---- fAsset->seek(oldOffset) failed\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 return 0;
233 }
234 off_t newOffset = fAsset->seek(size, SEEK_CUR);
235 if (-1 == newOffset) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400236 SkDebugf("---- fAsset->seek(%d) failed\n", size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 return 0;
238 }
239 amount = newOffset - oldOffset;
240 } else {
241 amount = fAsset->read(buffer, size);
Mike Reedc70e06b2009-04-24 11:09:12 -0400242 if (amount <= 0) {
243 SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
244 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 }
246
247 if (amount < 0) {
248 amount = 0;
249 }
250 return amount;
251 }
252
253private:
254 Asset* fAsset;
255};
256
257///////////////////////////////////////////////////////////////////////////////
258
259static inline int32_t validOrNeg1(bool isValid, int32_t value) {
260// return isValid ? value : -1;
261 SkASSERT((int)isValid == 0 || (int)isValid == 1);
262 return ((int32_t)isValid - 1) | value;
263}
264
265static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
266 static const struct {
267 SkImageDecoder::Format fFormat;
268 const char* fMimeType;
269 } gMimeTypes[] = {
270 { SkImageDecoder::kBMP_Format, "image/bmp" },
271 { SkImageDecoder::kGIF_Format, "image/gif" },
272 { SkImageDecoder::kICO_Format, "image/x-ico" },
273 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
274 { SkImageDecoder::kPNG_Format, "image/png" },
275 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
276 };
277
278 const char* cstr = NULL;
279 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
280 if (gMimeTypes[i].fFormat == format) {
281 cstr = gMimeTypes[i].fMimeType;
282 break;
283 }
284 }
285
286 jstring jstr = 0;
287 if (NULL != cstr) {
288 jstr = env->NewStringUTF(cstr);
289 }
290 return jstr;
291}
292
Mike Reedc70e06b2009-04-24 11:09:12 -0400293static bool optionsPurgeable(JNIEnv* env, jobject options) {
294 return options != NULL &&
295 env->GetBooleanField(options, gOptions_purgeableFieldID);
296}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297
Mike Reedc70e06b2009-04-24 11:09:12 -0400298static bool optionsShareable(JNIEnv* env, jobject options) {
299 return options != NULL &&
300 env->GetBooleanField(options, gOptions_shareableFieldID);
301}
302
303static jobject nullObjectReturn(const char msg[]) {
304 if (msg) {
305 SkDebugf("--- %s\n", msg);
306 }
307 return NULL;
308}
309
310static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
311 int sampleSize) {
312 SkPixelRef* pr;
313 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700314 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400315 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
316 } else {
317 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
318 }
319 bitmap->setPixelRef(pr)->unref();
320 return pr;
321}
322
323// since we "may" create a purgeable imageref, we require the stream be ref'able
324// i.e. dynamically allocated, since its lifetime may exceed the current stack
325// frame.
326static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
327 jobject options, bool allowPurgeable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 int sampleSize = 1;
329 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
330 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
331 bool doDither = true;
Mike Reedc70e06b2009-04-24 11:09:12 -0400332 bool isPurgeable = allowPurgeable && optionsPurgeable(env, options);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333
334 if (NULL != options) {
335 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
336 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
337 mode = SkImageDecoder::kDecodeBounds_Mode;
338 }
339 // initialize these, in case we fail later on
340 env->SetIntField(options, gOptions_widthFieldID, -1);
341 env->SetIntField(options, gOptions_heightFieldID, -1);
342 env->SetObjectField(options, gOptions_mimeFieldID, 0);
343
344 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
345 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
346 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
347 }
348
349 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
350 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400351 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
353
354 decoder->setSampleSize(sampleSize);
355 decoder->setDitherImage(doDither);
356
357 NinePatchPeeker peeker;
Mike Reedc70e06b2009-04-24 11:09:12 -0400358 JavaPixelAllocator javaAllocator(env);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 SkBitmap* bitmap = new SkBitmap;
360 Res_png_9patch dummy9Patch;
361
362 SkAutoTDelete<SkImageDecoder> add(decoder);
363 SkAutoTDelete<SkBitmap> adb(bitmap);
364
365 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400366 if (!isPurgeable) {
367 decoder->setAllocator(&javaAllocator);
368 }
369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 AutoDecoderCancel adc(options, decoder);
371
Ray Chen0a6a0e92009-04-02 01:31:49 -0700372 // To fix the race condition in case "requestCancelDecode"
373 // happens earlier than AutoDecoderCancel object is added
374 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400375 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
376 return nullObjectReturn("gOptions_mCancelID");;
Ray Chen0a6a0e92009-04-02 01:31:49 -0700377 }
378
Mike Reedc70e06b2009-04-24 11:09:12 -0400379 SkImageDecoder::Mode decodeMode = mode;
380 if (isPurgeable) {
381 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400383 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
384 return nullObjectReturn("decoder->decode returned false");
385 }
386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 // update options (if any)
388 if (NULL != options) {
389 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
390 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
391 // TODO: set the mimeType field with the data from the codec.
392 // but how to reuse a set of strings, rather than allocating new one
393 // each time?
394 env->SetObjectField(options, gOptions_mimeFieldID,
395 getMimeTypeString(env, decoder->getFormat()));
396 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 // if we're in justBounds mode, return now (skip the java bitmap)
399 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
400 return NULL;
401 }
402
403 jbyteArray ninePatchChunk = NULL;
404 if (peeker.fPatchIsValid) {
405 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
406 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
407 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400408 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 }
410 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
411 NULL);
412 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400413 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 }
415 peeker.fPatch->serialize(array);
416 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
417 }
418
419 // detach bitmap from its autotdeleter, since we want to own it now
420 adb.detach();
421
422 if (padding) {
423 if (peeker.fPatchIsValid) {
424 GraphicsJNI::set_jrect(env, padding,
425 peeker.fPatch->paddingLeft,
426 peeker.fPatch->paddingTop,
427 peeker.fPatch->paddingRight,
428 peeker.fPatch->paddingBottom);
429 } else {
430 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
431 }
432 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433
Mike Reedc70e06b2009-04-24 11:09:12 -0400434 SkPixelRef* pr;
435 if (isPurgeable) {
436 pr = installPixelRef(bitmap, stream, sampleSize);
437 } else {
438 // if we get here, we're in kDecodePixels_Mode and will therefore
439 // already have a pixelref installed.
440 pr = bitmap->pixelRef();
441 }
442 // promise we will never change our pixels (great for sharing and pictures)
443 pr->setImmutable();
444 // now create the java bitmap
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
446}
447
448static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
449 jobject is, // InputStream
450 jbyteArray storage, // byte[]
451 jobject padding,
452 jobject options) { // BitmapFactory$Options
453 jobject bitmap = NULL;
454 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
455
456 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400457 // for now we don't allow purgeable with java inputstreams
458 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 stream->unref();
460 }
461 return bitmap;
462}
463
464static ssize_t getFDSize(int fd) {
465 off_t curr = ::lseek(fd, 0, SEEK_CUR);
466 if (curr < 0) {
467 return 0;
468 }
469 size_t size = ::lseek(fd, 0, SEEK_END);
470 ::lseek(fd, curr, SEEK_SET);
471 return size;
472}
473
474/** Restore the file descriptor's offset in our destructor
475 */
476class AutoFDSeek {
477public:
478 AutoFDSeek(int fd) : fFD(fd) {
479 fCurr = ::lseek(fd, 0, SEEK_CUR);
480 }
481 ~AutoFDSeek() {
482 if (fCurr >= 0) {
483 ::lseek(fFD, fCurr, SEEK_SET);
484 }
485 }
486private:
487 int fFD;
488 off_t fCurr;
489};
490
491static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
492 jobject fileDescriptor,
493 jobject padding,
494 jobject bitmapFactoryOptions) {
495 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
496
497 jint descriptor = env->GetIntField(fileDescriptor,
498 gFileDescriptor_descriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400499
500 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
501 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
502 bool weOwnTheFD = false;
503 if (isPurgeable && isShareable) {
504 int newFD = ::dup(descriptor);
505 if (-1 != newFD) {
506 weOwnTheFD = true;
507 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 }
509 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510
Mike Reedc70e06b2009-04-24 11:09:12 -0400511 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
512 SkAutoUnref aur(stream);
513 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 return NULL;
515 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400516
517 /* Restore our offset when we leave, so we can be called more than once
518 with the same descriptor. This is only required if we didn't dup the
519 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 */
521 AutoFDSeek as(descriptor);
522
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700523 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
524 shareable case.
525 */
526 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400527}
528
529/* make a deep copy of the asset, and return it as a stream, or NULL if there
530 was an error.
531 */
532static SkStream* copyAssetToStream(Asset* asset) {
533 // if we could "ref/reopen" the asset, we may not need to copy it here
534 off_t size = asset->seek(0, SEEK_SET);
535 if ((off_t)-1 == size) {
536 SkDebugf("---- copyAsset: asset rewind failed\n");
537 return NULL;
538 }
539
540 size = asset->getLength();
541 if (size <= 0) {
542 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
543 return NULL;
544 }
545
546 SkStream* stream = new SkMemoryStream(size);
547 void* data = const_cast<void*>(stream->getMemoryBase());
548 off_t len = asset->read(data, size);
549 if (len != size) {
550 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
551 delete stream;
552 stream = NULL;
553 }
554 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555}
556
557static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
558 jint native_asset, // Asset
559 jobject padding, // Rect
560 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400561 SkStream* stream;
562 Asset* asset = reinterpret_cast<Asset*>(native_asset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563
Mike Reedc70e06b2009-04-24 11:09:12 -0400564 if (optionsPurgeable(env, options)) {
565 // if we could "ref/reopen" the asset, we may not need to copy it here
566 // and we could assume optionsShareable, since assets are always RO
567 stream = copyAssetToStream(asset);
568 if (NULL == stream) {
569 return NULL;
570 }
571 } else {
572 // since we know we'll be done with the asset when we return, we can
573 // just use a simple wrapper
574 stream = new AssetStreamAdaptor(asset);
575 }
576 SkAutoUnref aur(stream);
577 return doDecode(env, stream, padding, options, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578}
579
580static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
581 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400582 /* If optionsShareable() we could decide to just wrap the java array and
583 share it, but that means adding a globalref to the java array object
584 and managing its lifetime. For now we just always copy the array's data
585 if optionsPurgeable().
586 */
587 AutoJavaByteArray ar(env, byteArray);
588 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
589 optionsPurgeable(env, options));
590 SkAutoUnref aur(stream);
591 return doDecode(env, stream, NULL, options, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592}
593
594static void nativeRequestCancel(JNIEnv*, jobject joptions) {
595 (void)AutoDecoderCancel::RequestCancel(joptions);
596}
597
598static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
599 jobject padding) {
600
601 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
602 if (array != NULL) {
603 size_t chunkSize = env->GetArrayLength(chunkObject);
604 void* storage = alloca(chunkSize);
605 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
606 memcpy(chunk, array, chunkSize);
607 android::Res_png_9patch::deserialize(chunk);
608
609 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
610 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
611 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
612 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
613
614 for (int i = 0; i < chunk->numXDivs; i++) {
615 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
616 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
617 chunk->xDivs[i]++;
618 }
619 }
620
621 for (int i = 0; i < chunk->numYDivs; i++) {
622 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
623 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
624 chunk->yDivs[i]++;
625 }
626 }
627
628 memcpy(array, chunk, chunkSize);
629
630 if (padding) {
631 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
632 chunk->paddingRight, chunk->paddingBottom);
633 }
634
635 env->ReleaseByteArrayElements(chunkObject, array, 0);
636 }
637 return chunkObject;
638}
639
640///////////////////////////////////////////////////////////////////////////////
641
642static JNINativeMethod gMethods[] = {
643 { "nativeDecodeStream",
644 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
645 (void*)nativeDecodeStream
646 },
647
648 { "nativeDecodeFileDescriptor",
649 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
650 (void*)nativeDecodeFileDescriptor
651 },
652
653 { "nativeDecodeAsset",
654 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
655 (void*)nativeDecodeAsset
656 },
657
658 { "nativeDecodeByteArray",
659 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
660 (void*)nativeDecodeByteArray
661 },
662
663 { "nativeScaleNinePatch",
664 "([BFLandroid/graphics/Rect;)[B",
665 (void*)nativeScaleNinePatch
666 }
667
668};
669
670static JNINativeMethod gOptionsMethods[] = {
671 { "requestCancel", "()V", (void*)nativeRequestCancel }
672};
673
674static jclass make_globalref(JNIEnv* env, const char classname[]) {
675 jclass c = env->FindClass(classname);
676 SkASSERT(c);
677 return (jclass)env->NewGlobalRef(c);
678}
679
680static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
681 const char fieldname[], const char type[]) {
682 jfieldID id = env->GetFieldID(clazz, fieldname, type);
683 SkASSERT(id);
684 return id;
685}
686
687#define kClassPathName "android/graphics/BitmapFactory"
688
689#define RETURN_ERR_IF_NULL(value) \
690 do { if (!(value)) { assert(0); return -1; } } while (false)
691
692int register_android_graphics_BitmapFactory(JNIEnv* env);
693int register_android_graphics_BitmapFactory(JNIEnv* env) {
694 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
695 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
696 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
697 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
698 "Landroid/graphics/Bitmap$Config;");
699 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
Mike Reedc70e06b2009-04-24 11:09:12 -0400700 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
701 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
703 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
704 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700705 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706
707 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
708 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
709
710 int ret = AndroidRuntime::registerNativeMethods(env,
711 "android/graphics/BitmapFactory$Options",
712 gOptionsMethods,
713 SK_ARRAY_COUNT(gOptionsMethods));
714 if (ret) {
715 return ret;
716 }
717 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
718 gMethods, SK_ARRAY_COUNT(gMethods));
719}