1 #define LOG_TAG "BitmapFactory"
2
3 #include "SkImageDecoder.h"
4 #include "SkImageRef_ashmem.h"
5 #include "SkImageRef_GlobalPool.h"
6 #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
19 static jclass gOptions_class;
20 static jfieldID gOptions_justBoundsFieldID;
21 static jfieldID gOptions_sampleSizeFieldID;
22 static jfieldID gOptions_configFieldID;
23 static jfieldID gOptions_ditherFieldID;
24 static jfieldID gOptions_purgeableFieldID;
25 static jfieldID gOptions_shareableFieldID;
26 static jfieldID gOptions_nativeAllocFieldID;
27 static jfieldID gOptions_widthFieldID;
28 static jfieldID gOptions_heightFieldID;
29 static jfieldID gOptions_mimeFieldID;
30 static jfieldID gOptions_mCancelID;
31
32 static jclass gFileDescriptor_class;
33 static jfieldID gFileDescriptor_descriptor;
34
35 #if 0
36 #define TRACE_BITMAP(code) code
37 #else
38 #define TRACE_BITMAP(code)
39 #endif
40
41 ///////////////////////////////////////////////////////////////////////////////
42
43 class AutoDecoderCancel {
44 public:
45 AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
46 ~AutoDecoderCancel();
47
48 static bool RequestCancel(jobject options);
49
50 private:
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
Validate()59 static void Validate() {}
60 #endif
61 };
62
63 static SkMutex gAutoDecoderCancelMutex;
64 static AutoDecoderCancel* gAutoDecoderCancel;
65 #ifdef SK_DEBUG
66 static int gAutoDecoderCancelCount;
67 #endif
68
AutoDecoderCancel(jobject joptions,SkImageDecoder * decoder)69 AutoDecoderCancel::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
~AutoDecoderCancel()90 AutoDecoderCancel::~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
RequestCancel(jobject joptions)115 bool 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
Validate()133 void 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
164 using namespace android;
165
166 class NinePatchPeeker : public SkImageDecoder::Peeker {
167 public:
NinePatchPeeker()168 NinePatchPeeker() {
169 fPatchIsValid = false;
170 }
171
~NinePatchPeeker()172 ~NinePatchPeeker() {
173 if (fPatchIsValid) {
174 free(fPatch);
175 }
176 }
177
178 bool fPatchIsValid;
179 Res_png_9patch* fPatch;
180
peek(const char tag[],const void * data,size_t length)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
207 class AssetStreamAdaptor : public SkStream {
208 public:
AssetStreamAdaptor(Asset * a)209 AssetStreamAdaptor(Asset* a) : fAsset(a) {}
210
rewind()211 virtual bool rewind() {
212 off_t pos = fAsset->seek(0, SEEK_SET);
213 if (pos == (off_t)-1) {
214 SkDebugf("----- fAsset->seek(rewind) failed\n");
215 return false;
216 }
217 return true;
218 }
219
read(void * buffer,size_t size)220 virtual size_t read(void* buffer, size_t size) {
221 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) {
232 SkDebugf("---- fAsset->seek(oldOffset) failed\n");
233 return 0;
234 }
235 off_t newOffset = fAsset->seek(size, SEEK_CUR);
236 if (-1 == newOffset) {
237 SkDebugf("---- fAsset->seek(%d) failed\n", size);
238 return 0;
239 }
240 amount = newOffset - oldOffset;
241 } else {
242 amount = fAsset->read(buffer, size);
243 if (amount <= 0) {
244 SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
245 }
246 }
247
248 if (amount < 0) {
249 amount = 0;
250 }
251 return amount;
252 }
253
254 private:
255 Asset* fAsset;
256 };
257
258 ///////////////////////////////////////////////////////////////////////////////
259
validOrNeg1(bool isValid,int32_t value)260 static 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
getMimeTypeString(JNIEnv * env,SkImageDecoder::Format format)266 static 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
optionsPurgeable(JNIEnv * env,jobject options)294 static bool optionsPurgeable(JNIEnv* env, jobject options) {
295 return options != NULL &&
296 env->GetBooleanField(options, gOptions_purgeableFieldID);
297 }
298
optionsShareable(JNIEnv * env,jobject options)299 static bool optionsShareable(JNIEnv* env, jobject options) {
300 return options != NULL &&
301 env->GetBooleanField(options, gOptions_shareableFieldID);
302 }
303
optionsReportSizeToVM(JNIEnv * env,jobject options)304 static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
305 return NULL == options ||
306 !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
307 }
308
nullObjectReturn(const char msg[])309 static jobject nullObjectReturn(const char msg[]) {
310 if (msg) {
311 SkDebugf("--- %s\n", msg);
312 }
313 return NULL;
314 }
315
installPixelRef(SkBitmap * bitmap,SkStream * stream,int sampleSize,bool ditherImage)316 static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
317 int sampleSize, bool ditherImage) {
318 SkImageRef* pr;
319 // only use ashmem for large images, since mmaps come at a price
320 if (bitmap->getSize() >= 32 * 1024) {
321 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
322 } else {
323 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
324 }
325 pr->setDitherImage(ditherImage);
326 bitmap->setPixelRef(pr)->unref();
327 return pr;
328 }
329
330 // since we "may" create a purgeable imageref, we require the stream be ref'able
331 // i.e. dynamically allocated, since its lifetime may exceed the current stack
332 // frame.
doDecode(JNIEnv * env,SkStream * stream,jobject padding,jobject options,bool allowPurgeable)333 static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
334 jobject options, bool allowPurgeable) {
335 int sampleSize = 1;
336 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
337 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
338 bool doDither = true;
339 bool isPurgeable = allowPurgeable && optionsPurgeable(env, options);
340 bool reportSizeToVM = optionsReportSizeToVM(env, options);
341
342 if (NULL != options) {
343 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
344 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
345 mode = SkImageDecoder::kDecodeBounds_Mode;
346 }
347 // initialize these, in case we fail later on
348 env->SetIntField(options, gOptions_widthFieldID, -1);
349 env->SetIntField(options, gOptions_heightFieldID, -1);
350 env->SetObjectField(options, gOptions_mimeFieldID, 0);
351
352 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
353 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
354 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
355 }
356
357 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
358 if (NULL == decoder) {
359 return nullObjectReturn("SkImageDecoder::Factory returned null");
360 }
361
362 decoder->setSampleSize(sampleSize);
363 decoder->setDitherImage(doDither);
364
365 NinePatchPeeker peeker;
366 JavaPixelAllocator javaAllocator(env, reportSizeToVM);
367 SkBitmap* bitmap = new SkBitmap;
368 Res_png_9patch dummy9Patch;
369
370 SkAutoTDelete<SkImageDecoder> add(decoder);
371 SkAutoTDelete<SkBitmap> adb(bitmap);
372
373 decoder->setPeeker(&peeker);
374 if (!isPurgeable) {
375 decoder->setAllocator(&javaAllocator);
376 }
377
378 AutoDecoderCancel adc(options, decoder);
379
380 // To fix the race condition in case "requestCancelDecode"
381 // happens earlier than AutoDecoderCancel object is added
382 // to the gAutoDecoderCancelMutex linked list.
383 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
384 return nullObjectReturn("gOptions_mCancelID");;
385 }
386
387 SkImageDecoder::Mode decodeMode = mode;
388 if (isPurgeable) {
389 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
390 }
391 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
392 return nullObjectReturn("decoder->decode returned false");
393 }
394
395 // update options (if any)
396 if (NULL != options) {
397 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
398 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
399 // TODO: set the mimeType field with the data from the codec.
400 // but how to reuse a set of strings, rather than allocating new one
401 // each time?
402 env->SetObjectField(options, gOptions_mimeFieldID,
403 getMimeTypeString(env, decoder->getFormat()));
404 }
405
406 // if we're in justBounds mode, return now (skip the java bitmap)
407 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
408 return NULL;
409 }
410
411 jbyteArray ninePatchChunk = NULL;
412 if (peeker.fPatchIsValid) {
413 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
414 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
415 if (NULL == ninePatchChunk) {
416 return nullObjectReturn("ninePatchChunk == null");
417 }
418 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
419 NULL);
420 if (NULL == array) {
421 return nullObjectReturn("primitive array == null");
422 }
423 peeker.fPatch->serialize(array);
424 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
425 }
426
427 // detach bitmap from its autotdeleter, since we want to own it now
428 adb.detach();
429
430 if (padding) {
431 if (peeker.fPatchIsValid) {
432 GraphicsJNI::set_jrect(env, padding,
433 peeker.fPatch->paddingLeft,
434 peeker.fPatch->paddingTop,
435 peeker.fPatch->paddingRight,
436 peeker.fPatch->paddingBottom);
437 } else {
438 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
439 }
440 }
441
442 SkPixelRef* pr;
443 if (isPurgeable) {
444 pr = installPixelRef(bitmap, stream, sampleSize, doDither);
445 } else {
446 // if we get here, we're in kDecodePixels_Mode and will therefore
447 // already have a pixelref installed.
448 pr = bitmap->pixelRef();
449 }
450 // promise we will never change our pixels (great for sharing and pictures)
451 pr->setImmutable();
452 // now create the java bitmap
453 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
454 }
455
nativeDecodeStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage,jobject padding,jobject options)456 static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
457 jobject is, // InputStream
458 jbyteArray storage, // byte[]
459 jobject padding,
460 jobject options) { // BitmapFactory$Options
461 jobject bitmap = NULL;
462 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
463
464 if (stream) {
465 // for now we don't allow purgeable with java inputstreams
466 bitmap = doDecode(env, stream, padding, options, false);
467 stream->unref();
468 }
469 return bitmap;
470 }
471
getFDSize(int fd)472 static ssize_t getFDSize(int fd) {
473 off_t curr = ::lseek(fd, 0, SEEK_CUR);
474 if (curr < 0) {
475 return 0;
476 }
477 size_t size = ::lseek(fd, 0, SEEK_END);
478 ::lseek(fd, curr, SEEK_SET);
479 return size;
480 }
481
482 /** Restore the file descriptor's offset in our destructor
483 */
484 class AutoFDSeek {
485 public:
AutoFDSeek(int fd)486 AutoFDSeek(int fd) : fFD(fd) {
487 fCurr = ::lseek(fd, 0, SEEK_CUR);
488 }
~AutoFDSeek()489 ~AutoFDSeek() {
490 if (fCurr >= 0) {
491 ::lseek(fFD, fCurr, SEEK_SET);
492 }
493 }
494 private:
495 int fFD;
496 off_t fCurr;
497 };
498
nativeDecodeFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor,jobject padding,jobject bitmapFactoryOptions)499 static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
500 jobject fileDescriptor,
501 jobject padding,
502 jobject bitmapFactoryOptions) {
503 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
504
505 jint descriptor = env->GetIntField(fileDescriptor,
506 gFileDescriptor_descriptor);
507
508 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
509 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
510 bool weOwnTheFD = false;
511 if (isPurgeable && isShareable) {
512 int newFD = ::dup(descriptor);
513 if (-1 != newFD) {
514 weOwnTheFD = true;
515 descriptor = newFD;
516 }
517 }
518
519 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
520 SkAutoUnref aur(stream);
521 if (!stream->isValid()) {
522 return NULL;
523 }
524
525 /* Restore our offset when we leave, so we can be called more than once
526 with the same descriptor. This is only required if we didn't dup the
527 file descriptor, but it is OK to do it all the time.
528 */
529 AutoFDSeek as(descriptor);
530
531 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
532 shareable case.
533 */
534 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
535 }
536
537 /* make a deep copy of the asset, and return it as a stream, or NULL if there
538 was an error.
539 */
copyAssetToStream(Asset * asset)540 static SkStream* copyAssetToStream(Asset* asset) {
541 // if we could "ref/reopen" the asset, we may not need to copy it here
542 off_t size = asset->seek(0, SEEK_SET);
543 if ((off_t)-1 == size) {
544 SkDebugf("---- copyAsset: asset rewind failed\n");
545 return NULL;
546 }
547
548 size = asset->getLength();
549 if (size <= 0) {
550 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
551 return NULL;
552 }
553
554 SkStream* stream = new SkMemoryStream(size);
555 void* data = const_cast<void*>(stream->getMemoryBase());
556 off_t len = asset->read(data, size);
557 if (len != size) {
558 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
559 delete stream;
560 stream = NULL;
561 }
562 return stream;
563 }
564
nativeDecodeAsset(JNIEnv * env,jobject clazz,jint native_asset,jobject padding,jobject options)565 static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
566 jint native_asset, // Asset
567 jobject padding, // Rect
568 jobject options) { // BitmapFactory$Options
569 SkStream* stream;
570 Asset* asset = reinterpret_cast<Asset*>(native_asset);
571
572 if (optionsPurgeable(env, options)) {
573 // if we could "ref/reopen" the asset, we may not need to copy it here
574 // and we could assume optionsShareable, since assets are always RO
575 stream = copyAssetToStream(asset);
576 if (NULL == stream) {
577 return NULL;
578 }
579 } else {
580 // since we know we'll be done with the asset when we return, we can
581 // just use a simple wrapper
582 stream = new AssetStreamAdaptor(asset);
583 }
584 SkAutoUnref aur(stream);
585 return doDecode(env, stream, padding, options, true);
586 }
587
nativeDecodeByteArray(JNIEnv * env,jobject,jbyteArray byteArray,int offset,int length,jobject options)588 static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
589 int offset, int length, jobject options) {
590 /* If optionsShareable() we could decide to just wrap the java array and
591 share it, but that means adding a globalref to the java array object
592 and managing its lifetime. For now we just always copy the array's data
593 if optionsPurgeable().
594 */
595 AutoJavaByteArray ar(env, byteArray);
596 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
597 optionsPurgeable(env, options));
598 SkAutoUnref aur(stream);
599 return doDecode(env, stream, NULL, options, true);
600 }
601
nativeRequestCancel(JNIEnv *,jobject joptions)602 static void nativeRequestCancel(JNIEnv*, jobject joptions) {
603 (void)AutoDecoderCancel::RequestCancel(joptions);
604 }
605
nativeScaleNinePatch(JNIEnv * env,jobject,jbyteArray chunkObject,jfloat scale,jobject padding)606 static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
607 jobject padding) {
608
609 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
610 if (array != NULL) {
611 size_t chunkSize = env->GetArrayLength(chunkObject);
612 void* storage = alloca(chunkSize);
613 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
614 memcpy(chunk, array, chunkSize);
615 android::Res_png_9patch::deserialize(chunk);
616
617 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
618 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
619 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
620 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
621
622 for (int i = 0; i < chunk->numXDivs; i++) {
623 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
624 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
625 chunk->xDivs[i]++;
626 }
627 }
628
629 for (int i = 0; i < chunk->numYDivs; i++) {
630 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
631 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
632 chunk->yDivs[i]++;
633 }
634 }
635
636 memcpy(array, chunk, chunkSize);
637
638 if (padding) {
639 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
640 chunk->paddingRight, chunk->paddingBottom);
641 }
642
643 env->ReleaseByteArrayElements(chunkObject, array, 0);
644 }
645 return chunkObject;
646 }
647
648 ///////////////////////////////////////////////////////////////////////////////
649
650 static JNINativeMethod gMethods[] = {
651 { "nativeDecodeStream",
652 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
653 (void*)nativeDecodeStream
654 },
655
656 { "nativeDecodeFileDescriptor",
657 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
658 (void*)nativeDecodeFileDescriptor
659 },
660
661 { "nativeDecodeAsset",
662 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
663 (void*)nativeDecodeAsset
664 },
665
666 { "nativeDecodeByteArray",
667 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
668 (void*)nativeDecodeByteArray
669 },
670
671 { "nativeScaleNinePatch",
672 "([BFLandroid/graphics/Rect;)[B",
673 (void*)nativeScaleNinePatch
674 }
675
676 };
677
678 static JNINativeMethod gOptionsMethods[] = {
679 { "requestCancel", "()V", (void*)nativeRequestCancel }
680 };
681
make_globalref(JNIEnv * env,const char classname[])682 static jclass make_globalref(JNIEnv* env, const char classname[]) {
683 jclass c = env->FindClass(classname);
684 SkASSERT(c);
685 return (jclass)env->NewGlobalRef(c);
686 }
687
getFieldIDCheck(JNIEnv * env,jclass clazz,const char fieldname[],const char type[])688 static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
689 const char fieldname[], const char type[]) {
690 jfieldID id = env->GetFieldID(clazz, fieldname, type);
691 SkASSERT(id);
692 return id;
693 }
694
695 #define kClassPathName "android/graphics/BitmapFactory"
696
697 #define RETURN_ERR_IF_NULL(value) \
698 do { if (!(value)) { assert(0); return -1; } } while (false)
699
700 int register_android_graphics_BitmapFactory(JNIEnv* env);
register_android_graphics_BitmapFactory(JNIEnv * env)701 int register_android_graphics_BitmapFactory(JNIEnv* env) {
702 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
703 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
704 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
705 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
706 "Landroid/graphics/Bitmap$Config;");
707 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
708 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
709 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
710 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
711 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
712 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
713 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
714 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
715
716 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
717 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
718
719 int ret = AndroidRuntime::registerNativeMethods(env,
720 "android/graphics/BitmapFactory$Options",
721 gOptionsMethods,
722 SK_ARRAY_COUNT(gOptionsMethods));
723 if (ret) {
724 return ret;
725 }
726 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
727 gMethods, SK_ARRAY_COUNT(gMethods));
728 }
729