blob: 3b2406ce40d47d77de2ef6dd6d9d0dee2f73df81 [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
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000149static bool optionsJustBounds(JNIEnv* env, jobject options) {
150 return options != NULL &&
151 env->GetBooleanField(options, gOptions_justBoundsFieldID);
152}
153
Mike Reed1b22b972009-07-17 11:21:47 -0400154static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
155 return NULL == options ||
156 !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
157}
158
Mike Reedc70e06b2009-04-24 11:09:12 -0400159
160static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
Mike Reed17154412009-09-24 12:35:27 -0400161 int sampleSize, bool ditherImage) {
162 SkImageRef* pr;
Mike Reedc70e06b2009-04-24 11:09:12 -0400163 // only use ashmem for large images, since mmaps come at a price
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700164 if (bitmap->getSize() >= 32 * 1024) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400165 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
166 } else {
167 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
168 }
Mike Reed17154412009-09-24 12:35:27 -0400169 pr->setDitherImage(ditherImage);
Mike Reedc70e06b2009-04-24 11:09:12 -0400170 bitmap->setPixelRef(pr)->unref();
Romain Guy288471d2010-08-19 14:41:16 -0700171 pr->isOpaque(bitmap);
Mike Reedc70e06b2009-04-24 11:09:12 -0400172 return pr;
173}
174
175// since we "may" create a purgeable imageref, we require the stream be ref'able
176// i.e. dynamically allocated, since its lifetime may exceed the current stack
177// frame.
178static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
Mike Reed36ad54a2010-03-10 10:28:10 -0500179 jobject options, bool allowPurgeable,
180 bool forcePurgeable = false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 int sampleSize = 1;
182 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
183 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
184 bool doDither = true;
Mike Reed36ad54a2010-03-10 10:28:10 -0500185 bool isPurgeable = forcePurgeable ||
186 (allowPurgeable && optionsPurgeable(env, options));
Mike Reed1b22b972009-07-17 11:21:47 -0400187 bool reportSizeToVM = optionsReportSizeToVM(env, options);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188
189 if (NULL != options) {
190 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000191 if (optionsJustBounds(env, options)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 mode = SkImageDecoder::kDecodeBounds_Mode;
193 }
194 // initialize these, in case we fail later on
195 env->SetIntField(options, gOptions_widthFieldID, -1);
196 env->SetIntField(options, gOptions_heightFieldID, -1);
197 env->SetObjectField(options, gOptions_mimeFieldID, 0);
198
199 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
200 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
201 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
202 }
203
204 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
205 if (NULL == decoder) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400206 return nullObjectReturn("SkImageDecoder::Factory returned null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
208
209 decoder->setSampleSize(sampleSize);
210 decoder->setDitherImage(doDither);
211
Mike Reed39f10ec2010-03-24 10:08:50 -0400212 NinePatchPeeker peeker(decoder);
Mike Reed1b22b972009-07-17 11:21:47 -0400213 JavaPixelAllocator javaAllocator(env, reportSizeToVM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 SkBitmap* bitmap = new SkBitmap;
215 Res_png_9patch dummy9Patch;
216
217 SkAutoTDelete<SkImageDecoder> add(decoder);
218 SkAutoTDelete<SkBitmap> adb(bitmap);
219
220 decoder->setPeeker(&peeker);
Mike Reedc70e06b2009-04-24 11:09:12 -0400221 if (!isPurgeable) {
222 decoder->setAllocator(&javaAllocator);
223 }
224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 AutoDecoderCancel adc(options, decoder);
226
Ray Chen0a6a0e92009-04-02 01:31:49 -0700227 // To fix the race condition in case "requestCancelDecode"
228 // happens earlier than AutoDecoderCancel object is added
229 // to the gAutoDecoderCancelMutex linked list.
Mike Reedc70e06b2009-04-24 11:09:12 -0400230 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
231 return nullObjectReturn("gOptions_mCancelID");;
Ray Chen0a6a0e92009-04-02 01:31:49 -0700232 }
233
Mike Reedc70e06b2009-04-24 11:09:12 -0400234 SkImageDecoder::Mode decodeMode = mode;
235 if (isPurgeable) {
236 decodeMode = SkImageDecoder::kDecodeBounds_Mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400238 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
239 return nullObjectReturn("decoder->decode returned false");
240 }
241
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 // update options (if any)
243 if (NULL != options) {
244 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
245 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
246 // TODO: set the mimeType field with the data from the codec.
247 // but how to reuse a set of strings, rather than allocating new one
248 // each time?
249 env->SetObjectField(options, gOptions_mimeFieldID,
250 getMimeTypeString(env, decoder->getFormat()));
251 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400252
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 // if we're in justBounds mode, return now (skip the java bitmap)
254 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
255 return NULL;
256 }
257
258 jbyteArray ninePatchChunk = NULL;
259 if (peeker.fPatchIsValid) {
260 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
261 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
262 if (NULL == ninePatchChunk) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400263 return nullObjectReturn("ninePatchChunk == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 }
265 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
266 NULL);
267 if (NULL == array) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400268 return nullObjectReturn("primitive array == null");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 }
270 peeker.fPatch->serialize(array);
271 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
272 }
273
274 // detach bitmap from its autotdeleter, since we want to own it now
275 adb.detach();
276
277 if (padding) {
278 if (peeker.fPatchIsValid) {
279 GraphicsJNI::set_jrect(env, padding,
280 peeker.fPatch->paddingLeft,
281 peeker.fPatch->paddingTop,
282 peeker.fPatch->paddingRight,
283 peeker.fPatch->paddingBottom);
284 } else {
285 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
286 }
287 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288
Mike Reedc70e06b2009-04-24 11:09:12 -0400289 SkPixelRef* pr;
290 if (isPurgeable) {
Mike Reed17154412009-09-24 12:35:27 -0400291 pr = installPixelRef(bitmap, stream, sampleSize, doDither);
Mike Reedc70e06b2009-04-24 11:09:12 -0400292 } else {
293 // if we get here, we're in kDecodePixels_Mode and will therefore
294 // already have a pixelref installed.
295 pr = bitmap->pixelRef();
296 }
297 // promise we will never change our pixels (great for sharing and pictures)
298 pr->setImmutable();
299 // now create the java bitmap
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
301}
302
303static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
304 jobject is, // InputStream
305 jbyteArray storage, // byte[]
306 jobject padding,
307 jobject options) { // BitmapFactory$Options
308 jobject bitmap = NULL;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800309 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310
311 if (stream) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400312 // for now we don't allow purgeable with java inputstreams
313 bitmap = doDecode(env, stream, padding, options, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 stream->unref();
315 }
316 return bitmap;
317}
318
319static ssize_t getFDSize(int fd) {
320 off_t curr = ::lseek(fd, 0, SEEK_CUR);
321 if (curr < 0) {
322 return 0;
323 }
324 size_t size = ::lseek(fd, 0, SEEK_END);
325 ::lseek(fd, curr, SEEK_SET);
326 return size;
327}
328
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
330 jobject fileDescriptor,
331 jobject padding,
332 jobject bitmapFactoryOptions) {
333 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
334
335 jint descriptor = env->GetIntField(fileDescriptor,
336 gFileDescriptor_descriptor);
Mike Reedc70e06b2009-04-24 11:09:12 -0400337
338 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
339 bool isShareable = optionsShareable(env, bitmapFactoryOptions);
340 bool weOwnTheFD = false;
341 if (isPurgeable && isShareable) {
342 int newFD = ::dup(descriptor);
343 if (-1 != newFD) {
344 weOwnTheFD = true;
345 descriptor = newFD;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
347 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348
Mike Reedc70e06b2009-04-24 11:09:12 -0400349 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
350 SkAutoUnref aur(stream);
351 if (!stream->isValid()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 return NULL;
353 }
Mike Reedc70e06b2009-04-24 11:09:12 -0400354
355 /* Restore our offset when we leave, so we can be called more than once
356 with the same descriptor. This is only required if we didn't dup the
357 file descriptor, but it is OK to do it all the time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 */
359 AutoFDSeek as(descriptor);
360
Wei-Ta Chen2a2c5cd2009-06-03 14:08:04 -0700361 /* Allow purgeable iff we own the FD, i.e., in the puregeable and
362 shareable case.
363 */
364 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
Mike Reedc70e06b2009-04-24 11:09:12 -0400365}
366
367/* make a deep copy of the asset, and return it as a stream, or NULL if there
368 was an error.
369 */
370static SkStream* copyAssetToStream(Asset* asset) {
371 // if we could "ref/reopen" the asset, we may not need to copy it here
372 off_t size = asset->seek(0, SEEK_SET);
373 if ((off_t)-1 == size) {
374 SkDebugf("---- copyAsset: asset rewind failed\n");
375 return NULL;
376 }
377
378 size = asset->getLength();
379 if (size <= 0) {
380 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
381 return NULL;
382 }
383
384 SkStream* stream = new SkMemoryStream(size);
385 void* data = const_cast<void*>(stream->getMemoryBase());
386 off_t len = asset->read(data, size);
387 if (len != size) {
388 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
389 delete stream;
390 stream = NULL;
391 }
392 return stream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393}
394
395static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
396 jint native_asset, // Asset
397 jobject padding, // Rect
398 jobject options) { // BitmapFactory$Options
Mike Reedc70e06b2009-04-24 11:09:12 -0400399 SkStream* stream;
400 Asset* asset = reinterpret_cast<Asset*>(native_asset);
Romain Guy18ef37252010-08-19 15:10:54 -0700401 bool forcePurgeable = optionsPurgeable(env, options);
402 if (forcePurgeable) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400403 // if we could "ref/reopen" the asset, we may not need to copy it here
404 // and we could assume optionsShareable, since assets are always RO
405 stream = copyAssetToStream(asset);
406 if (NULL == stream) {
407 return NULL;
408 }
409 } else {
410 // since we know we'll be done with the asset when we return, we can
411 // just use a simple wrapper
412 stream = new AssetStreamAdaptor(asset);
413 }
414 SkAutoUnref aur(stream);
Mike Reed36ad54a2010-03-10 10:28:10 -0500415 return doDecode(env, stream, padding, options, true, forcePurgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416}
417
418static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
419 int offset, int length, jobject options) {
Mike Reedc70e06b2009-04-24 11:09:12 -0400420 /* If optionsShareable() we could decide to just wrap the java array and
421 share it, but that means adding a globalref to the java array object
422 and managing its lifetime. For now we just always copy the array's data
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000423 if optionsPurgeable(), unless we're just decoding bounds.
Mike Reedc70e06b2009-04-24 11:09:12 -0400424 */
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000425 bool purgeable = optionsPurgeable(env, options)
426 && !optionsJustBounds(env, options);
Mike Reedc70e06b2009-04-24 11:09:12 -0400427 AutoJavaByteArray ar(env, byteArray);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000428 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
Mike Reedc70e06b2009-04-24 11:09:12 -0400429 SkAutoUnref aur(stream);
Bryan Mawhinney2a3d7542010-11-03 17:23:57 +0000430 return doDecode(env, stream, NULL, options, purgeable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431}
432
433static void nativeRequestCancel(JNIEnv*, jobject joptions) {
434 (void)AutoDecoderCancel::RequestCancel(joptions);
435}
436
437static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
438 jobject padding) {
439
440 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
441 if (array != NULL) {
442 size_t chunkSize = env->GetArrayLength(chunkObject);
443 void* storage = alloca(chunkSize);
444 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
445 memcpy(chunk, array, chunkSize);
446 android::Res_png_9patch::deserialize(chunk);
447
448 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
449 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
450 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
451 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
452
453 for (int i = 0; i < chunk->numXDivs; i++) {
454 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
455 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
456 chunk->xDivs[i]++;
457 }
458 }
459
460 for (int i = 0; i < chunk->numYDivs; i++) {
461 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
462 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
463 chunk->yDivs[i]++;
464 }
465 }
466
467 memcpy(array, chunk, chunkSize);
468
469 if (padding) {
470 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
471 chunk->paddingRight, chunk->paddingBottom);
472 }
473
474 env->ReleaseByteArrayElements(chunkObject, array, 0);
475 }
476 return chunkObject;
477}
478
Mike Reedab4a0c12010-01-26 10:13:53 -0500479static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
480 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig);
481
482 // these are the only default configs that make sense for codecs right now
483 static const SkBitmap::Config gValidDefConfig[] = {
484 SkBitmap::kRGB_565_Config,
485 SkBitmap::kARGB_8888_Config,
486 };
487
488 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) {
489 if (config == gValidDefConfig[i]) {
490 SkImageDecoder::SetDeviceConfig(config);
491 break;
492 }
493 }
494}
495
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496///////////////////////////////////////////////////////////////////////////////
497
498static JNINativeMethod gMethods[] = {
499 { "nativeDecodeStream",
500 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
501 (void*)nativeDecodeStream
502 },
503
504 { "nativeDecodeFileDescriptor",
505 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
506 (void*)nativeDecodeFileDescriptor
507 },
508
509 { "nativeDecodeAsset",
510 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
511 (void*)nativeDecodeAsset
512 },
513
514 { "nativeDecodeByteArray",
515 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
516 (void*)nativeDecodeByteArray
517 },
518
519 { "nativeScaleNinePatch",
520 "([BFLandroid/graphics/Rect;)[B",
521 (void*)nativeScaleNinePatch
Mike Reedab4a0c12010-01-26 10:13:53 -0500522 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523
Mike Reedab4a0c12010-01-26 10:13:53 -0500524 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525};
526
527static JNINativeMethod gOptionsMethods[] = {
528 { "requestCancel", "()V", (void*)nativeRequestCancel }
529};
530
531static jclass make_globalref(JNIEnv* env, const char classname[]) {
532 jclass c = env->FindClass(classname);
533 SkASSERT(c);
534 return (jclass)env->NewGlobalRef(c);
535}
536
537static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
538 const char fieldname[], const char type[]) {
539 jfieldID id = env->GetFieldID(clazz, fieldname, type);
540 SkASSERT(id);
541 return id;
542}
543
544#define kClassPathName "android/graphics/BitmapFactory"
545
546#define RETURN_ERR_IF_NULL(value) \
547 do { if (!(value)) { assert(0); return -1; } } while (false)
548
549int register_android_graphics_BitmapFactory(JNIEnv* env);
550int register_android_graphics_BitmapFactory(JNIEnv* env) {
551 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
552 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
553 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
554 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
555 "Landroid/graphics/Bitmap$Config;");
556 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
Mike Reedc70e06b2009-04-24 11:09:12 -0400557 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
558 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
Mike Reed1b22b972009-07-17 11:21:47 -0400559 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
561 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
562 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
Ray Chen0a6a0e92009-04-02 01:31:49 -0700563 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564
565 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
566 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
567
568 int ret = AndroidRuntime::registerNativeMethods(env,
569 "android/graphics/BitmapFactory$Options",
570 gOptionsMethods,
571 SK_ARRAY_COUNT(gOptionsMethods));
572 if (ret) {
573 return ret;
574 }
575 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
576 gMethods, SK_ARRAY_COUNT(gMethods));
577}