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