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