1 #undef LOG_TAG
2 #define LOG_TAG "BitmapFactory"
3 
4 #include "BitmapFactory.h"
5 #include "CreateJavaOutputStreamAdaptor.h"
6 #include "GraphicsJNI.h"
7 #include "MimeType.h"
8 #include "NinePatchPeeker.h"
9 #include "SkAndroidCodec.h"
10 #include "SkBRDAllocator.h"
11 #include "SkFrontBufferedStream.h"
12 #include "SkMath.h"
13 #include "SkPixelRef.h"
14 #include "SkStream.h"
15 #include "SkUtils.h"
16 #include "Utils.h"
17 
18 #include <HardwareBitmapUploader.h>
19 #include <nativehelper/JNIHelp.h>
20 #include <androidfw/Asset.h>
21 #include <androidfw/ResourceTypes.h>
22 #include <cutils/compiler.h>
23 #include <fcntl.h>
24 #include <memory>
25 #include <stdio.h>
26 #include <sys/stat.h>
27 
28 jfieldID gOptions_justBoundsFieldID;
29 jfieldID gOptions_sampleSizeFieldID;
30 jfieldID gOptions_configFieldID;
31 jfieldID gOptions_colorSpaceFieldID;
32 jfieldID gOptions_premultipliedFieldID;
33 jfieldID gOptions_mutableFieldID;
34 jfieldID gOptions_ditherFieldID;
35 jfieldID gOptions_preferQualityOverSpeedFieldID;
36 jfieldID gOptions_scaledFieldID;
37 jfieldID gOptions_densityFieldID;
38 jfieldID gOptions_screenDensityFieldID;
39 jfieldID gOptions_targetDensityFieldID;
40 jfieldID gOptions_widthFieldID;
41 jfieldID gOptions_heightFieldID;
42 jfieldID gOptions_mimeFieldID;
43 jfieldID gOptions_outConfigFieldID;
44 jfieldID gOptions_outColorSpaceFieldID;
45 jfieldID gOptions_mCancelID;
46 jfieldID gOptions_bitmapFieldID;
47 
48 jfieldID gBitmap_ninePatchInsetsFieldID;
49 
50 jclass gBitmapConfig_class;
51 jmethodID gBitmapConfig_nativeToConfigMethodID;
52 
53 using namespace android;
54 
getMimeType(SkEncodedImageFormat format)55 const char* getMimeType(SkEncodedImageFormat format) {
56     switch (format) {
57         case SkEncodedImageFormat::kBMP:
58             return "image/bmp";
59         case SkEncodedImageFormat::kGIF:
60             return "image/gif";
61         case SkEncodedImageFormat::kICO:
62             return "image/x-ico";
63         case SkEncodedImageFormat::kJPEG:
64             return "image/jpeg";
65         case SkEncodedImageFormat::kPNG:
66             return "image/png";
67         case SkEncodedImageFormat::kWEBP:
68             return "image/webp";
69         case SkEncodedImageFormat::kHEIF:
70             return "image/heif";
71         case SkEncodedImageFormat::kWBMP:
72             return "image/vnd.wap.wbmp";
73         case SkEncodedImageFormat::kDNG:
74             return "image/x-adobe-dng";
75         default:
76             return nullptr;
77     }
78 }
79 
getMimeTypeAsJavaString(JNIEnv * env,SkEncodedImageFormat format)80 jstring getMimeTypeAsJavaString(JNIEnv* env, SkEncodedImageFormat format) {
81     jstring jstr = nullptr;
82     const char* mimeType = getMimeType(format);
83     if (mimeType) {
84         // NOTE: Caller should env->ExceptionCheck() for OOM
85         // (can't check for nullptr as it's a valid return value)
86         jstr = env->NewStringUTF(mimeType);
87     }
88     return jstr;
89 }
90 
91 class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
92 public:
ScaleCheckingAllocator(float scale,int size)93     ScaleCheckingAllocator(float scale, int size)
94             : mScale(scale), mSize(size) {
95     }
96 
allocPixelRef(SkBitmap * bitmap)97     virtual bool allocPixelRef(SkBitmap* bitmap) {
98         // accounts for scale in final allocation, using eventual size and config
99         const int bytesPerPixel = SkColorTypeBytesPerPixel(bitmap->colorType());
100         const int requestedSize = bytesPerPixel *
101                 int(bitmap->width() * mScale + 0.5f) *
102                 int(bitmap->height() * mScale + 0.5f);
103         if (requestedSize > mSize) {
104             ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
105                     mSize, requestedSize);
106             return false;
107         }
108         return SkBitmap::HeapAllocator::allocPixelRef(bitmap);
109     }
110 private:
111     const float mScale;
112     const int mSize;
113 };
114 
115 class RecyclingPixelAllocator : public SkBitmap::Allocator {
116 public:
RecyclingPixelAllocator(android::Bitmap * bitmap,unsigned int size)117     RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size)
118             : mBitmap(bitmap), mSize(size) {
119     }
120 
~RecyclingPixelAllocator()121     ~RecyclingPixelAllocator() {
122     }
123 
allocPixelRef(SkBitmap * bitmap)124     virtual bool allocPixelRef(SkBitmap* bitmap) {
125         const SkImageInfo& info = bitmap->info();
126         if (info.colorType() == kUnknown_SkColorType) {
127             ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
128             return false;
129         }
130 
131         const size_t size = info.computeByteSize(bitmap->rowBytes());
132         if (size > SK_MaxS32) {
133             ALOGW("bitmap is too large");
134             return false;
135         }
136 
137         if (size > mSize) {
138             ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap "
139                   "(%zu bytes)", mSize, size);
140             return false;
141         }
142 
143         mBitmap->reconfigure(info, bitmap->rowBytes());
144         bitmap->setPixelRef(sk_ref_sp(mBitmap), 0, 0);
145         return true;
146     }
147 
148 private:
149     android::Bitmap* const mBitmap;
150     const unsigned int mSize;
151 };
152 
153 // Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize
154 // (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the
155 // scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how
156 // best to round.
needsFineScale(const int fullSize,const int decodedSize,const int sampleSize)157 static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) {
158     if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) {
159         return true;
160     } else if ((fullSize / sampleSize + 1) != decodedSize &&
161                (fullSize / sampleSize) != decodedSize) {
162         return true;
163     }
164     return false;
165 }
166 
needsFineScale(const SkISize fullSize,const SkISize decodedSize,const int sampleSize)167 static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize,
168                            const int sampleSize) {
169     return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) ||
170            needsFineScale(fullSize.height(), decodedSize.height(), sampleSize);
171 }
172 
doDecode(JNIEnv * env,std::unique_ptr<SkStreamRewindable> stream,jobject padding,jobject options,jlong inBitmapHandle,jlong colorSpaceHandle)173 static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
174                         jobject padding, jobject options, jlong inBitmapHandle,
175                         jlong colorSpaceHandle) {
176     // Set default values for the options parameters.
177     int sampleSize = 1;
178     bool onlyDecodeSize = false;
179     SkColorType prefColorType = kN32_SkColorType;
180     bool isHardware = false;
181     bool isMutable = false;
182     float scale = 1.0f;
183     bool requireUnpremultiplied = false;
184     jobject javaBitmap = NULL;
185     sk_sp<SkColorSpace> prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
186 
187     // Update with options supplied by the client.
188     if (options != NULL) {
189         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
190         // Correct a non-positive sampleSize.  sampleSize defaults to zero within the
191         // options object, which is strange.
192         if (sampleSize <= 0) {
193             sampleSize = 1;
194         }
195 
196         if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
197             onlyDecodeSize = true;
198         }
199 
200         // initialize these, in case we fail later on
201         env->SetIntField(options, gOptions_widthFieldID, -1);
202         env->SetIntField(options, gOptions_heightFieldID, -1);
203         env->SetObjectField(options, gOptions_mimeFieldID, 0);
204         env->SetObjectField(options, gOptions_outConfigFieldID, 0);
205         env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0);
206 
207         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
208         prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
209         isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
210         isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
211         requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
212         javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
213 
214         if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
215             const int density = env->GetIntField(options, gOptions_densityFieldID);
216             const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
217             const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
218             if (density != 0 && targetDensity != 0 && density != screenDensity) {
219                 scale = (float) targetDensity / density;
220             }
221         }
222     }
223 
224     if (isMutable && isHardware) {
225         doThrowIAE(env, "Bitmaps with Config.HARDWARE are always immutable");
226         return nullObjectReturn("Cannot create mutable hardware bitmap");
227     }
228 
229     // Create the codec.
230     NinePatchPeeker peeker;
231     std::unique_ptr<SkAndroidCodec> codec;
232     {
233         SkCodec::Result result;
234         std::unique_ptr<SkCodec> c = SkCodec::MakeFromStream(std::move(stream), &result,
235                                                              &peeker);
236         if (!c) {
237             SkString msg;
238             msg.printf("Failed to create image decoder with message '%s'",
239                        SkCodec::ResultToString(result));
240             return nullObjectReturn(msg.c_str());
241         }
242 
243         codec = SkAndroidCodec::MakeFromCodec(std::move(c));
244         if (!codec) {
245             return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null");
246         }
247     }
248 
249     // Do not allow ninepatch decodes to 565.  In the past, decodes to 565
250     // would dither, and we do not want to pre-dither ninepatches, since we
251     // know that they will be stretched.  We no longer dither 565 decodes,
252     // but we continue to prevent ninepatches from decoding to 565, in order
253     // to maintain the old behavior.
254     if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) {
255         prefColorType = kN32_SkColorType;
256     }
257 
258     // Determine the output size.
259     SkISize size = codec->getSampledDimensions(sampleSize);
260 
261     int scaledWidth = size.width();
262     int scaledHeight = size.height();
263     bool willScale = false;
264 
265     // Apply a fine scaling step if necessary.
266     if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) {
267         willScale = true;
268         scaledWidth = codec->getInfo().width() / sampleSize;
269         scaledHeight = codec->getInfo().height() / sampleSize;
270     }
271 
272     // Set the decode colorType
273     SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
274     if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
275             !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
276         decodeColorType = kN32_SkColorType;
277     }
278 
279     sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(
280             decodeColorType, prefColorSpace);
281 
282     // Set the options and return if the client only wants the size.
283     if (options != NULL) {
284         jstring mimeType = getMimeTypeAsJavaString(env, codec->getEncodedFormat());
285         if (env->ExceptionCheck()) {
286             return nullObjectReturn("OOM in getMimeTypeAsJavaString()");
287         }
288         env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
289         env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
290         env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
291 
292         jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
293         if (isHardware) {
294             configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
295         }
296         jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
297                 gBitmapConfig_nativeToConfigMethodID, configID);
298         env->SetObjectField(options, gOptions_outConfigFieldID, config);
299 
300         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
301                 GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
302 
303         if (onlyDecodeSize) {
304             return nullptr;
305         }
306     }
307 
308     // Scale is necessary due to density differences.
309     if (scale != 1.0f) {
310         willScale = true;
311         scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f);
312         scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
313     }
314 
315     android::Bitmap* reuseBitmap = nullptr;
316     unsigned int existingBufferSize = 0;
317     if (javaBitmap != nullptr) {
318         reuseBitmap = &bitmap::toBitmap(inBitmapHandle);
319         if (reuseBitmap->isImmutable()) {
320             ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
321             javaBitmap = nullptr;
322             reuseBitmap = nullptr;
323         } else {
324             existingBufferSize = reuseBitmap->getAllocationByteCount();
325         }
326     }
327 
328     HeapAllocator defaultAllocator;
329     RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
330     ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
331     SkBitmap::HeapAllocator heapAllocator;
332     SkBitmap::Allocator* decodeAllocator;
333     if (javaBitmap != nullptr && willScale) {
334         // This will allocate pixels using a HeapAllocator, since there will be an extra
335         // scaling step that copies these pixels into Java memory.  This allocator
336         // also checks that the recycled javaBitmap is large enough.
337         decodeAllocator = &scaleCheckingAllocator;
338     } else if (javaBitmap != nullptr) {
339         decodeAllocator = &recyclingAllocator;
340     } else if (willScale || isHardware) {
341         // This will allocate pixels using a HeapAllocator,
342         // for scale case: there will be an extra scaling step.
343         // for hardware case: there will be extra swizzling & upload to gralloc step.
344         decodeAllocator = &heapAllocator;
345     } else {
346         decodeAllocator = &defaultAllocator;
347     }
348 
349     SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
350 
351     const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(),
352             decodeColorType, alphaType, decodeColorSpace);
353 
354     SkImageInfo bitmapInfo = decodeInfo;
355     if (decodeColorType == kGray_8_SkColorType) {
356         // The legacy implementation of BitmapFactory used kAlpha8 for
357         // grayscale images (before kGray8 existed).  While the codec
358         // recognizes kGray8, we need to decode into a kAlpha8 bitmap
359         // in order to avoid a behavior change.
360         bitmapInfo =
361                 bitmapInfo.makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType);
362     }
363     SkBitmap decodingBitmap;
364     if (!decodingBitmap.setInfo(bitmapInfo) ||
365             !decodingBitmap.tryAllocPixels(decodeAllocator)) {
366         // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo()
367         // should only only fail if the calculated value for rowBytes is too
368         // large.
369         // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the
370         // native heap, or the recycled javaBitmap being too small to reuse.
371         return nullptr;
372     }
373 
374     // Use SkAndroidCodec to perform the decode.
375     SkAndroidCodec::AndroidOptions codecOptions;
376     codecOptions.fZeroInitialized = decodeAllocator == &defaultAllocator ?
377             SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized;
378     codecOptions.fSampleSize = sampleSize;
379     SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(),
380             decodingBitmap.rowBytes(), &codecOptions);
381     switch (result) {
382         case SkCodec::kSuccess:
383         case SkCodec::kIncompleteInput:
384             break;
385         default:
386             return nullObjectReturn("codec->getAndroidPixels() failed.");
387     }
388 
389     // This is weird so let me explain: we could use the scale parameter
390     // directly, but for historical reasons this is how the corresponding
391     // Dalvik code has always behaved. We simply recreate the behavior here.
392     // The result is slightly different from simply using scale because of
393     // the 0.5f rounding bias applied when computing the target image size
394     const float scaleX = scaledWidth / float(decodingBitmap.width());
395     const float scaleY = scaledHeight / float(decodingBitmap.height());
396 
397     jbyteArray ninePatchChunk = NULL;
398     if (peeker.mPatch != NULL) {
399         if (willScale) {
400             peeker.scale(scaleX, scaleY, scaledWidth, scaledHeight);
401         }
402 
403         size_t ninePatchArraySize = peeker.mPatch->serializedSize();
404         ninePatchChunk = env->NewByteArray(ninePatchArraySize);
405         if (ninePatchChunk == NULL) {
406             return nullObjectReturn("ninePatchChunk == null");
407         }
408 
409         jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
410         if (array == NULL) {
411             return nullObjectReturn("primitive array == null");
412         }
413 
414         memcpy(array, peeker.mPatch, peeker.mPatchSize);
415         env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
416     }
417 
418     jobject ninePatchInsets = NULL;
419     if (peeker.mHasInsets) {
420         ninePatchInsets = peeker.createNinePatchInsets(env, scale);
421         if (ninePatchInsets == NULL) {
422             return nullObjectReturn("nine patch insets == null");
423         }
424         if (javaBitmap != NULL) {
425             env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
426         }
427     }
428 
429     SkBitmap outputBitmap;
430     if (willScale) {
431         // Set the allocator for the outputBitmap.
432         SkBitmap::Allocator* outputAllocator;
433         if (javaBitmap != nullptr) {
434             outputAllocator = &recyclingAllocator;
435         } else {
436             outputAllocator = &defaultAllocator;
437         }
438 
439         SkColorType scaledColorType = decodingBitmap.colorType();
440         // FIXME: If the alphaType is kUnpremul and the image has alpha, the
441         // colors may not be correct, since Skia does not yet support drawing
442         // to/from unpremultiplied bitmaps.
443         outputBitmap.setInfo(
444                 bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType));
445         if (!outputBitmap.tryAllocPixels(outputAllocator)) {
446             // This should only fail on OOM.  The recyclingAllocator should have
447             // enough memory since we check this before decoding using the
448             // scaleCheckingAllocator.
449             return nullObjectReturn("allocation failed for scaled bitmap");
450         }
451 
452         SkPaint paint;
453         // kSrc_Mode instructs us to overwrite the uninitialized pixels in
454         // outputBitmap.  Otherwise we would blend by default, which is not
455         // what we want.
456         paint.setBlendMode(SkBlendMode::kSrc);
457         paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
458 
459         SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy);
460         canvas.scale(scaleX, scaleY);
461         canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
462     } else {
463         outputBitmap.swap(decodingBitmap);
464     }
465 
466     if (padding) {
467         peeker.getPadding(env, padding);
468     }
469 
470     // If we get here, the outputBitmap should have an installed pixelref.
471     if (outputBitmap.pixelRef() == NULL) {
472         return nullObjectReturn("Got null SkPixelRef");
473     }
474 
475     if (!isMutable && javaBitmap == NULL) {
476         // promise we will never change our pixels (great for sharing and pictures)
477         outputBitmap.setImmutable();
478     }
479 
480     bool isPremultiplied = !requireUnpremultiplied;
481     if (javaBitmap != nullptr) {
482         bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
483         outputBitmap.notifyPixelsChanged();
484         // If a java bitmap was passed in for reuse, pass it back
485         return javaBitmap;
486     }
487 
488     int bitmapCreateFlags = 0x0;
489     if (isMutable) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Mutable;
490     if (isPremultiplied) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
491 
492     if (isHardware) {
493         sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(outputBitmap);
494         if (!hardwareBitmap.get()) {
495             return nullObjectReturn("Failed to allocate a hardware bitmap");
496         }
497         return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags,
498                 ninePatchChunk, ninePatchInsets, -1);
499     }
500 
501     // now create the java bitmap
502     return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
503             bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
504 }
505 
nativeDecodeStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage,jobject padding,jobject options,jlong inBitmapHandle,jlong colorSpaceHandle)506 static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
507         jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) {
508 
509     jobject bitmap = NULL;
510     std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
511 
512     if (stream.get()) {
513         std::unique_ptr<SkStreamRewindable> bufferedStream(
514                 SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded()));
515         SkASSERT(bufferedStream.get() != NULL);
516         bitmap = doDecode(env, std::move(bufferedStream), padding, options, inBitmapHandle,
517                           colorSpaceHandle);
518     }
519     return bitmap;
520 }
521 
nativeDecodeFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor,jobject padding,jobject bitmapFactoryOptions,jlong inBitmapHandle,jlong colorSpaceHandle)522 static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
523         jobject padding, jobject bitmapFactoryOptions, jlong inBitmapHandle, jlong colorSpaceHandle) {
524 #ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC
525       return nullObjectReturn("Not supported on Windows");
526 #else
527     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
528 
529     int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
530 
531     struct stat fdStat;
532     if (fstat(descriptor, &fdStat) == -1) {
533         doThrowIOE(env, "broken file descriptor");
534         return nullObjectReturn("fstat return -1");
535     }
536 
537     // Restore the descriptor's offset on exiting this function. Even though
538     // we dup the descriptor, both the original and dup refer to the same open
539     // file description and changes to the file offset in one impact the other.
540     AutoFDSeek autoRestore(descriptor);
541 
542     // Duplicate the descriptor here to prevent leaking memory. A leak occurs
543     // if we only close the file descriptor and not the file object it is used to
544     // create.  If we don't explicitly clean up the file (which in turn closes the
545     // descriptor) the buffers allocated internally by fseek will be leaked.
546     int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0);
547 
548     FILE* file = fdopen(dupDescriptor, "r");
549     if (file == NULL) {
550         // cleanup the duplicated descriptor since it will not be closed when the
551         // file is cleaned up (fclose).
552         close(dupDescriptor);
553         return nullObjectReturn("Could not open file");
554     }
555 
556     std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
557 
558     // If there is no offset for the file descriptor, we use SkFILEStream directly.
559     if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
560         assert(isSeekable(dupDescriptor));
561         return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions,
562                         inBitmapHandle, colorSpaceHandle);
563     }
564 
565     // Use a buffered stream. Although an SkFILEStream can be rewound, this
566     // ensures that SkImageDecoder::Factory never rewinds beyond the
567     // current position of the file descriptor.
568     std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Make(std::move(fileStream),
569             SkCodec::MinBufferedBytesNeeded()));
570 
571     return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle,
572                     colorSpaceHandle);
573 #endif
574 }
575 
nativeDecodeAsset(JNIEnv * env,jobject clazz,jlong native_asset,jobject padding,jobject options,jlong inBitmapHandle,jlong colorSpaceHandle)576 static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
577         jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) {
578 
579     Asset* asset = reinterpret_cast<Asset*>(native_asset);
580     // since we know we'll be done with the asset when we return, we can
581     // just use a simple wrapper
582     return doDecode(env, std::make_unique<AssetStreamAdaptor>(asset), padding, options,
583                     inBitmapHandle, colorSpaceHandle);
584 }
585 
nativeDecodeByteArray(JNIEnv * env,jobject,jbyteArray byteArray,jint offset,jint length,jobject options,jlong inBitmapHandle,jlong colorSpaceHandle)586 static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
587         jint offset, jint length, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) {
588 
589     AutoJavaByteArray ar(env, byteArray);
590     return doDecode(env, std::make_unique<SkMemoryStream>(ar.ptr() + offset, length, false),
591                     nullptr, options, inBitmapHandle, colorSpaceHandle);
592 }
593 
nativeIsSeekable(JNIEnv * env,jobject,jobject fileDescriptor)594 static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
595     jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
596     return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE;
597 }
598 
599 ///////////////////////////////////////////////////////////////////////////////
600 
601 static const JNINativeMethod gMethods[] = {
602     {   "nativeDecodeStream",
603         "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
604         (void*)nativeDecodeStream
605     },
606 
607     {   "nativeDecodeFileDescriptor",
608         "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
609         (void*)nativeDecodeFileDescriptor
610     },
611 
612     {   "nativeDecodeAsset",
613         "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
614         (void*)nativeDecodeAsset
615     },
616 
617     {   "nativeDecodeByteArray",
618         "([BIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
619         (void*)nativeDecodeByteArray
620     },
621 
622     {   "nativeIsSeekable",
623         "(Ljava/io/FileDescriptor;)Z",
624         (void*)nativeIsSeekable
625     },
626 };
627 
register_android_graphics_BitmapFactory(JNIEnv * env)628 int register_android_graphics_BitmapFactory(JNIEnv* env) {
629     jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options");
630     gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap",
631             "Landroid/graphics/Bitmap;");
632     gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z");
633     gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I");
634     gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig",
635             "Landroid/graphics/Bitmap$Config;");
636     gOptions_colorSpaceFieldID = GetFieldIDOrDie(env, options_class, "inPreferredColorSpace",
637             "Landroid/graphics/ColorSpace;");
638     gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z");
639     gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z");
640     gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z");
641     gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class,
642             "inPreferQualityOverSpeed", "Z");
643     gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z");
644     gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I");
645     gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I");
646     gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I");
647     gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I");
648     gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I");
649     gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;");
650     gOptions_outConfigFieldID = GetFieldIDOrDie(env, options_class, "outConfig",
651              "Landroid/graphics/Bitmap$Config;");
652     gOptions_outColorSpaceFieldID = GetFieldIDOrDie(env, options_class, "outColorSpace",
653              "Landroid/graphics/ColorSpace;");
654     gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z");
655 
656     jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap");
657     gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets",
658             "Landroid/graphics/NinePatch$InsetStruct;");
659 
660     gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
661             "android/graphics/Bitmap$Config"));
662     gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class,
663             "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;");
664 
665     return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory",
666                                          gMethods, NELEM(gMethods));
667 }
668