blob: 928a7f84507b23733f99aa18bdf029f16d5fa429 [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"
Leon Scroggins46cb9bd2014-03-06 15:36:39 -050010#include "SkMath.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080011#include "SkPixelRef.h"
12#include "SkStream.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080013#include "SkTemplates.h"
14#include "SkUtils.h"
15#include "CreateJavaOutputStreamAdaptor.h"
Joseph Wenf1f48bc2010-07-19 16:59:51 +080016#include "AutoDecodeCancel.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080017#include "Utils.h"
Elliott Hughesa3804cf2011-04-11 16:50:19 -070018#include "JNIHelp.h"
Chris Craik1abf5d62013-08-16 12:47:03 -070019#include "GraphicsJNI.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020
21#include <android_runtime/AndroidRuntime.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080022#include <androidfw/Asset.h>
23#include <androidfw/ResourceTypes.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024#include <netinet/in.h>
Leon Scroggins III0102f8a2014-01-14 15:14:57 -050025#include <stdio.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026#include <sys/mman.h>
Joseph Wen2dcfbef2010-09-10 10:15:09 +080027#include <sys/stat.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028
Joseph Wenf1f48bc2010-07-19 16:59:51 +080029jfieldID gOptions_justBoundsFieldID;
30jfieldID gOptions_sampleSizeFieldID;
31jfieldID gOptions_configFieldID;
Chris Craik1abf5d62013-08-16 12:47:03 -070032jfieldID gOptions_premultipliedFieldID;
Romain Guy23610982011-01-17 12:51:55 -080033jfieldID gOptions_mutableFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080034jfieldID gOptions_ditherFieldID;
35jfieldID gOptions_purgeableFieldID;
36jfieldID gOptions_shareableFieldID;
Wei-Ta Chen953f9092010-12-03 14:06:18 -080037jfieldID gOptions_preferQualityOverSpeedFieldID;
Chris Craik905e8242013-06-05 09:59:05 -070038jfieldID gOptions_scaledFieldID;
39jfieldID gOptions_densityFieldID;
40jfieldID gOptions_screenDensityFieldID;
41jfieldID gOptions_targetDensityFieldID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +080042jfieldID gOptions_widthFieldID;
43jfieldID gOptions_heightFieldID;
44jfieldID gOptions_mimeFieldID;
45jfieldID gOptions_mCancelID;
Chet Haase37f74ca2010-12-08 17:56:36 -080046jfieldID gOptions_bitmapFieldID;
Chet Haase37f74ca2010-12-08 17:56:36 -080047jfieldID gBitmap_nativeBitmapFieldID;
Amith Yamasaniec4a5042012-04-04 10:27:15 -070048jfieldID gBitmap_layoutBoundsFieldID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050#if 0
51 #define TRACE_BITMAP(code) code
52#else
53 #define TRACE_BITMAP(code)
54#endif
55
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056using namespace android;
57
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058static inline int32_t validOrNeg1(bool isValid, int32_t value) {
59// return isValid ? value : -1;
60 SkASSERT((int)isValid == 0 || (int)isValid == 1);
61 return ((int32_t)isValid - 1) | value;
62}
63
Joseph Wenf1f48bc2010-07-19 16:59:51 +080064jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 static const struct {
66 SkImageDecoder::Format fFormat;
67 const char* fMimeType;
68 } gMimeTypes[] = {
69 { SkImageDecoder::kBMP_Format, "image/bmp" },
70 { SkImageDecoder::kGIF_Format, "image/gif" },
71 { SkImageDecoder::kICO_Format, "image/x-ico" },
72 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
73 { SkImageDecoder::kPNG_Format, "image/png" },
Chris Craik95587f92013-07-12 19:46:19 -070074 { SkImageDecoder::kWEBP_Format, "image/webp" },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
76 };
Elliott Hughesa3804cf2011-04-11 16:50:19 -070077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 const char* cstr = NULL;
79 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
80 if (gMimeTypes[i].fFormat == format) {
81 cstr = gMimeTypes[i].fMimeType;
82 break;
83 }
84 }
85
86 jstring jstr = 0;
87 if (NULL != cstr) {
88 jstr = env->NewStringUTF(cstr);
89 }
90 return jstr;
91}
92
Mike Reedc70e06b2009-04-24 11:09:12 -040093static bool optionsPurgeable(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -070094 return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
Mike Reedc70e06b2009-04-24 11:09:12 -040095}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096
Mike Reedc70e06b2009-04-24 11:09:12 -040097static bool optionsShareable(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -070098 return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
Mike Reedc70e06b2009-04-24 11:09:12 -040099}
100
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000101static bool optionsJustBounds(JNIEnv* env, jobject options) {
Romain Guycaf813f2012-03-15 18:57:48 -0700102 return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000103}
104
Romain Guy7b2f8b82012-03-19 17:18:54 -0700105static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
106 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
107 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
108 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
109 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
110
Narayan Kamath6381dd42014-03-03 17:12:03 +0000111 int32_t* xDivs = chunk->getXDivs();
Romain Guy7b2f8b82012-03-19 17:18:54 -0700112 for (int i = 0; i < chunk->numXDivs; i++) {
Narayan Kamath6381dd42014-03-03 17:12:03 +0000113 xDivs[i] = int32_t(xDivs[i] * scale + 0.5f);
114 if (i > 0 && xDivs[i] == xDivs[i - 1]) {
115 xDivs[i]++;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700116 }
117 }
118
Narayan Kamath42a51ae2014-03-11 16:50:30 +0000119 int32_t* yDivs = chunk->getYDivs();
Romain Guy7b2f8b82012-03-19 17:18:54 -0700120 for (int i = 0; i < chunk->numYDivs; i++) {
Narayan Kamath6381dd42014-03-03 17:12:03 +0000121 yDivs[i] = int32_t(yDivs[i] * scale + 0.5f);
122 if (i > 0 && yDivs[i] == yDivs[i - 1]) {
123 yDivs[i]++;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700124 }
125 }
126}
127
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400128static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700129 int sampleSize, bool ditherImage) {
130
Leon Scroggins III28408c22014-05-27 15:55:39 -0400131 if (kUnknown_SkColorType == bitmap->colorType()) {
Derek Sollenbergerb644a3b2014-01-17 15:45:10 -0500132 ALOGW("bitmap has unknown configuration so no memory has been allocated");
133 return NULL;
134 }
135
Mike Reed17154412009-09-24 12:35:27 -0400136 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400137 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700138 if (bitmap->getSize() >= 32 * 1024) {
Leon Scroggins III28408c22014-05-27 15:55:39 -0400139 pr = new SkImageRef_ashmem(bitmap->info(), stream, sampleSize);
Mike Reedc70e06b2009-04-24 11:09:12 -0400140 } else {
Leon Scroggins III28408c22014-05-27 15:55:39 -0400141 pr = new SkImageRef_GlobalPool(bitmap->info(), stream, sampleSize);
Mike Reedc70e06b2009-04-24 11:09:12 -0400142 }
Mike Reed17154412009-09-24 12:35:27 -0400143 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400144 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700145 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400146 return pr;
147}
148
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500149static SkColorType colorTypeForScaledOutput(SkColorType colorType) {
150 switch (colorType) {
151 case kUnknown_SkColorType:
152 case kIndex_8_SkColorType:
Leon Scrogginscc11f152014-03-31 16:52:13 -0400153 return kNative_8888_SkColorType;
Chris Craik905e8242013-06-05 09:59:05 -0700154 default:
155 break;
156 }
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500157 return colorType;
Chris Craik905e8242013-06-05 09:59:05 -0700158}
159
160class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
161public:
162 ScaleCheckingAllocator(float scale, int size)
163 : mScale(scale), mSize(size) {
164 }
165
166 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
167 // accounts for scale in final allocation, using eventual size and config
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500168 const int bytesPerPixel = SkColorTypeBytesPerPixel(
169 colorTypeForScaledOutput(bitmap->colorType()));
Chris Craik905e8242013-06-05 09:59:05 -0700170 const int requestedSize = bytesPerPixel *
171 int(bitmap->width() * mScale + 0.5f) *
172 int(bitmap->height() * mScale + 0.5f);
173 if (requestedSize > mSize) {
174 ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
175 mSize, requestedSize);
176 return false;
177 }
178 return SkBitmap::HeapAllocator::allocPixelRef(bitmap, ctable);
179 }
180private:
181 const float mScale;
182 const int mSize;
183};
184
Chris Craik7e8c03c2013-06-03 13:53:36 -0700185class RecyclingPixelAllocator : public SkBitmap::Allocator {
186public:
187 RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
Chris Craik905e8242013-06-05 09:59:05 -0700188 : mPixelRef(pixelRef), mSize(size) {
Chris Craik7e8c03c2013-06-03 13:53:36 -0700189 SkSafeRef(mPixelRef);
190 }
191
192 ~RecyclingPixelAllocator() {
193 SkSafeUnref(mPixelRef);
194 }
195
196 virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500197 const SkImageInfo& info = bitmap->info();
198 if (info.fColorType == kUnknown_SkColorType) {
199 ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
Chris Craik7e8c03c2013-06-03 13:53:36 -0700200 return false;
201 }
Chris Craikcd0ba712013-09-06 14:40:30 -0700202
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500203 const int64_t size64 = info.getSafeSize64(bitmap->rowBytes());
204 if (!sk_64_isS32(size64)) {
205 ALOGW("bitmap is too large");
206 return false;
207 }
208
209 const size_t size = sk_64_asS32(size64);
210 if (size > mSize) {
211 ALOGW("bitmap marked for reuse (%d bytes) can't fit new bitmap (%d bytes)",
212 mSize, size);
Derek Sollenbergerb644a3b2014-01-17 15:45:10 -0500213 return false;
214 }
215
Chris Craikcd0ba712013-09-06 14:40:30 -0700216 // Create a new pixelref with the new ctable that wraps the previous pixelref
Derek Sollenbergerb644a3b2014-01-17 15:45:10 -0500217 SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef),
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500218 info, bitmap->rowBytes(), ctable);
Chris Craikcd0ba712013-09-06 14:40:30 -0700219
220 bitmap->setPixelRef(pr)->unref();
221 // since we're already allocated, we lockPixels right away
222 // HeapAllocator/JavaPixelAllocator behaves this way too
Chris Craik7e8c03c2013-06-03 13:53:36 -0700223 bitmap->lockPixels();
224 return true;
225 }
226
227private:
228 SkPixelRef* const mPixelRef;
229 const unsigned int mSize;
230};
231
Mike Reedc70e06b2009-04-24 11:09:12 -0400232// since we "may" create a purgeable imageref, we require the stream be ref'able
233// i.e. dynamically allocated, since its lifetime may exceed the current stack
234// frame.
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400235static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
Chris Craik905e8242013-06-05 09:59:05 -0700236 jobject options, bool allowPurgeable, bool forcePurgeable = false) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 int sampleSize = 1;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700239
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
Romain Guy99b7b7a2011-07-14 12:42:19 -0700241 SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 bool doDither = true;
Romain Guy23610982011-01-17 12:51:55 -0800244 bool isMutable = false;
Chris Craik905e8242013-06-05 09:59:05 -0700245 float scale = 1.0f;
246 bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800247 bool preferQualityOverSpeed = false;
Chris Craik1abf5d62013-08-16 12:47:03 -0700248 bool requireUnpremultiplied = false;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700249
Chet Haase37f74ca2010-12-08 17:56:36 -0800250 jobject javaBitmap = NULL;
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700251
Romain Guy7b2f8b82012-03-19 17:18:54 -0700252 if (options != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000254 if (optionsJustBounds(env, options)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 mode = SkImageDecoder::kDecodeBounds_Mode;
256 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700257
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 // initialize these, in case we fail later on
259 env->SetIntField(options, gOptions_widthFieldID, -1);
260 env->SetIntField(options, gOptions_heightFieldID, -1);
261 env->SetObjectField(options, gOptions_mimeFieldID, 0);
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
264 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
Romain Guy23610982011-01-17 12:51:55 -0800265 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800267 preferQualityOverSpeed = env->GetBooleanField(options,
268 gOptions_preferQualityOverSpeedFieldID);
Chris Craik1abf5d62013-08-16 12:47:03 -0700269 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
Chet Haase37f74ca2010-12-08 17:56:36 -0800270 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
Chris Craik905e8242013-06-05 09:59:05 -0700271
272 if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
273 const int density = env->GetIntField(options, gOptions_densityFieldID);
274 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
275 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
276 if (density != 0 && targetDensity != 0 && density != screenDensity) {
277 scale = (float) targetDensity / density;
278 }
279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 }
281
Chris Craik905e8242013-06-05 09:59:05 -0700282 const bool willScale = scale != 1.0f;
283 isPurgeable &= !willScale;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700284
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700286 if (decoder == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400287 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 decoder->setSampleSize(sampleSize);
291 decoder->setDitherImage(doDither);
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800292 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
Chris Craik1abf5d62013-08-16 12:47:03 -0700293 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294
Chris Craik7e8c03c2013-06-03 13:53:36 -0700295 SkBitmap* outputBitmap = NULL;
Chris Craik9f583612013-05-20 18:13:47 -0700296 unsigned int existingBufferSize = 0;
Chris Craik7e8c03c2013-06-03 13:53:36 -0700297 if (javaBitmap != NULL) {
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000298 outputBitmap = (SkBitmap*) env->GetLongField(javaBitmap, gBitmap_nativeBitmapFieldID);
Chris Craik7e8c03c2013-06-03 13:53:36 -0700299 if (outputBitmap->isImmutable()) {
Derek Sollenberger2a6ecae2012-08-31 14:03:51 -0400300 ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
Chris Craik7e8c03c2013-06-03 13:53:36 -0700301 javaBitmap = NULL;
302 outputBitmap = NULL;
303 } else {
304 existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
Derek Sollenberger2a6ecae2012-08-31 14:03:51 -0400305 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307
Chris Craik7e8c03c2013-06-03 13:53:36 -0700308 SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
309 if (outputBitmap == NULL) outputBitmap = adb.get();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310
Chris Craik7e8c03c2013-06-03 13:53:36 -0700311 NinePatchPeeker peeker(decoder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400313
Chris Craik7e8c03c2013-06-03 13:53:36 -0700314 SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
Romain Guy7b2f8b82012-03-19 17:18:54 -0700315
Chris Craik905e8242013-06-05 09:59:05 -0700316 JavaPixelAllocator javaAllocator(env);
317 RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
318 ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
319 SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
320 (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
321 if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
322 if (!willScale) {
Leon Scroggins III1ffe7272013-09-19 11:34:06 -0400323 // If the java allocator is being used to allocate the pixel memory, the decoder
324 // need not write zeroes, since the memory is initialized to 0.
325 decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator);
Chris Craik905e8242013-06-05 09:59:05 -0700326 decoder->setAllocator(outputAllocator);
327 } else if (javaBitmap != NULL) {
328 // check for eventual scaled bounds at allocation time, so we don't decode the bitmap
329 // only to find the scaled result too large to fit in the allocation
330 decoder->setAllocator(&scaleCheckingAllocator);
331 }
332 }
333
Derek Sollenberger6b0437c2013-06-24 15:40:54 -0400334 // Only setup the decoder to be deleted after its stack-based, refcounted
335 // components (allocators, peekers, etc) are declared. This prevents RefCnt
336 // asserts from firing due to the order objects are deleted from the stack.
337 SkAutoTDelete<SkImageDecoder> add(decoder);
338
339 AutoDecoderCancel adc(options, decoder);
340
341 // To fix the race condition in case "requestCancelDecode"
342 // happens earlier than AutoDecoderCancel object is added
343 // to the gAutoDecoderCancelMutex linked list.
344 if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
345 return nullObjectReturn("gOptions_mCancelID");
346 }
347
Chris Craik7e8c03c2013-06-03 13:53:36 -0700348 SkBitmap decodingBitmap;
349 if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400350 return nullObjectReturn("decoder->decode returned false");
351 }
352
Chris Craik7e8c03c2013-06-03 13:53:36 -0700353 int scaledWidth = decodingBitmap.width();
354 int scaledHeight = decodingBitmap.height();
Romain Guy7b2f8b82012-03-19 17:18:54 -0700355
356 if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
357 scaledWidth = int(scaledWidth * scale + 0.5f);
358 scaledHeight = int(scaledHeight * scale + 0.5f);
359 }
360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 // update options (if any)
Romain Guy7b2f8b82012-03-19 17:18:54 -0700362 if (options != NULL) {
363 env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
364 env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 env->SetObjectField(options, gOptions_mimeFieldID,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700366 getMimeTypeString(env, decoder->getFormat()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 // if we're in justBounds mode, return now (skip the java bitmap)
Romain Guy7b2f8b82012-03-19 17:18:54 -0700370 if (mode == SkImageDecoder::kDecodeBounds_Mode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 return NULL;
372 }
373
374 jbyteArray ninePatchChunk = NULL;
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700375 if (peeker.fPatch != NULL) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700376 if (willScale) {
377 scaleNinePatchChunk(peeker.fPatch, scale);
378 }
379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
381 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700382 if (ninePatchChunk == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400383 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700385
386 jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
387 if (array == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400388 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700390
Narayan Kamath6381dd42014-03-03 17:12:03 +0000391 memcpy(array, peeker.fPatch, peeker.fPatchSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
393 }
394
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700395 jintArray layoutBounds = NULL;
396 if (peeker.fLayoutBounds != NULL) {
397 layoutBounds = env->NewIntArray(4);
398 if (layoutBounds == NULL) {
399 return nullObjectReturn("layoutBounds == null");
400 }
401
Dianne Hackborn76344242012-04-30 14:05:09 -0700402 jint scaledBounds[4];
403 if (willScale) {
404 for (int i=0; i<4; i++) {
405 scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f);
406 }
407 } else {
408 memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds));
409 }
410 env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds);
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700411 if (javaBitmap != NULL) {
412 env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
413 }
414 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415
Romain Guy7b2f8b82012-03-19 17:18:54 -0700416 if (willScale) {
417 // This is weird so let me explain: we could use the scale parameter
418 // directly, but for historical reasons this is how the corresponding
419 // Dalvik code has always behaved. We simply recreate the behavior here.
420 // The result is slightly different from simply using scale because of
421 // the 0.5f rounding bias applied when computing the target image size
Chris Craik7e8c03c2013-06-03 13:53:36 -0700422 const float sx = scaledWidth / float(decodingBitmap.width());
423 const float sy = scaledHeight / float(decodingBitmap.height());
Romain Guy7b2f8b82012-03-19 17:18:54 -0700424
Chris Craik905e8242013-06-05 09:59:05 -0700425 // TODO: avoid copying when scaled size equals decodingBitmap size
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500426 SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType());
Leon Scroggins III8790be62013-12-03 16:26:51 -0500427 // FIXME: If the alphaType is kUnpremul and the image has alpha, the
428 // colors may not be correct, since Skia does not yet support drawing
429 // to/from unpremultiplied bitmaps.
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500430 outputBitmap->setConfig(SkImageInfo::Make(scaledWidth, scaledHeight,
431 colorType, decodingBitmap.alphaType()));
Chris Craik905e8242013-06-05 09:59:05 -0700432 if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
Raph Levien005bfc62012-09-20 22:51:47 -0700433 return nullObjectReturn("allocation failed for scaled bitmap");
434 }
Leon Scroggins III1ffe7272013-09-19 11:34:06 -0400435
436 // If outputBitmap's pixels are newly allocated by Java, there is no need
437 // to erase to 0, since the pixels were initialized to 0.
438 if (outputAllocator != &javaAllocator) {
439 outputBitmap->eraseColor(0);
440 }
Romain Guy7b2f8b82012-03-19 17:18:54 -0700441
442 SkPaint paint;
Derek Sollenbergerb644a3b2014-01-17 15:45:10 -0500443 paint.setFilterLevel(SkPaint::kLow_FilterLevel);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700444
Chris Craik7e8c03c2013-06-03 13:53:36 -0700445 SkCanvas canvas(*outputBitmap);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700446 canvas.scale(sx, sy);
Chris Craik7e8c03c2013-06-03 13:53:36 -0700447 canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
448 } else {
449 outputBitmap->swap(decodingBitmap);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700450 }
451
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 if (padding) {
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700453 if (peeker.fPatch != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 GraphicsJNI::set_jrect(env, padding,
Romain Guy7b2f8b82012-03-19 17:18:54 -0700455 peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
456 peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 } else {
458 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
459 }
460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461
Mike Reedc70e06b2009-04-24 11:09:12 -0400462 SkPixelRef* pr;
463 if (isPurgeable) {
Chris Craik7e8c03c2013-06-03 13:53:36 -0700464 pr = installPixelRef(outputBitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400465 } else {
466 // if we get here, we're in kDecodePixels_Mode and will therefore
467 // already have a pixelref installed.
Chris Craik7e8c03c2013-06-03 13:53:36 -0700468 pr = outputBitmap->pixelRef();
Mike Reedc70e06b2009-04-24 11:09:12 -0400469 }
Marco Nelissenb2fe3be2012-05-07 11:24:13 -0700470 if (pr == NULL) {
471 return nullObjectReturn("Got null SkPixelRef");
472 }
Romain Guy23610982011-01-17 12:51:55 -0800473
Chris Craik7e8c03c2013-06-03 13:53:36 -0700474 if (!isMutable && javaBitmap == NULL) {
Romain Guy23610982011-01-17 12:51:55 -0800475 // promise we will never change our pixels (great for sharing and pictures)
476 pr->setImmutable();
477 }
Chet Haase37f74ca2010-12-08 17:56:36 -0800478
Jeff Brown27d83832012-05-09 17:30:31 -0700479 // detach bitmap from its autodeleter, since we want to own it now
480 adb.detach();
481
Chris Craik7e8c03c2013-06-03 13:53:36 -0700482 if (javaBitmap != NULL) {
Chris Craik1abf5d62013-08-16 12:47:03 -0700483 bool isPremultiplied = !requireUnpremultiplied;
484 GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied);
Chris Craik7e8c03c2013-06-03 13:53:36 -0700485 outputBitmap->notifyPixelsChanged();
Chet Haase37f74ca2010-12-08 17:56:36 -0800486 // If a java bitmap was passed in for reuse, pass it back
487 return javaBitmap;
488 }
Chris Craik1abf5d62013-08-16 12:47:03 -0700489
490 int bitmapCreateFlags = 0x0;
491 if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
492 if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
493
Mike Reedc70e06b2009-04-24 11:09:12 -0400494 // now create the java bitmap
Chris Craik7e8c03c2013-06-03 13:53:36 -0700495 return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
Chris Craik1abf5d62013-08-16 12:47:03 -0700496 bitmapCreateFlags, ninePatchChunk, layoutBounds, -1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497}
498
Leon Scroggins III2826e5f2014-02-05 19:46:02 -0500499// Need to buffer enough input to be able to rewind as much as might be read by a decoder
500// trying to determine the stream's format. Currently the most is 64, read by
501// SkImageDecoder_libwebp.
502// FIXME: Get this number from SkImageDecoder
503#define BYTES_TO_BUFFER 64
504
Chris Craik905e8242013-06-05 09:59:05 -0700505static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
506 jobject padding, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 jobject bitmap = NULL;
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400509 SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400511 if (stream.get()) {
Leon Scroggins III2826e5f2014-02-05 19:46:02 -0500512 SkAutoTUnref<SkStreamRewindable> bufferedStream(
513 SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER));
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400514 SkASSERT(bufferedStream.get() != NULL);
Mike Reedc70e06b2009-04-24 11:09:12 -0400515 // for now we don't allow purgeable with java inputstreams
Leon Scroggins III7315f1b2013-09-10 20:26:05 -0400516 bitmap = doDecode(env, bufferedStream, padding, options, false, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518 return bitmap;
519}
520
Romain Guy7b2f8b82012-03-19 17:18:54 -0700521static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
522 jobject padding, jobject bitmapFactoryOptions) {
523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
525
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700526 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400527
Derek Sollenberger5827cb52013-07-26 14:58:06 -0400528 struct stat fdStat;
529 if (fstat(descriptor, &fdStat) == -1) {
530 doThrowIOE(env, "broken file descriptor");
531 return nullObjectReturn("fstat return -1");
532 }
533
Leon Scroggins III2826e5f2014-02-05 19:46:02 -0500534 // Restore the descriptor's offset on exiting this function.
535 AutoFDSeek autoRestore(descriptor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536
Leon Scroggins III0102f8a2014-01-14 15:14:57 -0500537 FILE* file = fdopen(descriptor, "r");
538 if (file == NULL) {
539 return nullObjectReturn("Could not open file");
Leon Scroggins IIIf65183f2013-10-07 16:32:14 -0400540 }
Leon Scroggins III0102f8a2014-01-14 15:14:57 -0500541
Leon Scroggins III2826e5f2014-02-05 19:46:02 -0500542 SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,
Leon Scroggins III0102f8a2014-01-14 15:14:57 -0500543 SkFILEStream::kCallerRetains_Ownership));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544
Leon Scroggins III2826e5f2014-02-05 19:46:02 -0500545 SkAutoTUnref<SkStreamRewindable> stream;
546
547 // Retain the old behavior of allowing purgeable if both purgeable and
548 // shareable are set to true.
549 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions)
550 && optionsShareable(env, bitmapFactoryOptions);
551 if (isPurgeable) {
552 // Copy the stream, so the image can be decoded multiple times without
553 // continuing to modify the original file descriptor.
554 // Copy beginning from the current position.
555 const size_t fileSize = fileStream->getLength() - fileStream->getPosition();
556 void* buffer = sk_malloc_flags(fileSize, 0);
557 if (buffer == NULL) {
558 return nullObjectReturn("Could not make a copy for ashmem");
559 }
560
561 SkAutoTUnref<SkData> data(SkData::NewFromMalloc(buffer, fileSize));
562
563 if (fileStream->read(buffer, fileSize) != fileSize) {
564 return nullObjectReturn("Could not read the file.");
565 }
566
567 stream.reset(new SkMemoryStream(data));
568 } else {
569 // Use a buffered stream. Although an SkFILEStream can be rewound, this
570 // ensures that SkImageDecoder::Factory never rewinds beyond the
571 // current position of the file descriptor.
572 stream.reset(SkFrontBufferedStream::Create(fileStream, BYTES_TO_BUFFER));
573 }
574
575 return doDecode(env, stream, padding, bitmapFactoryOptions, isPurgeable);
Mike Reedc70e06b2009-04-24 11:09:12 -0400576}
577
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000578static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
Chris Craik905e8242013-06-05 09:59:05 -0700579 jobject padding, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700580
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400581 SkStreamRewindable* stream;
Mike Reedc70e06b2009-04-24 11:09:12 -0400582 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Romain Guy18ef37252010-08-19 15:10:54 -0700583 bool forcePurgeable = optionsPurgeable(env, options);
584 if (forcePurgeable) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400585 // if we could "ref/reopen" the asset, we may not need to copy it here
586 // and we could assume optionsShareable, since assets are always RO
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400587 stream = CopyAssetToStream(asset);
Romain Guy7b2f8b82012-03-19 17:18:54 -0700588 if (stream == NULL) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400589 return NULL;
590 }
591 } else {
592 // since we know we'll be done with the asset when we return, we can
593 // just use a simple wrapper
Leon Scroggins IIIb9c58ab2013-12-03 15:10:04 -0500594 stream = new AssetStreamAdaptor(asset,
595 AssetStreamAdaptor::kNo_OwnAsset,
596 AssetStreamAdaptor::kNo_HasMemoryBase);
Mike Reedc70e06b2009-04-24 11:09:12 -0400597 }
598 SkAutoUnref aur(stream);
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400599 return doDecode(env, stream, padding, options, forcePurgeable, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600}
601
602static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000603 jint offset, jint length, jobject options) {
Romain Guy7b2f8b82012-03-19 17:18:54 -0700604
Mike Reedc70e06b2009-04-24 11:09:12 -0400605 /* If optionsShareable() we could decide to just wrap the java array and
606 share it, but that means adding a globalref to the java array object
607 and managing its lifetime. For now we just always copy the array's data
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000608 if optionsPurgeable(), unless we're just decoding bounds.
Mike Reedc70e06b2009-04-24 11:09:12 -0400609 */
Romain Guy7b2f8b82012-03-19 17:18:54 -0700610 bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
Mike Reedc70e06b2009-04-24 11:09:12 -0400611 AutoJavaByteArray ar(env, byteArray);
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400612 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
Mike Reedc70e06b2009-04-24 11:09:12 -0400613 SkAutoUnref aur(stream);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000614 return doDecode(env, stream, NULL, options, purgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615}
616
617static void nativeRequestCancel(JNIEnv*, jobject joptions) {
618 (void)AutoDecoderCancel::RequestCancel(joptions);
619}
620
Owen Lina9d0d472011-01-18 17:39:15 +0800621static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700622 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
Owen Lina9d0d472011-01-18 17:39:15 +0800623 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
624}
625
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626///////////////////////////////////////////////////////////////////////////////
627
628static JNINativeMethod gMethods[] = {
629 { "nativeDecodeStream",
630 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
631 (void*)nativeDecodeStream
632 },
633
634 { "nativeDecodeFileDescriptor",
635 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
636 (void*)nativeDecodeFileDescriptor
637 },
638
639 { "nativeDecodeAsset",
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000640 "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 (void*)nativeDecodeAsset
642 },
643
644 { "nativeDecodeByteArray",
645 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
646 (void*)nativeDecodeByteArray
647 },
648
Owen Lina9d0d472011-01-18 17:39:15 +0800649 { "nativeIsSeekable",
650 "(Ljava/io/FileDescriptor;)Z",
651 (void*)nativeIsSeekable
652 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653};
654
655static JNINativeMethod gOptionsMethods[] = {
656 { "requestCancel", "()V", (void*)nativeRequestCancel }
657};
658
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
660 const char fieldname[], const char type[]) {
661 jfieldID id = env->GetFieldID(clazz, fieldname, type);
662 SkASSERT(id);
663 return id;
664}
665
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666int register_android_graphics_BitmapFactory(JNIEnv* env) {
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700667 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
668 SkASSERT(options_class);
669 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
Chet Haase37f74ca2010-12-08 17:56:36 -0800670 "Landroid/graphics/Bitmap;");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700671 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
672 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
673 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 "Landroid/graphics/Bitmap$Config;");
Chris Craik1abf5d62013-08-16 12:47:03 -0700675 gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700676 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
677 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
678 gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
679 gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
680 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
Wei-Ta Chen953f9092010-12-03 14:06:18 -0800681 "inPreferQualityOverSpeed", "Z");
Chris Craik905e8242013-06-05 09:59:05 -0700682 gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
683 gOptions_densityFieldID = getFieldIDCheck(env, options_class, "inDensity", "I");
684 gOptions_screenDensityFieldID = getFieldIDCheck(env, options_class, "inScreenDensity", "I");
685 gOptions_targetDensityFieldID = getFieldIDCheck(env, options_class, "inTargetDensity", "I");
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700686 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
687 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
688 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
689 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700691 jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
692 SkASSERT(bitmap_class);
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000693 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "J");
Amith Yamasaniec4a5042012-04-04 10:27:15 -0700694 gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 int ret = AndroidRuntime::registerNativeMethods(env,
696 "android/graphics/BitmapFactory$Options",
697 gOptionsMethods,
698 SK_ARRAY_COUNT(gOptionsMethods));
699 if (ret) {
700 return ret;
701 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700702 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 gMethods, SK_ARRAY_COUNT(gMethods));
704}