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