blob: 3e1f26adc1118f7de48f08129be52fb0ff117a70 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001#define LOG_TAG "BitmapFactory"
2
Joseph Wenf1f48bc2010-07-19 16:59:51 +08003#include "BitmapFactory.h"
Leon Scrogginsa06d86a2011-03-02 16:56:54 -05004#include "NinePatchPeeker.h"
Derek Sollenberger5827cb52013-07-26 14:58:06 -04005#include "SkData.h"
Leon Scroggins III7315f1b2013-09-10 20:26:05 -04006#include "SkFrontBufferedStream.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007#include "SkImageDecoder.h"
Mike Reedc70e06b2009-04-24 11:09:12 -04008#include "SkImageRef_ashmem.h"
9#include "SkImageRef_GlobalPool.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080010#include "SkPixelRef.h"
11#include "SkStream.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080012#include "SkTemplates.h"
13#include "SkUtils.h"
14#include "CreateJavaOutputStreamAdaptor.h"
Joseph Wenf1f48bc2010-07-19 16:59:51 +080015#include "AutoDecodeCancel.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080016#include "Utils.h"
Elliott Hughesa3804cf2011-04-11 16:50:19 -070017#include "JNIHelp.h"
Chris Craik1abf5d62013-08-16 12:47:03 -070018#include "GraphicsJNI.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019
20#include <android_runtime/AndroidRuntime.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080021#include <androidfw/Asset.h>
22#include <androidfw/ResourceTypes.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023#include <netinet/in.h>
Leon Scroggins III0102f8a2014-01-14 15:14:57 -050024#include <stdio.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025#include <sys/mman.h>
Joseph Wen2dcfbef2010-09-10 10:15:09 +080026#include <sys/stat.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027
Joseph Wenf1f48bc2010-07-19 16:59:51 +080028jfieldID gOptions_justBoundsFieldID;
29jfieldID gOptions_sampleSizeFieldID;
30jfieldID gOptions_configFieldID;
Chris Craik1abf5d62013-08-16 12:47:03 -070031jfieldID gOptions_premultipliedFieldID;
Romain Guy23610982011-01-17 12:51:55 -080032jfieldID gOptions_mutableFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080033jfieldID gOptions_ditherFieldID;
34jfieldID gOptions_purgeableFieldID;
35jfieldID gOptions_shareableFieldID;
Wei-Ta Chen953f9092010-12-03 14:06:18 -080036jfieldID gOptions_preferQualityOverSpeedFieldID;
Chris Craik905e8242013-06-05 09:59:05 -070037jfieldID gOptions_scaledFieldID;
38jfieldID gOptions_densityFieldID;
39jfieldID gOptions_screenDensityFieldID;
40jfieldID gOptions_targetDensityFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080041jfieldID gOptions_widthFieldID;
42jfieldID gOptions_heightFieldID;
43jfieldID gOptions_mimeFieldID;
44jfieldID gOptions_mCancelID;
Chet Haase37f74ca2010-12-08 17:56:36 -080045jfieldID gOptions_bitmapFieldID;
Chet Haase37f74ca2010-12-08 17:56:36 -080046jfieldID gBitmap_nativeBitmapFieldID;
Amith Yamasaniec4a5042012-04-04 10:27:15 -070047jfieldID gBitmap_layoutBoundsFieldID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049#if 0
50 #define TRACE_BITMAP(code) code
51#else
52 #define TRACE_BITMAP(code)
53#endif
54
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055using namespace android;
56
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057static inline int32_t validOrNeg1(bool isValid, int32_t value) {
58// return isValid ? value : -1;
59 SkASSERT((int)isValid == 0 || (int)isValid == 1);
60 return ((int32_t)isValid - 1) | value;
61}
62
Joseph Wenf1f48bc2010-07-19 16:59:51 +080063jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 static const struct {
65 SkImageDecoder::Format fFormat;
66 const char* fMimeType;
67 } gMimeTypes[] = {
68 { SkImageDecoder::kBMP_Format, "image/bmp" },
69 { SkImageDecoder::kGIF_Format, "image/gif" },
70 { SkImageDecoder::kICO_Format, "image/x-ico" },
71 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
72 { SkImageDecoder::kPNG_Format, "image/png" },
Chris Craik95587f92013-07-12 19:46:19 -070073 { SkImageDecoder::kWEBP_Format, "image/webp" },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
75 };
Elliott Hughesa3804cf2011-04-11 16:50:19 -070076
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 const char* cstr = NULL;
78 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
79 if (gMimeTypes[i].fFormat == format) {
80 cstr = gMimeTypes[i].fMimeType;
81 break;
82 }
83 }
84
85 jstring jstr = 0;
86 if (NULL != cstr) {
87 jstr = env->NewStringUTF(cstr);
88 }
89 return jstr;
90}
91
Mike Reedc70e06b2009-04-24 11:09:12 -040092static bool optionsPurgeable(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -070093 return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
Mike Reedc70e06b2009-04-24 11:09:12 -040094}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095
Mike Reedc70e06b2009-04-24 11:09:12 -040096static bool optionsShareable(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -070097 return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
Mike Reedc70e06b2009-04-24 11:09:12 -040098}
99
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000100static bool optionsJustBounds(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -0700101 return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000102}
103
Romain Guy7b2f8b82012-03-19 17:18:54 -0700104static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
105 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
106 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
107 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
108 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
109
110 for (int i = 0; i < chunk->numXDivs; i++) {
111 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
112 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
113 chunk->xDivs[i]++;
114 }
115 }
116
117 for (int i = 0; i < chunk->numYDivs; i++) {
118 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
119 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
120 chunk->yDivs[i]++;
121 }
122 }
123}
124
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400125static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700126 int sampleSize, bool ditherImage) {
127
Mike Reed17154412009-09-24 12:35:27 -0400128 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400129 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700130 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400131 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
132 } else {
133 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
134 }
Mike Reed17154412009-09-24 12:35:27 -0400135 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400136 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700137 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400138 return pr;
139}
140
Chris Craik905e8242013-06-05 09:59:05 -0700141static SkBitmap::Config configForScaledOutput(SkBitmap::Config config) {
142 switch (config) {
143 case SkBitmap::kNo_Config:
144 case SkBitmap::kIndex8_Config:
Chris Craik905e8242013-06-05 09:59:05 -0700145 return SkBitmap::kARGB_8888_Config;
146 default:
147 break;
148 }
149 return config;
150}
151
152class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
153public:
154 ScaleCheckingAllocator(float scale, int size)
155 : mScale(scale), mSize(size) {
156 }
157
158 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
159 // accounts for scale in final allocation, using eventual size and config
160 const int bytesPerPixel = SkBitmap::ComputeBytesPerPixel(
Leon Scroggins III4b9a19a2013-12-03 15:06:30 -0500161 configForScaledOutput(bitmap->config()));
Chris Craik905e8242013-06-05 09:59:05 -0700162 const int requestedSize = bytesPerPixel *
163 int(bitmap->width() * mScale + 0.5f) *
164 int(bitmap->height() * mScale + 0.5f);
165 if (requestedSize > mSize) {
166 ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
167 mSize, requestedSize);
168 return false;
169 }
170 return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
171 }
172private:
173 const float mScale;
174 const int mSize;
175};
176
Chris Craik7e8c03c2013-06-03 13:53:36 -0700177class RecyclingPixelAllocator : public SkBitmap::Allocator {
178public:
179 RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
Chris Craik905e8242013-06-05 09:59:05 -0700180 : mPixelRef(pixelRef), mSize(size) {
Chris Craik7e8c03c2013-06-03 13:53:36 -0700181 SkSafeRef(mPixelRef);
182 }
183
184 ~RecyclingPixelAllocator() {
185 SkSafeUnref(mPixelRef);
186 }
187
188 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
189 if (!bitmap->getSize64().is32() || bitmap->getSize() > mSize) {
Chris Craik905e8242013-06-05 09:59:05 -0700190 ALOGW("bitmap marked for reuse (%d bytes) can't fit new bitmap (%d bytes)",
191 mSize, bitmap->getSize());
Chris Craik7e8c03c2013-06-03 13:53:36 -0700192 return false;
193 }
Chris Craikcd0ba712013-09-06 14:40:30 -0700194
195 // Create a new pixelref with the new ctable that wraps the previous pixelref
196 SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef), ctable);
197
198 bitmap->setPixelRef(pr)->unref();
199 // since we're already allocated, we lockPixels right away
200 // HeapAllocator/JavaPixelAllocator behaves this way too
Chris Craik7e8c03c2013-06-03 13:53:36 -0700201 bitmap->lockPixels();
202 return true;
203 }
204
205private:
206 SkPixelRef* const mPixelRef;
207 const unsigned int mSize;
208};
209
Mike Reedc70e06b2009-04-24 11:09:12 -0400210// since we "may" create a purgeable imageref, we require the stream be ref'able
211// i.e. dynamically allocated, since its lifetime may exceed the current stack
212// frame.
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400213static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
Chris Craik905e8242013-06-05 09:59:05 -0700214 jobject options, bool allowPurgeable, bool forcePurgeable = false) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 int sampleSize = 1;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
Romain Guy99b7b7a2011-07-14 12:42:19 -0700219 SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700220
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 bool doDither = true;
Romain Guy23610982011-01-17 12:51:55 -0800222 bool isMutable = false;
Chris Craik905e8242013-06-05 09:59:05 -0700223 float scale = 1.0f;
224 bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800225 bool preferQualityOverSpeed = false;
Chris Craik1abf5d62013-08-16 12:47:03 -0700226 bool requireUnpremultiplied = false;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700227
Chet Haase37f74ca2010-12-08 17:56:36 -0800228 jobject javaBitmap = NULL;
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700229
Romain Guy7b2f8b82012-03-19 17:18:54 -0700230 if (options != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000232 if (optionsJustBounds(env, options)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 mode = SkImageDecoder::kDecodeBounds_Mode;
234 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 // initialize these, in case we fail later on
237 env->SetIntField(options, gOptions_widthFieldID, -1);
238 env->SetIntField(options, gOptions_heightFieldID, -1);
239 env->SetObjectField(options, gOptions_mimeFieldID, 0);
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
242 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
Romain Guy23610982011-01-17 12:51:55 -0800243 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800245 preferQualityOverSpeed = env->GetBooleanField(options,
246 gOptions_preferQualityOverSpeedFieldID);
Chris Craik1abf5d62013-08-16 12:47:03 -0700247 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
Chet Haase37f74ca2010-12-08 17:56:36 -0800248 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
Chris Craik905e8242013-06-05 09:59:05 -0700249
250 if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
251 const int density = env->GetIntField(options, gOptions_densityFieldID);
252 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
253 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
254 if (density != 0 && targetDensity != 0 && density != screenDensity) {
255 scale = (float) targetDensity / density;
256 }
257 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 }
259
Chris Craik905e8242013-06-05 09:59:05 -0700260 const bool willScale = scale != 1.0f;
261 isPurgeable &= !willScale;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700264 if (decoder == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400265 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 decoder->setSampleSize(sampleSize);
269 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800270 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
Chris Craik1abf5d62013-08-16 12:47:03 -0700271 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272
Chris Craik7e8c03c2013-06-03 13:53:36 -0700273 SkBitmap* outputBitmap = NULL;
Chris Craik9f583612013-05-20 18:13:47 -0700274 unsigned int existingBufferSize = 0;
Chris Craik7e8c03c2013-06-03 13:53:36 -0700275 if (javaBitmap != NULL) {
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000276 outputBitmap = (SkBitmap*) env->GetLongField(javaBitmap, gBitmap_nativeBitmapFieldID);
Chris Craik7e8c03c2013-06-03 13:53:36 -0700277 if (outputBitmap->isImmutable()) {
Derek Sollenberger2a6ecae2012-08-31 14:03:51 -0400278 ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
Chris Craik7e8c03c2013-06-03 13:53:36 -0700279 javaBitmap = NULL;
280 outputBitmap = NULL;
281 } else {
282 existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
Derek Sollenberger2a6ecae2012-08-31 14:03:51 -0400283 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800284 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285
Chris Craik7e8c03c2013-06-03 13:53:36 -0700286 SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
287 if (outputBitmap == NULL) outputBitmap = adb.get();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288
Chris Craik7e8c03c2013-06-03 13:53:36 -0700289 NinePatchPeeker peeker(decoder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400291
Chris Craik7e8c03c2013-06-03 13:53:36 -0700292 SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700293
Chris Craik905e8242013-06-05 09:59:05 -0700294 JavaPixelAllocator javaAllocator(env);
295 RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
296 ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
297 SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
298 (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
299 if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
300 if (!willScale) {
Leon Scroggins III1ffe7272013-09-19 11:34:06 -0400301 // If the java allocator is being used to allocate the pixel memory, the decoder
302 // need not write zeroes, since the memory is initialized to 0.
303 decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator);
Chris Craik905e8242013-06-05 09:59:05 -0700304 decoder->setAllocator(outputAllocator);
305 } else if (javaBitmap != NULL) {
306 // check for eventual scaled bounds at allocation time, so we don't decode the bitmap
307 // only to find the scaled result too large to fit in the allocation
308 decoder->setAllocator(&scaleCheckingAllocator);
309 }
310 }
311
Derek Sollenberger6b0437c2013-06-24 15:40:54 -0400312 // Only setup the decoder to be deleted after its stack-based, refcounted
313 // components (allocators, peekers, etc) are declared. This prevents RefCnt
314 // asserts from firing due to the order objects are deleted from the stack.
315 SkAutoTDelete<SkImageDecoder> add(decoder);
316
317 AutoDecoderCancel adc(options, decoder);
318
319 // To fix the race condition in case "requestCancelDecode"
320 // happens earlier than AutoDecoderCancel object is added
321 // to the gAutoDecoderCancelMutex linked list.
322 if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
323 return nullObjectReturn("gOptions_mCancelID");
324 }
325
Chris Craik7e8c03c2013-06-03 13:53:36 -0700326 SkBitmap decodingBitmap;
327 if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400328 return nullObjectReturn("decoder->decode returned false");
329 }
330
Chris Craik7e8c03c2013-06-03 13:53:36 -0700331 int scaledWidth = decodingBitmap.width();
332 int scaledHeight = decodingBitmap.height();
Romain Guy7b2f8b82012-03-19 17:18:54 -0700333
334 if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
335 scaledWidth = int(scaledWidth * scale + 0.5f);
336 scaledHeight = int(scaledHeight * scale + 0.5f);
337 }
338
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 // update options (if any)
Romain Guy7b2f8b82012-03-19 17:18:54 -0700340 if (options != NULL) {
341 env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
342 env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 env->SetObjectField(options, gOptions_mimeFieldID,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700344 getMimeTypeString(env, decoder->getFormat()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 // if we're in justBounds mode, return now (skip the java bitmap)
Romain Guy7b2f8b82012-03-19 17:18:54 -0700348 if (mode == SkImageDecoder::kDecodeBounds_Mode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 return NULL;
350 }
351
352 jbyteArray ninePatchChunk = NULL;
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700353 if (peeker.fPatch != NULL) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700354 if (willScale) {
355 scaleNinePatchChunk(peeker.fPatch, scale);
356 }
357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
359 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700360 if (ninePatchChunk == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400361 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700363
364 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
365 if (array == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400366 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 peeker.fPatch->serialize(array);
370 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
371 }
372
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700373 jintArray layoutBounds = NULL;
374 if (peeker.fLayoutBounds != NULL) {
375 layoutBounds = env->NewIntArray(4);
376 if (layoutBounds == NULL) {
377 return nullObjectReturn("layoutBounds == null");
378 }
379
Dianne Hackborn76344242012-04-30 14:05:09 -0700380 jint scaledBounds[4];
381 if (willScale) {
382 for (int i=0; i<4; i++) {
383 scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f);
384 }
385 } else {
386 memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds));
387 }
388 env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds);
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700389 if (javaBitmap != NULL) {
390 env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
391 }
392 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393
Romain Guy7b2f8b82012-03-19 17:18:54 -0700394 if (willScale) {
395 // This is weird so let me explain: we could use the scale parameter
396 // directly, but for historical reasons this is how the corresponding
397 // Dalvik code has always behaved. We simply recreate the behavior here.
398 // The result is slightly different from simply using scale because of
399 // the 0.5f rounding bias applied when computing the target image size
Chris Craik7e8c03c2013-06-03 13:53:36 -0700400 const float sx = scaledWidth / float(decodingBitmap.width());
401 const float sy = scaledHeight / float(decodingBitmap.height());
Romain Guy7b2f8b82012-03-19 17:18:54 -0700402
Chris Craik905e8242013-06-05 09:59:05 -0700403 // TODO: avoid copying when scaled size equals decodingBitmap size
404 SkBitmap::Config config = configForScaledOutput(decodingBitmap.config());
Leon Scroggins III8790be62013-12-03 16:26:51 -0500405 // FIXME: If the alphaType is kUnpremul and the image has alpha, the
406 // colors may not be correct, since Skia does not yet support drawing
407 // to/from unpremultiplied bitmaps.
408 outputBitmap->setConfig(config, scaledWidth, scaledHeight, 0,
409 decodingBitmap.alphaType());
Chris Craik905e8242013-06-05 09:59:05 -0700410 if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
Raph Levien005bfc62012-09-20 22:51:47 -0700411 return nullObjectReturn("allocation failed for scaled bitmap");
412 }
Leon Scroggins III1ffe7272013-09-19 11:34:06 -0400413
414 // If outputBitmap's pixels are newly allocated by Java, there is no need
415 // to erase to 0, since the pixels were initialized to 0.
416 if (outputAllocator != &javaAllocator) {
417 outputBitmap->eraseColor(0);
418 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700419
420 SkPaint paint;
421 paint.setFilterBitmap(true);
422
Chris Craik7e8c03c2013-06-03 13:53:36 -0700423 SkCanvas canvas(*outputBitmap);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700424 canvas.scale(sx, sy);
Chris Craik7e8c03c2013-06-03 13:53:36 -0700425 canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
426 } else {
427 outputBitmap->swap(decodingBitmap);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700428 }
429
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 if (padding) {
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700431 if (peeker.fPatch != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 GraphicsJNI::set_jrect(env, padding,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700433 peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
434 peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 } else {
436 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
437 }
438 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439
Mike Reedc70e06b2009-04-24 11:09:12 -0400440 SkPixelRef* pr;
441 if (isPurgeable) {
Chris Craik7e8c03c2013-06-03 13:53:36 -0700442 pr = installPixelRef(outputBitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400443 } else {
444 // if we get here, we're in kDecodePixels_Mode and will therefore
445 // already have a pixelref installed.
Chris Craik7e8c03c2013-06-03 13:53:36 -0700446 pr = outputBitmap->pixelRef();
Mike Reedc70e06b2009-04-24 11:09:12 -0400447 }
Marco Nelissenb2fe3be2012-05-07 11:24:13 -0700448 if (pr == NULL) {
449 return nullObjectReturn("Got null SkPixelRef");
450 }
Romain Guy23610982011-01-17 12:51:55 -0800451
Chris Craik7e8c03c2013-06-03 13:53:36 -0700452 if (!isMutable && javaBitmap == NULL) {
Romain Guy23610982011-01-17 12:51:55 -0800453 // promise we will never change our pixels (great for sharing and pictures)
454 pr->setImmutable();
455 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800456
Jeff Brown27d83832012-05-09 17:30:31 -0700457 // detach bitmap from its autodeleter, since we want to own it now
458 adb.detach();
459
Chris Craik7e8c03c2013-06-03 13:53:36 -0700460 if (javaBitmap != NULL) {
Chris Craik1abf5d62013-08-16 12:47:03 -0700461 bool isPremultiplied = !requireUnpremultiplied;
462 GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied);
Chris Craik7e8c03c2013-06-03 13:53:36 -0700463 outputBitmap->notifyPixelsChanged();
Chet Haase37f74ca2010-12-08 17:56:36 -0800464 // If a java bitmap was passed in for reuse, pass it back
465 return javaBitmap;
466 }
Chris Craik1abf5d62013-08-16 12:47:03 -0700467
468 int bitmapCreateFlags = 0x0;
469 if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
470 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
471
Mike Reedc70e06b2009-04-24 11:09:12 -0400472 // now create the java bitmap
Chris Craik7e8c03c2013-06-03 13:53:36 -0700473 return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
Chris Craik1abf5d62013-08-16 12:47:03 -0700474 bitmapCreateFlags, ninePatchChunk, layoutBounds, -1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475}
476
Chris Craik905e8242013-06-05 09:59:05 -0700477static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
478 jobject padding, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 jobject bitmap = NULL;
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400481 SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400483 if (stream.get()) {
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400484 // Need to buffer enough input to be able to rewind as much as might be read by a decoder
485 // trying to determine the stream's format. Currently the most is 64, read by
486 // SkImageDecoder_libwebp.
487 // FIXME: Get this number from SkImageDecoder
488 SkAutoTUnref<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Create(stream, 64));
489 SkASSERT(bufferedStream.get() != NULL);
Mike Reedc70e06b2009-04-24 11:09:12 -0400490 // for now we don't allow purgeable with java inputstreams
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400491 bitmap = doDecode(env, bufferedStream, padding, options, false, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 }
493 return bitmap;
494}
495
Romain Guy7b2f8b82012-03-19 17:18:54 -0700496static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
497 jobject padding, jobject bitmapFactoryOptions) {
498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
500
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700501 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400502
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400503 struct stat fdStat;
504 if (fstat(descriptor, &fdStat) == -1) {
505 doThrowIOE(env, "broken file descriptor");
506 return nullObjectReturn("fstat return -1");
507 }
508
Mike Reedc70e06b2009-04-24 11:09:12 -0400509 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
510 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
511 bool weOwnTheFD = false;
512 if (isPurgeable && isShareable) {
513 int newFD = ::dup(descriptor);
514 if (-1 != newFD) {
515 weOwnTheFD = true;
516 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519
Leon Scroggins III0102f8a2014-01-14 15:14:57 -0500520 FILE* file = fdopen(descriptor, "r");
521 if (file == NULL) {
522 return nullObjectReturn("Could not open file");
Leon Scroggins IIIf65183f2013-10-07 16:32:14 -0400523 }
Leon Scroggins III0102f8a2014-01-14 15:14:57 -0500524
525 SkAutoTUnref<SkFILEStream> stream(new SkFILEStream(file,
526 weOwnTheFD ? SkFILEStream::kCallerPasses_Ownership :
527 SkFILEStream::kCallerRetains_Ownership));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700529 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
530 shareable case.
531 */
532 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400533}
534
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000535static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
Chris Craik905e8242013-06-05 09:59:05 -0700536 jobject padding, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700537
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400538 SkStreamRewindable* stream;
Mike Reedc70e06b2009-04-24 11:09:12 -0400539 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Romain Guy18ef37252010-08-19 15:10:54 -0700540 bool forcePurgeable = optionsPurgeable(env, options);
541 if (forcePurgeable) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400542 // if we could "ref/reopen" the asset, we may not need to copy it here
543 // and we could assume optionsShareable, since assets are always RO
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400544 stream = CopyAssetToStream(asset);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700545 if (stream == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400546 return NULL;
547 }
548 } else {
549 // since we know we'll be done with the asset when we return, we can
550 // just use a simple wrapper
Leon Scroggins IIIb9c58ab2013-12-03 15:10:04 -0500551 stream = new AssetStreamAdaptor(asset,
552 AssetStreamAdaptor::kNo_OwnAsset,
553 AssetStreamAdaptor::kNo_HasMemoryBase);
Mike Reedc70e06b2009-04-24 11:09:12 -0400554 }
555 SkAutoUnref aur(stream);
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400556 return doDecode(env, stream, padding, options, forcePurgeable, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557}
558
559static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000560 jint offset, jint length, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700561
Mike Reedc70e06b2009-04-24 11:09:12 -0400562 /* If optionsShareable() we could decide to just wrap the java array and
563 share it, but that means adding a globalref to the java array object
564 and managing its lifetime. For now we just always copy the array's data
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000565 if optionsPurgeable(), unless we're just decoding bounds.
Mike Reedc70e06b2009-04-24 11:09:12 -0400566 */
Romain Guy7b2f8b82012-03-19 17:18:54 -0700567 bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
Mike Reedc70e06b2009-04-24 11:09:12 -0400568 AutoJavaByteArray ar(env, byteArray);
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400569 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
Mike Reedc70e06b2009-04-24 11:09:12 -0400570 SkAutoUnref aur(stream);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000571 return doDecode(env, stream, NULL, options, purgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572}
573
574static void nativeRequestCancel(JNIEnv*, jobject joptions) {
575 (void)AutoDecoderCancel::RequestCancel(joptions);
576}
577
Owen Lina9d0d472011-01-18 17:39:15 +0800578static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700579 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Owen Lina9d0d472011-01-18 17:39:15 +0800580 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
581}
582
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583///////////////////////////////////////////////////////////////////////////////
584
585static JNINativeMethod gMethods[] = {
586 { "nativeDecodeStream",
587 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
588 (void*)nativeDecodeStream
589 },
590
591 { "nativeDecodeFileDescriptor",
592 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
593 (void*)nativeDecodeFileDescriptor
594 },
595
596 { "nativeDecodeAsset",
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000597 "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 (void*)nativeDecodeAsset
599 },
600
601 { "nativeDecodeByteArray",
602 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
603 (void*)nativeDecodeByteArray
604 },
605
Owen Lina9d0d472011-01-18 17:39:15 +0800606 { "nativeIsSeekable",
607 "(Ljava/io/FileDescriptor;)Z",
608 (void*)nativeIsSeekable
609 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610};
611
612static JNINativeMethod gOptionsMethods[] = {
613 { "requestCancel", "()V", (void*)nativeRequestCancel }
614};
615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
617 const char fieldname[], const char type[]) {
618 jfieldID id = env->GetFieldID(clazz, fieldname, type);
619 SkASSERT(id);
620 return id;
621}
622
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623int register_android_graphics_BitmapFactory(JNIEnv* env) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700624 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
625 SkASSERT(options_class);
626 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
Chet Haase37f74ca2010-12-08 17:56:36 -0800627 "Landroid/graphics/Bitmap;");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700628 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
629 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
630 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 "Landroid/graphics/Bitmap$Config;");
Chris Craik1abf5d62013-08-16 12:47:03 -0700632 gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700633 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
634 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
635 gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
636 gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
637 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800638 "inPreferQualityOverSpeed", "Z");
Chris Craik905e8242013-06-05 09:59:05 -0700639 gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
640 gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
641 gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
642 gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700643 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
644 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
645 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
646 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700648 jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
649 SkASSERT(bitmap_class);
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000650 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "J");
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700651 gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 int ret = AndroidRuntime::registerNativeMethods(env,
653 "android/graphics/BitmapFactory$Options",
654 gOptionsMethods,
655 SK_ARRAY_COUNT(gOptionsMethods));
656 if (ret) {
657 return ret;
658 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700659 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 gMethods, SK_ARRAY_COUNT(gMethods));
661}