blob: f58f35da300fb3878def260278aac434f8e37658 [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"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005#include "SkImageDecoder.h"
Mike Reedc70e06b2009-04-24 11:09:12 -04006#include "SkImageRef_ashmem.h"
7#include "SkImageRef_GlobalPool.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008#include "SkPixelRef.h"
9#include "SkStream.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080010#include "SkTemplates.h"
11#include "SkUtils.h"
12#include "CreateJavaOutputStreamAdaptor.h"
Joseph Wenf1f48bc2010-07-19 16:59:51 +080013#include "AutoDecodeCancel.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080014#include "Utils.h"
Elliott Hughesa3804cf2011-04-11 16:50:19 -070015#include "JNIHelp.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080016
17#include <android_runtime/AndroidRuntime.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080018#include <androidfw/Asset.h>
19#include <androidfw/ResourceTypes.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020#include <netinet/in.h>
21#include <sys/mman.h>
Joseph Wen2dcfbef2010-09-10 10:15:09 +080022#include <sys/stat.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
Joseph Wenf1f48bc2010-07-19 16:59:51 +080024jfieldID gOptions_justBoundsFieldID;
25jfieldID gOptions_sampleSizeFieldID;
26jfieldID gOptions_configFieldID;
Romain Guy23610982011-01-17 12:51:55 -080027jfieldID gOptions_mutableFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080028jfieldID gOptions_ditherFieldID;
29jfieldID gOptions_purgeableFieldID;
30jfieldID gOptions_shareableFieldID;
Wei-Ta Chen953f9092010-12-03 14:06:18 -080031jfieldID gOptions_preferQualityOverSpeedFieldID;
Chris Craik905e8242013-06-05 09:59:05 -070032jfieldID gOptions_scaledFieldID;
33jfieldID gOptions_densityFieldID;
34jfieldID gOptions_screenDensityFieldID;
35jfieldID gOptions_targetDensityFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080036jfieldID gOptions_widthFieldID;
37jfieldID gOptions_heightFieldID;
38jfieldID gOptions_mimeFieldID;
39jfieldID gOptions_mCancelID;
Chet Haase37f74ca2010-12-08 17:56:36 -080040jfieldID gOptions_bitmapFieldID;
Chet Haase37f74ca2010-12-08 17:56:36 -080041jfieldID gBitmap_nativeBitmapFieldID;
Amith Yamasaniec4a5042012-04-04 10:27:15 -070042jfieldID gBitmap_layoutBoundsFieldID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044#if 0
45 #define TRACE_BITMAP(code) code
46#else
47 #define TRACE_BITMAP(code)
48#endif
49
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050using namespace android;
51
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052static inline int32_t validOrNeg1(bool isValid, int32_t value) {
53// return isValid ? value : -1;
54 SkASSERT((int)isValid == 0 || (int)isValid == 1);
55 return ((int32_t)isValid - 1) | value;
56}
57
Joseph Wenf1f48bc2010-07-19 16:59:51 +080058jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 static const struct {
60 SkImageDecoder::Format fFormat;
61 const char* fMimeType;
62 } gMimeTypes[] = {
63 { SkImageDecoder::kBMP_Format, "image/bmp" },
64 { SkImageDecoder::kGIF_Format, "image/gif" },
65 { SkImageDecoder::kICO_Format, "image/x-ico" },
66 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
67 { SkImageDecoder::kPNG_Format, "image/png" },
Chris Craik95587f92013-07-12 19:46:19 -070068 { SkImageDecoder::kWEBP_Format, "image/webp" },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
70 };
Elliott Hughesa3804cf2011-04-11 16:50:19 -070071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 const char* cstr = NULL;
73 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
74 if (gMimeTypes[i].fFormat == format) {
75 cstr = gMimeTypes[i].fMimeType;
76 break;
77 }
78 }
79
80 jstring jstr = 0;
81 if (NULL != cstr) {
82 jstr = env->NewStringUTF(cstr);
83 }
84 return jstr;
85}
86
Mike Reedc70e06b2009-04-24 11:09:12 -040087static bool optionsPurgeable(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -070088 return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
Mike Reedc70e06b2009-04-24 11:09:12 -040089}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
Mike Reedc70e06b2009-04-24 11:09:12 -040091static bool optionsShareable(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -070092 return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
Mike Reedc70e06b2009-04-24 11:09:12 -040093}
94
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +000095static bool optionsJustBounds(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -070096 return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +000097}
98
Romain Guy7b2f8b82012-03-19 17:18:54 -070099static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
100 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
101 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
102 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
103 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
104
105 for (int i = 0; i < chunk->numXDivs; i++) {
106 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
107 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
108 chunk->xDivs[i]++;
109 }
110 }
111
112 for (int i = 0; i < chunk->numYDivs; i++) {
113 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
114 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
115 chunk->yDivs[i]++;
116 }
117 }
118}
119
Mike Reedc70e06b2009-04-24 11:09:12 -0400120static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700121 int sampleSize, bool ditherImage) {
122
Mike Reed17154412009-09-24 12:35:27 -0400123 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400124 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700125 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400126 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
127 } else {
128 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
129 }
Mike Reed17154412009-09-24 12:35:27 -0400130 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400131 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700132 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400133 return pr;
134}
135
Chris Craik905e8242013-06-05 09:59:05 -0700136static SkBitmap::Config configForScaledOutput(SkBitmap::Config config) {
137 switch (config) {
138 case SkBitmap::kNo_Config:
139 case SkBitmap::kIndex8_Config:
140 case SkBitmap::kRLE_Index8_Config:
141 return SkBitmap::kARGB_8888_Config;
142 default:
143 break;
144 }
145 return config;
146}
147
148class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
149public:
150 ScaleCheckingAllocator(float scale, int size)
151 : mScale(scale), mSize(size) {
152 }
153
154 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
155 // accounts for scale in final allocation, using eventual size and config
156 const int bytesPerPixel = SkBitmap::ComputeBytesPerPixel(
157 configForScaledOutput(bitmap->getConfig()));
158 const int requestedSize = bytesPerPixel *
159 int(bitmap->width() * mScale + 0.5f) *
160 int(bitmap->height() * mScale + 0.5f);
161 if (requestedSize > mSize) {
162 ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
163 mSize, requestedSize);
164 return false;
165 }
166 return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
167 }
168private:
169 const float mScale;
170 const int mSize;
171};
172
Chris Craik7e8c03c2013-06-03 13:53:36 -0700173class RecyclingPixelAllocator : public SkBitmap::Allocator {
174public:
175 RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
Chris Craik905e8242013-06-05 09:59:05 -0700176 : mPixelRef(pixelRef), mSize(size) {
Chris Craik7e8c03c2013-06-03 13:53:36 -0700177 SkSafeRef(mPixelRef);
178 }
179
180 ~RecyclingPixelAllocator() {
181 SkSafeUnref(mPixelRef);
182 }
183
184 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
185 if (!bitmap->getSize64().is32() || bitmap->getSize() > mSize) {
Chris Craik905e8242013-06-05 09:59:05 -0700186 ALOGW("bitmap marked for reuse (%d bytes) can't fit new bitmap (%d bytes)",
187 mSize, bitmap->getSize());
Chris Craik7e8c03c2013-06-03 13:53:36 -0700188 return false;
189 }
190 bitmap->setPixelRef(mPixelRef);
191 bitmap->lockPixels();
192 return true;
193 }
194
195private:
196 SkPixelRef* const mPixelRef;
197 const unsigned int mSize;
198};
199
Mike Reedc70e06b2009-04-24 11:09:12 -0400200// since we "may" create a purgeable imageref, we require the stream be ref'able
201// i.e. dynamically allocated, since its lifetime may exceed the current stack
202// frame.
203static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
Chris Craik905e8242013-06-05 09:59:05 -0700204 jobject options, bool allowPurgeable, bool forcePurgeable = false) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 int sampleSize = 1;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
Romain Guy99b7b7a2011-07-14 12:42:19 -0700209 SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 bool doDither = true;
Romain Guy23610982011-01-17 12:51:55 -0800212 bool isMutable = false;
Chris Craik905e8242013-06-05 09:59:05 -0700213 float scale = 1.0f;
214 bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800215 bool preferQualityOverSpeed = false;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700216
Chet Haase37f74ca2010-12-08 17:56:36 -0800217 jobject javaBitmap = NULL;
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700218
Romain Guy7b2f8b82012-03-19 17:18:54 -0700219 if (options != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000221 if (optionsJustBounds(env, options)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 mode = SkImageDecoder::kDecodeBounds_Mode;
223 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 // initialize these, in case we fail later on
226 env->SetIntField(options, gOptions_widthFieldID, -1);
227 env->SetIntField(options, gOptions_heightFieldID, -1);
228 env->SetObjectField(options, gOptions_mimeFieldID, 0);
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
231 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
Romain Guy23610982011-01-17 12:51:55 -0800232 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800234 preferQualityOverSpeed = env->GetBooleanField(options,
235 gOptions_preferQualityOverSpeedFieldID);
Chet Haase37f74ca2010-12-08 17:56:36 -0800236 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
Chris Craik905e8242013-06-05 09:59:05 -0700237
238 if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
239 const int density = env->GetIntField(options, gOptions_densityFieldID);
240 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
241 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
242 if (density != 0 && targetDensity != 0 && density != screenDensity) {
243 scale = (float) targetDensity / density;
244 }
245 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 }
247
Chris Craik905e8242013-06-05 09:59:05 -0700248 const bool willScale = scale != 1.0f;
249 isPurgeable &= !willScale;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700252 if (decoder == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400253 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700255
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 decoder->setSampleSize(sampleSize);
257 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800258 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259
Chris Craik7e8c03c2013-06-03 13:53:36 -0700260 SkBitmap* outputBitmap = NULL;
Chris Craik9f583612013-05-20 18:13:47 -0700261 unsigned int existingBufferSize = 0;
Chris Craik7e8c03c2013-06-03 13:53:36 -0700262 if (javaBitmap != NULL) {
263 outputBitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
264 if (outputBitmap->isImmutable()) {
Derek Sollenberger2a6ecae2012-08-31 14:03:51 -0400265 ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
Chris Craik7e8c03c2013-06-03 13:53:36 -0700266 javaBitmap = NULL;
267 outputBitmap = NULL;
268 } else {
269 existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
Derek Sollenberger2a6ecae2012-08-31 14:03:51 -0400270 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800271 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272
Chris Craik7e8c03c2013-06-03 13:53:36 -0700273 SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
274 if (outputBitmap == NULL) outputBitmap = adb.get();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275
Chris Craik7e8c03c2013-06-03 13:53:36 -0700276 NinePatchPeeker peeker(decoder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400278
Chris Craik7e8c03c2013-06-03 13:53:36 -0700279 SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700280
Chris Craik905e8242013-06-05 09:59:05 -0700281 JavaPixelAllocator javaAllocator(env);
282 RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
283 ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
284 SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
285 (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
286 if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
287 if (!willScale) {
288 decoder->setAllocator(outputAllocator);
289 } else if (javaBitmap != NULL) {
290 // check for eventual scaled bounds at allocation time, so we don't decode the bitmap
291 // only to find the scaled result too large to fit in the allocation
292 decoder->setAllocator(&scaleCheckingAllocator);
293 }
294 }
295
Derek Sollenberger6b0437c2013-06-24 15:40:54 -0400296 // Only setup the decoder to be deleted after its stack-based, refcounted
297 // components (allocators, peekers, etc) are declared. This prevents RefCnt
298 // asserts from firing due to the order objects are deleted from the stack.
299 SkAutoTDelete<SkImageDecoder> add(decoder);
300
301 AutoDecoderCancel adc(options, decoder);
302
303 // To fix the race condition in case "requestCancelDecode"
304 // happens earlier than AutoDecoderCancel object is added
305 // to the gAutoDecoderCancelMutex linked list.
306 if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
307 return nullObjectReturn("gOptions_mCancelID");
308 }
309
Chris Craik7e8c03c2013-06-03 13:53:36 -0700310 SkBitmap decodingBitmap;
311 if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400312 return nullObjectReturn("decoder->decode returned false");
313 }
314
Chris Craik7e8c03c2013-06-03 13:53:36 -0700315 int scaledWidth = decodingBitmap.width();
316 int scaledHeight = decodingBitmap.height();
Romain Guy7b2f8b82012-03-19 17:18:54 -0700317
318 if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
319 scaledWidth = int(scaledWidth * scale + 0.5f);
320 scaledHeight = int(scaledHeight * scale + 0.5f);
321 }
322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 // update options (if any)
Romain Guy7b2f8b82012-03-19 17:18:54 -0700324 if (options != NULL) {
325 env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
326 env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 env->SetObjectField(options, gOptions_mimeFieldID,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700328 getMimeTypeString(env, decoder->getFormat()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400330
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 // if we're in justBounds mode, return now (skip the java bitmap)
Romain Guy7b2f8b82012-03-19 17:18:54 -0700332 if (mode == SkImageDecoder::kDecodeBounds_Mode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 return NULL;
334 }
335
336 jbyteArray ninePatchChunk = NULL;
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700337 if (peeker.fPatch != NULL) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700338 if (willScale) {
339 scaleNinePatchChunk(peeker.fPatch, scale);
340 }
341
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
343 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700344 if (ninePatchChunk == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400345 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700347
348 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
349 if (array == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400350 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 peeker.fPatch->serialize(array);
354 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
355 }
356
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700357 jintArray layoutBounds = NULL;
358 if (peeker.fLayoutBounds != NULL) {
359 layoutBounds = env->NewIntArray(4);
360 if (layoutBounds == NULL) {
361 return nullObjectReturn("layoutBounds == null");
362 }
363
Dianne Hackborn76344242012-04-30 14:05:09 -0700364 jint scaledBounds[4];
365 if (willScale) {
366 for (int i=0; i<4; i++) {
367 scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f);
368 }
369 } else {
370 memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds));
371 }
372 env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds);
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700373 if (javaBitmap != NULL) {
374 env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
375 }
376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377
Romain Guy7b2f8b82012-03-19 17:18:54 -0700378 if (willScale) {
379 // This is weird so let me explain: we could use the scale parameter
380 // directly, but for historical reasons this is how the corresponding
381 // Dalvik code has always behaved. We simply recreate the behavior here.
382 // The result is slightly different from simply using scale because of
383 // the 0.5f rounding bias applied when computing the target image size
Chris Craik7e8c03c2013-06-03 13:53:36 -0700384 const float sx = scaledWidth / float(decodingBitmap.width());
385 const float sy = scaledHeight / float(decodingBitmap.height());
Romain Guy7b2f8b82012-03-19 17:18:54 -0700386
Chris Craik905e8242013-06-05 09:59:05 -0700387 // TODO: avoid copying when scaled size equals decodingBitmap size
388 SkBitmap::Config config = configForScaledOutput(decodingBitmap.config());
Chris Craik7e8c03c2013-06-03 13:53:36 -0700389 outputBitmap->setConfig(config, scaledWidth, scaledHeight);
390 outputBitmap->setIsOpaque(decodingBitmap.isOpaque());
Chris Craik905e8242013-06-05 09:59:05 -0700391 if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
Raph Levien005bfc62012-09-20 22:51:47 -0700392 return nullObjectReturn("allocation failed for scaled bitmap");
393 }
Chris Craik7e8c03c2013-06-03 13:53:36 -0700394 outputBitmap->eraseColor(0);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700395
396 SkPaint paint;
397 paint.setFilterBitmap(true);
398
Chris Craik7e8c03c2013-06-03 13:53:36 -0700399 SkCanvas canvas(*outputBitmap);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700400 canvas.scale(sx, sy);
Chris Craik7e8c03c2013-06-03 13:53:36 -0700401 canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
402 } else {
403 outputBitmap->swap(decodingBitmap);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700404 }
405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 if (padding) {
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700407 if (peeker.fPatch != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 GraphicsJNI::set_jrect(env, padding,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700409 peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
410 peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 } else {
412 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
413 }
414 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415
Mike Reedc70e06b2009-04-24 11:09:12 -0400416 SkPixelRef* pr;
417 if (isPurgeable) {
Chris Craik7e8c03c2013-06-03 13:53:36 -0700418 pr = installPixelRef(outputBitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400419 } else {
420 // if we get here, we're in kDecodePixels_Mode and will therefore
421 // already have a pixelref installed.
Chris Craik7e8c03c2013-06-03 13:53:36 -0700422 pr = outputBitmap->pixelRef();
Mike Reedc70e06b2009-04-24 11:09:12 -0400423 }
Marco Nelissenb2fe3be2012-05-07 11:24:13 -0700424 if (pr == NULL) {
425 return nullObjectReturn("Got null SkPixelRef");
426 }
Romain Guy23610982011-01-17 12:51:55 -0800427
Chris Craik7e8c03c2013-06-03 13:53:36 -0700428 if (!isMutable && javaBitmap == NULL) {
Romain Guy23610982011-01-17 12:51:55 -0800429 // promise we will never change our pixels (great for sharing and pictures)
430 pr->setImmutable();
431 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800432
Jeff Brown27d83832012-05-09 17:30:31 -0700433 // detach bitmap from its autodeleter, since we want to own it now
434 adb.detach();
435
Chris Craik7e8c03c2013-06-03 13:53:36 -0700436 if (javaBitmap != NULL) {
437 GraphicsJNI::reinitBitmap(env, javaBitmap);
438 outputBitmap->notifyPixelsChanged();
Chet Haase37f74ca2010-12-08 17:56:36 -0800439 // If a java bitmap was passed in for reuse, pass it back
440 return javaBitmap;
441 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400442 // now create the java bitmap
Chris Craik7e8c03c2013-06-03 13:53:36 -0700443 return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700444 isMutable, ninePatchChunk, layoutBounds, -1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445}
446
Chris Craik905e8242013-06-05 09:59:05 -0700447static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
448 jobject padding, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 jobject bitmap = NULL;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800451 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452
453 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400454 // for now we don't allow purgeable with java inputstreams
Chris Craik905e8242013-06-05 09:59:05 -0700455 bitmap = doDecode(env, stream, padding, options, false, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 stream->unref();
457 }
458 return bitmap;
459}
460
461static ssize_t getFDSize(int fd) {
Kenny Rootddb76c42010-11-24 12:56:06 -0800462 off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 if (curr < 0) {
464 return 0;
465 }
466 size_t size = ::lseek(fd, 0, SEEK_END);
Kenny Rootddb76c42010-11-24 12:56:06 -0800467 ::lseek64(fd, curr, SEEK_SET);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 return size;
469}
470
Romain Guy7b2f8b82012-03-19 17:18:54 -0700471static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
472 jobject padding, jobject bitmapFactoryOptions) {
473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
475
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700476 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400477
478 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
479 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
480 bool weOwnTheFD = false;
481 if (isPurgeable && isShareable) {
482 int newFD = ::dup(descriptor);
483 if (-1 != newFD) {
484 weOwnTheFD = true;
485 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488
Mike Reedc70e06b2009-04-24 11:09:12 -0400489 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
490 SkAutoUnref aur(stream);
491 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 return NULL;
493 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400494
495 /* Restore our offset when we leave, so we can be called more than once
496 with the same descriptor. This is only required if we didn't dup the
497 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 */
499 AutoFDSeek as(descriptor);
500
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700501 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
502 shareable case.
503 */
504 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400505}
506
507/* make a deep copy of the asset, and return it as a stream, or NULL if there
508 was an error.
509 */
510static SkStream* copyAssetToStream(Asset* asset) {
511 // if we could "ref/reopen" the asset, we may not need to copy it here
Kenny Rootddb76c42010-11-24 12:56:06 -0800512 off64_t size = asset->seek(0, SEEK_SET);
513 if ((off64_t)-1 == size) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400514 SkDebugf("---- copyAsset: asset rewind failed\n");
515 return NULL;
516 }
517
518 size = asset->getLength();
519 if (size <= 0) {
520 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
521 return NULL;
522 }
523
524 SkStream* stream = new SkMemoryStream(size);
525 void* data = const_cast<void*>(stream->getMemoryBase());
Kenny Rootddb76c42010-11-24 12:56:06 -0800526 off64_t len = asset->read(data, size);
Mike Reedc70e06b2009-04-24 11:09:12 -0400527 if (len != size) {
528 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
529 delete stream;
530 stream = NULL;
531 }
532 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533}
534
Chris Craik905e8242013-06-05 09:59:05 -0700535static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset,
536 jobject padding, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700537
Mike Reedc70e06b2009-04-24 11:09:12 -0400538 SkStream* stream;
539 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
544 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
551 stream = new AssetStreamAdaptor(asset);
552 }
553 SkAutoUnref aur(stream);
Chris Craik905e8242013-06-05 09:59:05 -0700554 return doDecode(env, stream, padding, options, true, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555}
556
557static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700558 int offset, int length, jobject options) {
559
Mike Reedc70e06b2009-04-24 11:09:12 -0400560 /* If optionsShareable() we could decide to just wrap the java array and
561 share it, but that means adding a globalref to the java array object
562 and managing its lifetime. For now we just always copy the array's data
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000563 if optionsPurgeable(), unless we're just decoding bounds.
Mike Reedc70e06b2009-04-24 11:09:12 -0400564 */
Romain Guy7b2f8b82012-03-19 17:18:54 -0700565 bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
Mike Reedc70e06b2009-04-24 11:09:12 -0400566 AutoJavaByteArray ar(env, byteArray);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000567 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
Mike Reedc70e06b2009-04-24 11:09:12 -0400568 SkAutoUnref aur(stream);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000569 return doDecode(env, stream, NULL, options, purgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570}
571
572static void nativeRequestCancel(JNIEnv*, jobject joptions) {
573 (void)AutoDecoderCancel::RequestCancel(joptions);
574}
575
Owen Lina9d0d472011-01-18 17:39:15 +0800576static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700577 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Owen Lina9d0d472011-01-18 17:39:15 +0800578 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
579}
580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581///////////////////////////////////////////////////////////////////////////////
582
583static JNINativeMethod gMethods[] = {
584 { "nativeDecodeStream",
585 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
586 (void*)nativeDecodeStream
587 },
588
589 { "nativeDecodeFileDescriptor",
590 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
591 (void*)nativeDecodeFileDescriptor
592 },
593
594 { "nativeDecodeAsset",
595 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
596 (void*)nativeDecodeAsset
597 },
598
599 { "nativeDecodeByteArray",
600 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
601 (void*)nativeDecodeByteArray
602 },
603
Owen Lina9d0d472011-01-18 17:39:15 +0800604 { "nativeIsSeekable",
605 "(Ljava/io/FileDescriptor;)Z",
606 (void*)nativeIsSeekable
607 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608};
609
610static JNINativeMethod gOptionsMethods[] = {
611 { "requestCancel", "()V", (void*)nativeRequestCancel }
612};
613
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
615 const char fieldname[], const char type[]) {
616 jfieldID id = env->GetFieldID(clazz, fieldname, type);
617 SkASSERT(id);
618 return id;
619}
620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621int register_android_graphics_BitmapFactory(JNIEnv* env) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700622 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
623 SkASSERT(options_class);
624 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
Chet Haase37f74ca2010-12-08 17:56:36 -0800625 "Landroid/graphics/Bitmap;");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700626 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
627 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
628 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 "Landroid/graphics/Bitmap$Config;");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700630 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
631 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
632 gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
633 gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
634 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800635 "inPreferQualityOverSpeed", "Z");
Chris Craik905e8242013-06-05 09:59:05 -0700636 gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
637 gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
638 gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
639 gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700640 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
641 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
642 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
643 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700645 jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
646 SkASSERT(bitmap_class);
647 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700648 gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 int ret = AndroidRuntime::registerNativeMethods(env,
650 "android/graphics/BitmapFactory$Options",
651 gOptionsMethods,
652 SK_ARRAY_COUNT(gOptionsMethods));
653 if (ret) {
654 return ret;
655 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700656 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 gMethods, SK_ARRAY_COUNT(gMethods));
658}