blob: 90a024387ed2ed2fb83db47a862ee2b487e43a61 [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"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004#include "SkImageDecoder.h"
Mike Reedc70e06b2009-04-24 11:09:12 -04005#include "SkImageRef_ashmem.h"
6#include "SkImageRef_GlobalPool.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007#include "SkPixelRef.h"
8#include "SkStream.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009#include "SkTemplates.h"
10#include "SkUtils.h"
11#include "CreateJavaOutputStreamAdaptor.h"
Joseph Wenf1f48bc2010-07-19 16:59:51 +080012#include "AutoDecodeCancel.h"
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080013#include "Utils.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080014
15#include <android_runtime/AndroidRuntime.h>
16#include <utils/Asset.h>
17#include <utils/ResourceTypes.h>
18#include <netinet/in.h>
19#include <sys/mman.h>
Joseph Wen2dcfbef2010-09-10 10:15:09 +080020#include <sys/stat.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021
Joseph Wenf1f48bc2010-07-19 16:59:51 +080022jclass gOptions_class;
23jfieldID gOptions_justBoundsFieldID;
24jfieldID gOptions_sampleSizeFieldID;
25jfieldID gOptions_configFieldID;
26jfieldID gOptions_ditherFieldID;
27jfieldID gOptions_purgeableFieldID;
28jfieldID gOptions_shareableFieldID;
29jfieldID gOptions_nativeAllocFieldID;
30jfieldID gOptions_widthFieldID;
31jfieldID gOptions_heightFieldID;
32jfieldID gOptions_mimeFieldID;
33jfieldID gOptions_mCancelID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034
35static jclass gFileDescriptor_class;
36static jfieldID gFileDescriptor_descriptor;
37
38#if 0
39 #define TRACE_BITMAP(code) code
40#else
41 #define TRACE_BITMAP(code)
42#endif
43
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044using namespace android;
45
46class NinePatchPeeker : public SkImageDecoder::Peeker {
Mike Reed39f10ec2010-03-24 10:08:50 -040047 SkImageDecoder* fHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048public:
Mike Reed39f10ec2010-03-24 10:08:50 -040049 NinePatchPeeker(SkImageDecoder* host) {
50 // the host lives longer than we do, so a raw ptr is safe
51 fHost = host;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 fPatchIsValid = false;
53 }
54
55 ~NinePatchPeeker() {
56 if (fPatchIsValid) {
57 free(fPatch);
58 }
59 }
60
61 bool fPatchIsValid;
62 Res_png_9patch* fPatch;
63
64 virtual bool peek(const char tag[], const void* data, size_t length) {
65 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
66 Res_png_9patch* patch = (Res_png_9patch*) data;
67 size_t patchSize = patch->serializedSize();
68 assert(length == patchSize);
69 // You have to copy the data because it is owned by the png reader
70 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
71 memcpy(patchNew, patch, patchSize);
72 // this relies on deserialization being done in place
73 Res_png_9patch::deserialize(patchNew);
74 patchNew->fileToDevice();
75 if (fPatchIsValid) {
76 free(fPatch);
77 }
78 fPatch = patchNew;
79 //printf("9patch: (%d,%d)-(%d,%d)\n",
80 // fPatch.sizeLeft, fPatch.sizeTop,
81 // fPatch.sizeRight, fPatch.sizeBottom);
82 fPatchIsValid = true;
Mike Reed39f10ec2010-03-24 10:08:50 -040083
84 // now update our host to force index or 32bit config
85 // 'cause we don't want 565 predithered, since as a 9patch, we know
86 // we will be stretched, and therefore we want to dither afterwards.
87 static const SkBitmap::Config gNo565Pref[] = {
88 SkBitmap::kIndex8_Config,
89 SkBitmap::kIndex8_Config,
90 SkBitmap::kARGB_8888_Config,
91 SkBitmap::kARGB_8888_Config,
92 SkBitmap::kARGB_8888_Config,
93 SkBitmap::kARGB_8888_Config,
94 };
95 fHost->setPrefConfigTable(gNo565Pref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 } else {
97 fPatch = NULL;
98 }
99 return true; // keep on decoding
100 }
101};
102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103///////////////////////////////////////////////////////////////////////////////
104
105static inline int32_t validOrNeg1(bool isValid, int32_t value) {
106// return isValid ? value : -1;
107 SkASSERT((int)isValid == 0 || (int)isValid == 1);
108 return ((int32_t)isValid - 1) | value;
109}
110
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800111jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 static const struct {
113 SkImageDecoder::Format fFormat;
114 const char* fMimeType;
115 } gMimeTypes[] = {
116 { SkImageDecoder::kBMP_Format, "image/bmp" },
117 { SkImageDecoder::kGIF_Format, "image/gif" },
118 { SkImageDecoder::kICO_Format, "image/x-ico" },
119 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
120 { SkImageDecoder::kPNG_Format, "image/png" },
121 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
122 };
123
124 const char* cstr = NULL;
125 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
126 if (gMimeTypes[i].fFormat == format) {
127 cstr = gMimeTypes[i].fMimeType;
128 break;
129 }
130 }
131
132 jstring jstr = 0;
133 if (NULL != cstr) {
134 jstr = env->NewStringUTF(cstr);
135 }
136 return jstr;
137}
138
Mike Reedc70e06b2009-04-24 11:09:12 -0400139static bool optionsPurgeable(JNIEnv* env, jobject options) {
140 return options != NULL &&
141 env->GetBooleanField(options, gOptions_purgeableFieldID);
142}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
Mike Reedc70e06b2009-04-24 11:09:12 -0400144static bool optionsShareable(JNIEnv* env, jobject options) {
145 return options != NULL &&
146 env->GetBooleanField(options, gOptions_shareableFieldID);
147}
148
Mike Reed1b22b972009-07-17 11:21:47 -0400149static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
150 return NULL == options ||
151 !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
152}
153
Mike Reedc70e06b2009-04-24 11:09:12 -0400154
155static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
Mike Reed17154412009-09-24 12:35:27 -0400156 int sampleSize, bool ditherImage) {
157 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400158 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700159 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400160 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
161 } else {
162 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
163 }
Mike Reed17154412009-09-24 12:35:27 -0400164 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400165 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700166 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400167 return pr;
168}
169
170// since we "may" create a purgeable imageref, we require the stream be ref'able
171// i.e. dynamically allocated, since its lifetime may exceed the current stack
172// frame.
173static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
Mike Reed36ad54a2010-03-10 10:28:10 -0500174 jobject options, bool allowPurgeable,
175 bool forcePurgeable = false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 int sampleSize = 1;
177 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
178 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
179 bool doDither = true;
Mike Reed36ad54a2010-03-10 10:28:10 -0500180 bool isPurgeable = forcePurgeable ||
181 (allowPurgeable && optionsPurgeable(env, options));
Mike Reed1b22b972009-07-17 11:21:47 -0400182 bool reportSizeToVM = optionsReportSizeToVM(env, options);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183
184 if (NULL != options) {
185 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
186 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
187 mode = SkImageDecoder::kDecodeBounds_Mode;
188 }
189 // initialize these, in case we fail later on
190 env->SetIntField(options, gOptions_widthFieldID, -1);
191 env->SetIntField(options, gOptions_heightFieldID, -1);
192 env->SetObjectField(options, gOptions_mimeFieldID, 0);
193
194 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
195 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
196 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
197 }
198
199 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
200 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400201 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203
204 decoder->setSampleSize(sampleSize);
205 decoder->setDitherImage(doDither);
206
Mike Reed39f10ec2010-03-24 10:08:50 -0400207 NinePatchPeeker peeker(decoder);
Mike Reed1b22b972009-07-17 11:21:47 -0400208 JavaPixelAllocator javaAllocator(env, reportSizeToVM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 SkBitmap* bitmap = new SkBitmap;
210 Res_png_9patch dummy9Patch;
211
212 SkAutoTDelete<SkImageDecoder> add(decoder);
213 SkAutoTDelete<SkBitmap> adb(bitmap);
214
215 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400216 if (!isPurgeable) {
217 decoder->setAllocator(&javaAllocator);
218 }
219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 AutoDecoderCancel adc(options, decoder);
221
Ray Chen0a6a0e92009-04-02 01:31:49 -0700222 // To fix the race condition in case "requestCancelDecode"
223 // happens earlier than AutoDecoderCancel object is added
224 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400225 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
226 return nullObjectReturn("gOptions_mCancelID");;
Ray Chen0a6a0e92009-04-02 01:31:49 -0700227 }
228
Mike Reedc70e06b2009-04-24 11:09:12 -0400229 SkImageDecoder::Mode decodeMode = mode;
230 if (isPurgeable) {
231 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400233 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
234 return nullObjectReturn("decoder->decode returned false");
235 }
236
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 // update options (if any)
238 if (NULL != options) {
239 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
240 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
241 // TODO: set the mimeType field with the data from the codec.
242 // but how to reuse a set of strings, rather than allocating new one
243 // each time?
244 env->SetObjectField(options, gOptions_mimeFieldID,
245 getMimeTypeString(env, decoder->getFormat()));
246 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 // if we're in justBounds mode, return now (skip the java bitmap)
249 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
250 return NULL;
251 }
252
253 jbyteArray ninePatchChunk = NULL;
254 if (peeker.fPatchIsValid) {
255 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
256 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
257 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400258 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 }
260 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
261 NULL);
262 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400263 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 }
265 peeker.fPatch->serialize(array);
266 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
267 }
268
269 // detach bitmap from its autotdeleter, since we want to own it now
270 adb.detach();
271
272 if (padding) {
273 if (peeker.fPatchIsValid) {
274 GraphicsJNI::set_jrect(env, padding,
275 peeker.fPatch->paddingLeft,
276 peeker.fPatch->paddingTop,
277 peeker.fPatch->paddingRight,
278 peeker.fPatch->paddingBottom);
279 } else {
280 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
281 }
282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283
Mike Reedc70e06b2009-04-24 11:09:12 -0400284 SkPixelRef* pr;
285 if (isPurgeable) {
Mike Reed17154412009-09-24 12:35:27 -0400286 pr = installPixelRef(bitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400287 } else {
288 // if we get here, we're in kDecodePixels_Mode and will therefore
289 // already have a pixelref installed.
290 pr = bitmap->pixelRef();
291 }
292 // promise we will never change our pixels (great for sharing and pictures)
293 pr->setImmutable();
294 // now create the java bitmap
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
296}
297
298static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
299 jobject is, // InputStream
300 jbyteArray storage, // byte[]
301 jobject padding,
302 jobject options) { // BitmapFactory$Options
303 jobject bitmap = NULL;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800304 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305
306 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400307 // for now we don't allow purgeable with java inputstreams
308 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 stream->unref();
310 }
311 return bitmap;
312}
313
314static ssize_t getFDSize(int fd) {
315 off_t curr = ::lseek(fd, 0, SEEK_CUR);
316 if (curr < 0) {
317 return 0;
318 }
319 size_t size = ::lseek(fd, 0, SEEK_END);
320 ::lseek(fd, curr, SEEK_SET);
321 return size;
322}
323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
325 jobject fileDescriptor,
326 jobject padding,
327 jobject bitmapFactoryOptions) {
328 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
329
330 jint descriptor = env->GetIntField(fileDescriptor,
331 gFileDescriptor_descriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400332
333 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
334 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
335 bool weOwnTheFD = false;
336 if (isPurgeable && isShareable) {
337 int newFD = ::dup(descriptor);
338 if (-1 != newFD) {
339 weOwnTheFD = true;
340 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343
Mike Reedc70e06b2009-04-24 11:09:12 -0400344 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
345 SkAutoUnref aur(stream);
346 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 return NULL;
348 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400349
350 /* Restore our offset when we leave, so we can be called more than once
351 with the same descriptor. This is only required if we didn't dup the
352 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 */
354 AutoFDSeek as(descriptor);
355
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700356 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
357 shareable case.
358 */
359 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400360}
361
362/* make a deep copy of the asset, and return it as a stream, or NULL if there
363 was an error.
364 */
365static SkStream* copyAssetToStream(Asset* asset) {
366 // if we could "ref/reopen" the asset, we may not need to copy it here
367 off_t size = asset->seek(0, SEEK_SET);
368 if ((off_t)-1 == size) {
369 SkDebugf("---- copyAsset: asset rewind failed\n");
370 return NULL;
371 }
372
373 size = asset->getLength();
374 if (size <= 0) {
375 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
376 return NULL;
377 }
378
379 SkStream* stream = new SkMemoryStream(size);
380 void* data = const_cast<void*>(stream->getMemoryBase());
381 off_t len = asset->read(data, size);
382 if (len != size) {
383 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
384 delete stream;
385 stream = NULL;
386 }
387 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388}
389
390static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
391 jint native_asset, // Asset
392 jobject padding, // Rect
393 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400394 SkStream* stream;
395 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Romain Guy18ef37252010-08-19 15:10:54 -0700396 bool forcePurgeable = optionsPurgeable(env, options);
397 if (forcePurgeable) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400398 // if we could "ref/reopen" the asset, we may not need to copy it here
399 // and we could assume optionsShareable, since assets are always RO
400 stream = copyAssetToStream(asset);
401 if (NULL == stream) {
402 return NULL;
403 }
404 } else {
405 // since we know we'll be done with the asset when we return, we can
406 // just use a simple wrapper
407 stream = new AssetStreamAdaptor(asset);
408 }
409 SkAutoUnref aur(stream);
Mike Reed36ad54a2010-03-10 10:28:10 -0500410 return doDecode(env, stream, padding, options, true, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411}
412
413static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
414 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400415 /* If optionsShareable() we could decide to just wrap the java array and
416 share it, but that means adding a globalref to the java array object
417 and managing its lifetime. For now we just always copy the array's data
418 if optionsPurgeable().
419 */
420 AutoJavaByteArray ar(env, byteArray);
421 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
422 optionsPurgeable(env, options));
423 SkAutoUnref aur(stream);
424 return doDecode(env, stream, NULL, options, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425}
426
427static void nativeRequestCancel(JNIEnv*, jobject joptions) {
428 (void)AutoDecoderCancel::RequestCancel(joptions);
429}
430
431static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
432 jobject padding) {
433
434 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
435 if (array != NULL) {
436 size_t chunkSize = env->GetArrayLength(chunkObject);
437 void* storage = alloca(chunkSize);
438 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
439 memcpy(chunk, array, chunkSize);
440 android::Res_png_9patch::deserialize(chunk);
441
442 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
443 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
444 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
445 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
446
447 for (int i = 0; i < chunk->numXDivs; i++) {
448 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
449 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
450 chunk->xDivs[i]++;
451 }
452 }
453
454 for (int i = 0; i < chunk->numYDivs; i++) {
455 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
456 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
457 chunk->yDivs[i]++;
458 }
459 }
460
461 memcpy(array, chunk, chunkSize);
462
463 if (padding) {
464 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
465 chunk->paddingRight, chunk->paddingBottom);
466 }
467
468 env->ReleaseByteArrayElements(chunkObject, array, 0);
469 }
470 return chunkObject;
471}
472
Mike Reedab4a0c12010-01-26 10:13:53 -0500473static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
474 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
475
476 // these are the only default configs that make sense for codecs right now
477 static const SkBitmap::Config gValidDefConfig[] = {
478 SkBitmap::kRGB_565_Config,
479 SkBitmap::kARGB_8888_Config,
480 };
481
482 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
483 if (config == gValidDefConfig[i]) {
484 SkImageDecoder::SetDeviceConfig(config);
485 break;
486 }
487 }
488}
489
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490///////////////////////////////////////////////////////////////////////////////
491
492static JNINativeMethod gMethods[] = {
493 { "nativeDecodeStream",
494 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
495 (void*)nativeDecodeStream
496 },
497
498 { "nativeDecodeFileDescriptor",
499 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
500 (void*)nativeDecodeFileDescriptor
501 },
502
503 { "nativeDecodeAsset",
504 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
505 (void*)nativeDecodeAsset
506 },
507
508 { "nativeDecodeByteArray",
509 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
510 (void*)nativeDecodeByteArray
511 },
512
513 { "nativeScaleNinePatch",
514 "([BFLandroid/graphics/Rect;)[B",
515 (void*)nativeScaleNinePatch
Mike Reedab4a0c12010-01-26 10:13:53 -0500516 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517
Mike Reedab4a0c12010-01-26 10:13:53 -0500518 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519};
520
521static JNINativeMethod gOptionsMethods[] = {
522 { "requestCancel", "()V", (void*)nativeRequestCancel }
523};
524
525static jclass make_globalref(JNIEnv* env, const char classname[]) {
526 jclass c = env->FindClass(classname);
527 SkASSERT(c);
528 return (jclass)env->NewGlobalRef(c);
529}
530
531static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
532 const char fieldname[], const char type[]) {
533 jfieldID id = env->GetFieldID(clazz, fieldname, type);
534 SkASSERT(id);
535 return id;
536}
537
538#define kClassPathName "android/graphics/BitmapFactory"
539
540#define RETURN_ERR_IF_NULL(value) \
541 do { if (!(value)) { assert(0); return -1; } } while (false)
542
543int register_android_graphics_BitmapFactory(JNIEnv* env);
544int register_android_graphics_BitmapFactory(JNIEnv* env) {
545 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
546 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
547 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
548 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
549 "Landroid/graphics/Bitmap$Config;");
550 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
Mike Reedc70e06b2009-04-24 11:09:12 -0400551 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
552 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
Mike Reed1b22b972009-07-17 11:21:47 -0400553 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
555 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
556 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700557 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558
559 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
560 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
561
562 int ret = AndroidRuntime::registerNativeMethods(env,
563 "android/graphics/BitmapFactory$Options",
564 gOptionsMethods,
565 SK_ARRAY_COUNT(gOptionsMethods));
566 if (ret) {
567 return ret;
568 }
569 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
570 gMethods, SK_ARRAY_COUNT(gMethods));
571}