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