• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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