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