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