blob: 0c842650962fb0480224a1fb30d34cdbc523a3d1 [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 {
167public:
168 NinePatchPeeker() {
169 fPatchIsValid = false;
170 }
171
172 ~NinePatchPeeker() {
173 if (fPatchIsValid) {
174 free(fPatch);
175 }
176 }
177
178 bool fPatchIsValid;
179 Res_png_9patch* fPatch;
180
181 virtual bool peek(const char tag[], const void* data, size_t length) {
182 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
183 Res_png_9patch* patch = (Res_png_9patch*) data;
184 size_t patchSize = patch->serializedSize();
185 assert(length == patchSize);
186 // You have to copy the data because it is owned by the png reader
187 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
188 memcpy(patchNew, patch, patchSize);
189 // this relies on deserialization being done in place
190 Res_png_9patch::deserialize(patchNew);
191 patchNew->fileToDevice();
192 if (fPatchIsValid) {
193 free(fPatch);
194 }
195 fPatch = patchNew;
196 //printf("9patch: (%d,%d)-(%d,%d)\n",
197 // fPatch.sizeLeft, fPatch.sizeTop,
198 // fPatch.sizeRight, fPatch.sizeBottom);
199 fPatchIsValid = true;
200 } else {
201 fPatch = NULL;
202 }
203 return true; // keep on decoding
204 }
205};
206
207class AssetStreamAdaptor : public SkStream {
208public:
209 AssetStreamAdaptor(Asset* a) : fAsset(a) {}
210
Ray Chen0a6a0e92009-04-02 01:31:49 -0700211 virtual bool rewind() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 off_t pos = fAsset->seek(0, SEEK_SET);
Mike Reedc70e06b2009-04-24 11:09:12 -0400213 if (pos == (off_t)-1) {
214 SkDebugf("----- fAsset->seek(rewind) failed\n");
215 return false;
216 }
217 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
219
Ray Chen0a6a0e92009-04-02 01:31:49 -0700220 virtual size_t read(void* buffer, size_t size) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 ssize_t amount;
222
223 if (NULL == buffer) {
224 if (0 == size) { // caller is asking us for our total length
225 return fAsset->getLength();
226 }
227 // asset->seek returns new total offset
228 // we want to return amount that was skipped
229
230 off_t oldOffset = fAsset->seek(0, SEEK_CUR);
231 if (-1 == oldOffset) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400232 SkDebugf("---- fAsset->seek(oldOffset) failed\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 return 0;
234 }
235 off_t newOffset = fAsset->seek(size, SEEK_CUR);
236 if (-1 == newOffset) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400237 SkDebugf("---- fAsset->seek(%d) failed\n", size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 return 0;
239 }
240 amount = newOffset - oldOffset;
241 } else {
242 amount = fAsset->read(buffer, size);
Mike Reedc70e06b2009-04-24 11:09:12 -0400243 if (amount <= 0) {
244 SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
245 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 }
247
248 if (amount < 0) {
249 amount = 0;
250 }
251 return amount;
252 }
253
254private:
255 Asset* fAsset;
256};
257
258///////////////////////////////////////////////////////////////////////////////
259
260static inline int32_t validOrNeg1(bool isValid, int32_t value) {
261// return isValid ? value : -1;
262 SkASSERT((int)isValid == 0 || (int)isValid == 1);
263 return ((int32_t)isValid - 1) | value;
264}
265
266static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
267 static const struct {
268 SkImageDecoder::Format fFormat;
269 const char* fMimeType;
270 } gMimeTypes[] = {
271 { SkImageDecoder::kBMP_Format, "image/bmp" },
272 { SkImageDecoder::kGIF_Format, "image/gif" },
273 { SkImageDecoder::kICO_Format, "image/x-ico" },
274 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
275 { SkImageDecoder::kPNG_Format, "image/png" },
276 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
277 };
278
279 const char* cstr = NULL;
280 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
281 if (gMimeTypes[i].fFormat == format) {
282 cstr = gMimeTypes[i].fMimeType;
283 break;
284 }
285 }
286
287 jstring jstr = 0;
288 if (NULL != cstr) {
289 jstr = env->NewStringUTF(cstr);
290 }
291 return jstr;
292}
293
Mike Reedc70e06b2009-04-24 11:09:12 -0400294static bool optionsPurgeable(JNIEnv* env, jobject options) {
295 return options != NULL &&
296 env->GetBooleanField(options, gOptions_purgeableFieldID);
297}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298
Mike Reedc70e06b2009-04-24 11:09:12 -0400299static bool optionsShareable(JNIEnv* env, jobject options) {
300 return options != NULL &&
301 env->GetBooleanField(options, gOptions_shareableFieldID);
302}
303
Mike Reed1b22b972009-07-17 11:21:47 -0400304static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
305 return NULL == options ||
306 !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
307}
308
Mike Reedc70e06b2009-04-24 11:09:12 -0400309static jobject nullObjectReturn(const char msg[]) {
310 if (msg) {
311 SkDebugf("--- %s\n", msg);
312 }
313 return NULL;
314}
315
316static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
317 int sampleSize) {
318 SkPixelRef* pr;
319 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700320 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400321 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
322 } else {
323 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
324 }
325 bitmap->setPixelRef(pr)->unref();
326 return pr;
327}
328
329// since we "may" create a purgeable imageref, we require the stream be ref'able
330// i.e. dynamically allocated, since its lifetime may exceed the current stack
331// frame.
332static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
333 jobject options, bool allowPurgeable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 int sampleSize = 1;
335 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
336 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
337 bool doDither = true;
Mike Reedc70e06b2009-04-24 11:09:12 -0400338 bool isPurgeable = allowPurgeable && optionsPurgeable(env, options);
Mike Reed1b22b972009-07-17 11:21:47 -0400339 bool reportSizeToVM = optionsReportSizeToVM(env, options);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340
341 if (NULL != options) {
342 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
343 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
344 mode = SkImageDecoder::kDecodeBounds_Mode;
345 }
346 // initialize these, in case we fail later on
347 env->SetIntField(options, gOptions_widthFieldID, -1);
348 env->SetIntField(options, gOptions_heightFieldID, -1);
349 env->SetObjectField(options, gOptions_mimeFieldID, 0);
350
351 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
352 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
353 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
354 }
355
356 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
357 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400358 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 }
360
361 decoder->setSampleSize(sampleSize);
362 decoder->setDitherImage(doDither);
363
364 NinePatchPeeker peeker;
Mike Reed1b22b972009-07-17 11:21:47 -0400365 JavaPixelAllocator javaAllocator(env, reportSizeToVM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 SkBitmap* bitmap = new SkBitmap;
367 Res_png_9patch dummy9Patch;
368
369 SkAutoTDelete<SkImageDecoder> add(decoder);
370 SkAutoTDelete<SkBitmap> adb(bitmap);
371
372 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400373 if (!isPurgeable) {
374 decoder->setAllocator(&javaAllocator);
375 }
376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 AutoDecoderCancel adc(options, decoder);
378
Ray Chen0a6a0e92009-04-02 01:31:49 -0700379 // To fix the race condition in case "requestCancelDecode"
380 // happens earlier than AutoDecoderCancel object is added
381 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400382 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
383 return nullObjectReturn("gOptions_mCancelID");;
Ray Chen0a6a0e92009-04-02 01:31:49 -0700384 }
385
Mike Reedc70e06b2009-04-24 11:09:12 -0400386 SkImageDecoder::Mode decodeMode = mode;
387 if (isPurgeable) {
388 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400390 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
391 return nullObjectReturn("decoder->decode returned false");
392 }
393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 // update options (if any)
395 if (NULL != options) {
396 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
397 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
398 // TODO: set the mimeType field with the data from the codec.
399 // but how to reuse a set of strings, rather than allocating new one
400 // each time?
401 env->SetObjectField(options, gOptions_mimeFieldID,
402 getMimeTypeString(env, decoder->getFormat()));
403 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 // if we're in justBounds mode, return now (skip the java bitmap)
406 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
407 return NULL;
408 }
409
410 jbyteArray ninePatchChunk = NULL;
411 if (peeker.fPatchIsValid) {
412 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
413 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
414 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400415 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 }
417 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
418 NULL);
419 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400420 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 }
422 peeker.fPatch->serialize(array);
423 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
424 }
425
426 // detach bitmap from its autotdeleter, since we want to own it now
427 adb.detach();
428
429 if (padding) {
430 if (peeker.fPatchIsValid) {
431 GraphicsJNI::set_jrect(env, padding,
432 peeker.fPatch->paddingLeft,
433 peeker.fPatch->paddingTop,
434 peeker.fPatch->paddingRight,
435 peeker.fPatch->paddingBottom);
436 } else {
437 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
438 }
439 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440
Mike Reedc70e06b2009-04-24 11:09:12 -0400441 SkPixelRef* pr;
442 if (isPurgeable) {
443 pr = installPixelRef(bitmap, stream, sampleSize);
444 } else {
445 // if we get here, we're in kDecodePixels_Mode and will therefore
446 // already have a pixelref installed.
447 pr = bitmap->pixelRef();
448 }
449 // promise we will never change our pixels (great for sharing and pictures)
450 pr->setImmutable();
451 // now create the java bitmap
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
453}
454
455static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
456 jobject is, // InputStream
457 jbyteArray storage, // byte[]
458 jobject padding,
459 jobject options) { // BitmapFactory$Options
460 jobject bitmap = NULL;
461 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
462
463 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400464 // for now we don't allow purgeable with java inputstreams
465 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 stream->unref();
467 }
468 return bitmap;
469}
470
471static ssize_t getFDSize(int fd) {
472 off_t curr = ::lseek(fd, 0, SEEK_CUR);
473 if (curr < 0) {
474 return 0;
475 }
476 size_t size = ::lseek(fd, 0, SEEK_END);
477 ::lseek(fd, curr, SEEK_SET);
478 return size;
479}
480
481/** Restore the file descriptor's offset in our destructor
482 */
483class AutoFDSeek {
484public:
485 AutoFDSeek(int fd) : fFD(fd) {
486 fCurr = ::lseek(fd, 0, SEEK_CUR);
487 }
488 ~AutoFDSeek() {
489 if (fCurr >= 0) {
490 ::lseek(fFD, fCurr, SEEK_SET);
491 }
492 }
493private:
494 int fFD;
495 off_t fCurr;
496};
497
498static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
499 jobject fileDescriptor,
500 jobject padding,
501 jobject bitmapFactoryOptions) {
502 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
503
504 jint descriptor = env->GetIntField(fileDescriptor,
505 gFileDescriptor_descriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400506
507 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
508 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
509 bool weOwnTheFD = false;
510 if (isPurgeable && isShareable) {
511 int newFD = ::dup(descriptor);
512 if (-1 != newFD) {
513 weOwnTheFD = true;
514 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 }
516 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517
Mike Reedc70e06b2009-04-24 11:09:12 -0400518 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
519 SkAutoUnref aur(stream);
520 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 return NULL;
522 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400523
524 /* Restore our offset when we leave, so we can be called more than once
525 with the same descriptor. This is only required if we didn't dup the
526 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 */
528 AutoFDSeek as(descriptor);
529
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700530 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
531 shareable case.
532 */
533 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400534}
535
536/* make a deep copy of the asset, and return it as a stream, or NULL if there
537 was an error.
538 */
539static SkStream* copyAssetToStream(Asset* asset) {
540 // if we could "ref/reopen" the asset, we may not need to copy it here
541 off_t size = asset->seek(0, SEEK_SET);
542 if ((off_t)-1 == size) {
543 SkDebugf("---- copyAsset: asset rewind failed\n");
544 return NULL;
545 }
546
547 size = asset->getLength();
548 if (size <= 0) {
549 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
550 return NULL;
551 }
552
553 SkStream* stream = new SkMemoryStream(size);
554 void* data = const_cast<void*>(stream->getMemoryBase());
555 off_t len = asset->read(data, size);
556 if (len != size) {
557 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
558 delete stream;
559 stream = NULL;
560 }
561 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562}
563
564static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
565 jint native_asset, // Asset
566 jobject padding, // Rect
567 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400568 SkStream* stream;
569 Asset* asset = reinterpret_cast<Asset*>(native_asset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570
Mike Reedc70e06b2009-04-24 11:09:12 -0400571 if (optionsPurgeable(env, options)) {
572 // if we could "ref/reopen" the asset, we may not need to copy it here
573 // and we could assume optionsShareable, since assets are always RO
574 stream = copyAssetToStream(asset);
575 if (NULL == stream) {
576 return NULL;
577 }
578 } else {
579 // since we know we'll be done with the asset when we return, we can
580 // just use a simple wrapper
581 stream = new AssetStreamAdaptor(asset);
582 }
583 SkAutoUnref aur(stream);
584 return doDecode(env, stream, padding, options, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585}
586
587static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
588 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400589 /* If optionsShareable() we could decide to just wrap the java array and
590 share it, but that means adding a globalref to the java array object
591 and managing its lifetime. For now we just always copy the array's data
592 if optionsPurgeable().
593 */
594 AutoJavaByteArray ar(env, byteArray);
595 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
596 optionsPurgeable(env, options));
597 SkAutoUnref aur(stream);
598 return doDecode(env, stream, NULL, options, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599}
600
601static void nativeRequestCancel(JNIEnv*, jobject joptions) {
602 (void)AutoDecoderCancel::RequestCancel(joptions);
603}
604
605static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
606 jobject padding) {
607
608 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
609 if (array != NULL) {
610 size_t chunkSize = env->GetArrayLength(chunkObject);
611 void* storage = alloca(chunkSize);
612 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
613 memcpy(chunk, array, chunkSize);
614 android::Res_png_9patch::deserialize(chunk);
615
616 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
617 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
618 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
619 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
620
621 for (int i = 0; i < chunk->numXDivs; i++) {
622 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
623 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
624 chunk->xDivs[i]++;
625 }
626 }
627
628 for (int i = 0; i < chunk->numYDivs; i++) {
629 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
630 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
631 chunk->yDivs[i]++;
632 }
633 }
634
635 memcpy(array, chunk, chunkSize);
636
637 if (padding) {
638 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
639 chunk->paddingRight, chunk->paddingBottom);
640 }
641
642 env->ReleaseByteArrayElements(chunkObject, array, 0);
643 }
644 return chunkObject;
645}
646
647///////////////////////////////////////////////////////////////////////////////
648
649static JNINativeMethod gMethods[] = {
650 { "nativeDecodeStream",
651 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
652 (void*)nativeDecodeStream
653 },
654
655 { "nativeDecodeFileDescriptor",
656 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
657 (void*)nativeDecodeFileDescriptor
658 },
659
660 { "nativeDecodeAsset",
661 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
662 (void*)nativeDecodeAsset
663 },
664
665 { "nativeDecodeByteArray",
666 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
667 (void*)nativeDecodeByteArray
668 },
669
670 { "nativeScaleNinePatch",
671 "([BFLandroid/graphics/Rect;)[B",
672 (void*)nativeScaleNinePatch
673 }
674
675};
676
677static JNINativeMethod gOptionsMethods[] = {
678 { "requestCancel", "()V", (void*)nativeRequestCancel }
679};
680
681static jclass make_globalref(JNIEnv* env, const char classname[]) {
682 jclass c = env->FindClass(classname);
683 SkASSERT(c);
684 return (jclass)env->NewGlobalRef(c);
685}
686
687static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
688 const char fieldname[], const char type[]) {
689 jfieldID id = env->GetFieldID(clazz, fieldname, type);
690 SkASSERT(id);
691 return id;
692}
693
694#define kClassPathName "android/graphics/BitmapFactory"
695
696#define RETURN_ERR_IF_NULL(value) \
697 do { if (!(value)) { assert(0); return -1; } } while (false)
698
699int register_android_graphics_BitmapFactory(JNIEnv* env);
700int register_android_graphics_BitmapFactory(JNIEnv* env) {
701 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
702 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
703 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
704 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
705 "Landroid/graphics/Bitmap$Config;");
706 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
Mike Reedc70e06b2009-04-24 11:09:12 -0400707 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
708 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
Mike Reed1b22b972009-07-17 11:21:47 -0400709 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
711 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
712 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700713 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714
715 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
716 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
717
718 int ret = AndroidRuntime::registerNativeMethods(env,
719 "android/graphics/BitmapFactory$Options",
720 gOptionsMethods,
721 SK_ARRAY_COUNT(gOptionsMethods));
722 if (ret) {
723 return ret;
724 }
725 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
726 gMethods, SK_ARRAY_COUNT(gMethods));
727}