blob: 332b01c7ce70546e8ee6d6a3abdf172e4a5e7c17 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001#define LOG_TAG "BitmapFactory"
2
3#include "SkImageDecoder.h"
4#include "SkPixelRef.h"
5#include "SkStream.h"
6#include "GraphicsJNI.h"
7#include "SkTemplates.h"
8#include "SkUtils.h"
9#include "CreateJavaOutputStreamAdaptor.h"
10
11#include <android_runtime/AndroidRuntime.h>
12#include <utils/Asset.h>
13#include <utils/ResourceTypes.h>
14#include <netinet/in.h>
15#include <sys/mman.h>
16
17static jclass gOptions_class;
18static jfieldID gOptions_justBoundsFieldID;
19static jfieldID gOptions_sampleSizeFieldID;
20static jfieldID gOptions_configFieldID;
21static jfieldID gOptions_ditherFieldID;
22static jfieldID gOptions_widthFieldID;
23static jfieldID gOptions_heightFieldID;
24static jfieldID gOptions_mimeFieldID;
25
26static jclass gFileDescriptor_class;
27static jfieldID gFileDescriptor_descriptor;
28
29#if 0
30 #define TRACE_BITMAP(code) code
31#else
32 #define TRACE_BITMAP(code)
33#endif
34
35//#define MIN_SIZE_TO_USE_MMAP (4*1024)
36
37///////////////////////////////////////////////////////////////////////////////
38
39class AutoDecoderCancel {
40public:
41 AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
42 ~AutoDecoderCancel();
43
44 static bool RequestCancel(jobject options);
45
46private:
47 AutoDecoderCancel* fNext;
48 AutoDecoderCancel* fPrev;
49 jobject fJOptions; // java options object
50 SkImageDecoder* fDecoder;
51
52#ifdef SK_DEBUG
53 static void Validate();
54#else
55 static void Validate() {}
56#endif
57};
58
59static SkMutex gAutoDecoderCancelMutex;
60static AutoDecoderCancel* gAutoDecoderCancel;
61#ifdef SK_DEBUG
62 static int gAutoDecoderCancelCount;
63#endif
64
65AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
66 SkImageDecoder* decoder) {
67 fJOptions = joptions;
68 fDecoder = decoder;
69
70 if (NULL != joptions) {
71 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
72
73 // Add us as the head of the list
74 fPrev = NULL;
75 fNext = gAutoDecoderCancel;
76 if (gAutoDecoderCancel) {
77 gAutoDecoderCancel->fPrev = this;
78 }
79 gAutoDecoderCancel = this;
80
81 SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
82 Validate();
83 }
84}
85
86AutoDecoderCancel::~AutoDecoderCancel() {
87 if (NULL != fJOptions) {
88 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
89
90 // take us out of the dllist
91 AutoDecoderCancel* prev = fPrev;
92 AutoDecoderCancel* next = fNext;
93
94 if (prev) {
95 SkASSERT(prev->fNext == this);
96 prev->fNext = next;
97 } else {
98 SkASSERT(gAutoDecoderCancel == this);
99 gAutoDecoderCancel = next;
100 }
101 if (next) {
102 SkASSERT(next->fPrev == this);
103 next->fPrev = prev;
104 }
105
106 SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
107 Validate();
108 }
109}
110
111bool AutoDecoderCancel::RequestCancel(jobject joptions) {
112 SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
113
114 Validate();
115
116 AutoDecoderCancel* pair = gAutoDecoderCancel;
117 while (pair != NULL) {
118 if (pair->fJOptions == joptions) {
119 pair->fDecoder->cancelDecode();
120 return true;
121 }
122 pair = pair->fNext;
123 }
124 return false;
125}
126
127#ifdef SK_DEBUG
128// can only call this inside a lock on gAutoDecoderCancelMutex
129void AutoDecoderCancel::Validate() {
130 const int gCount = gAutoDecoderCancelCount;
131
132 if (gCount == 0) {
133 SkASSERT(gAutoDecoderCancel == NULL);
134 } else {
135 SkASSERT(gCount > 0);
136
137 AutoDecoderCancel* curr = gAutoDecoderCancel;
138 SkASSERT(curr);
139 SkASSERT(curr->fPrev == NULL);
140
141 int count = 0;
142 while (curr) {
143 count += 1;
144 SkASSERT(count <= gCount);
145 if (curr->fPrev) {
146 SkASSERT(curr->fPrev->fNext == curr);
147 }
148 if (curr->fNext) {
149 SkASSERT(curr->fNext->fPrev == curr);
150 }
151 curr = curr->fNext;
152 }
153 SkASSERT(count == gCount);
154 }
155}
156#endif
157
158///////////////////////////////////////////////////////////////////////////////
159
160using namespace android;
161
162class NinePatchPeeker : public SkImageDecoder::Peeker {
163public:
164 NinePatchPeeker() {
165 fPatchIsValid = false;
166 }
167
168 ~NinePatchPeeker() {
169 if (fPatchIsValid) {
170 free(fPatch);
171 }
172 }
173
174 bool fPatchIsValid;
175 Res_png_9patch* fPatch;
176
177 virtual bool peek(const char tag[], const void* data, size_t length) {
178 if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
179 Res_png_9patch* patch = (Res_png_9patch*) data;
180 size_t patchSize = patch->serializedSize();
181 assert(length == patchSize);
182 // You have to copy the data because it is owned by the png reader
183 Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
184 memcpy(patchNew, patch, patchSize);
185 // this relies on deserialization being done in place
186 Res_png_9patch::deserialize(patchNew);
187 patchNew->fileToDevice();
188 if (fPatchIsValid) {
189 free(fPatch);
190 }
191 fPatch = patchNew;
192 //printf("9patch: (%d,%d)-(%d,%d)\n",
193 // fPatch.sizeLeft, fPatch.sizeTop,
194 // fPatch.sizeRight, fPatch.sizeBottom);
195 fPatchIsValid = true;
196 } else {
197 fPatch = NULL;
198 }
199 return true; // keep on decoding
200 }
201};
202
203class AssetStreamAdaptor : public SkStream {
204public:
205 AssetStreamAdaptor(Asset* a) : fAsset(a) {}
206
207 virtual bool rewind() {
208 off_t pos = fAsset->seek(0, SEEK_SET);
209 return pos != (off_t)-1;
210 }
211
212 virtual size_t read(void* buffer, size_t size) {
213 ssize_t amount;
214
215 if (NULL == buffer) {
216 if (0 == size) { // caller is asking us for our total length
217 return fAsset->getLength();
218 }
219 // asset->seek returns new total offset
220 // we want to return amount that was skipped
221
222 off_t oldOffset = fAsset->seek(0, SEEK_CUR);
223 if (-1 == oldOffset) {
224 return 0;
225 }
226 off_t newOffset = fAsset->seek(size, SEEK_CUR);
227 if (-1 == newOffset) {
228 return 0;
229 }
230 amount = newOffset - oldOffset;
231 } else {
232 amount = fAsset->read(buffer, size);
233 }
234
235 if (amount < 0) {
236 amount = 0;
237 }
238 return amount;
239 }
240
241private:
242 Asset* fAsset;
243};
244
245///////////////////////////////////////////////////////////////////////////////
246
247static inline int32_t validOrNeg1(bool isValid, int32_t value) {
248// return isValid ? value : -1;
249 SkASSERT((int)isValid == 0 || (int)isValid == 1);
250 return ((int32_t)isValid - 1) | value;
251}
252
253static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
254 static const struct {
255 SkImageDecoder::Format fFormat;
256 const char* fMimeType;
257 } gMimeTypes[] = {
258 { SkImageDecoder::kBMP_Format, "image/bmp" },
259 { SkImageDecoder::kGIF_Format, "image/gif" },
260 { SkImageDecoder::kICO_Format, "image/x-ico" },
261 { SkImageDecoder::kJPEG_Format, "image/jpeg" },
262 { SkImageDecoder::kPNG_Format, "image/png" },
263 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
264 };
265
266 const char* cstr = NULL;
267 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
268 if (gMimeTypes[i].fFormat == format) {
269 cstr = gMimeTypes[i].fMimeType;
270 break;
271 }
272 }
273
274 jstring jstr = 0;
275 if (NULL != cstr) {
276 jstr = env->NewStringUTF(cstr);
277 }
278 return jstr;
279}
280
281static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
282 jobject options) {
283
284 int sampleSize = 1;
285 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
286 SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
287 bool doDither = true;
288
289 if (NULL != options) {
290 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
291 if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
292 mode = SkImageDecoder::kDecodeBounds_Mode;
293 }
294 // initialize these, in case we fail later on
295 env->SetIntField(options, gOptions_widthFieldID, -1);
296 env->SetIntField(options, gOptions_heightFieldID, -1);
297 env->SetObjectField(options, gOptions_mimeFieldID, 0);
298
299 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
300 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
301 doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
302 }
303
304 SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
305 if (NULL == decoder) {
306 return NULL;
307 }
308
309 decoder->setSampleSize(sampleSize);
310 decoder->setDitherImage(doDither);
311
312 NinePatchPeeker peeker;
313 JavaPixelAllocator allocator(env);
314 SkBitmap* bitmap = new SkBitmap;
315 Res_png_9patch dummy9Patch;
316
317 SkAutoTDelete<SkImageDecoder> add(decoder);
318 SkAutoTDelete<SkBitmap> adb(bitmap);
319
320 decoder->setPeeker(&peeker);
321 decoder->setAllocator(&allocator);
322
323 AutoDecoderCancel adc(options, decoder);
324
325 if (!decoder->decode(stream, bitmap, prefConfig, mode)) {
326 return NULL;
327 }
328
329 // update options (if any)
330 if (NULL != options) {
331 env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
332 env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
333 // TODO: set the mimeType field with the data from the codec.
334 // but how to reuse a set of strings, rather than allocating new one
335 // each time?
336 env->SetObjectField(options, gOptions_mimeFieldID,
337 getMimeTypeString(env, decoder->getFormat()));
338 }
339
340 // if we're in justBounds mode, return now (skip the java bitmap)
341 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
342 return NULL;
343 }
344
345 jbyteArray ninePatchChunk = NULL;
346 if (peeker.fPatchIsValid) {
347 size_t ninePatchArraySize = peeker.fPatch->serializedSize();
348 ninePatchChunk = env->NewByteArray(ninePatchArraySize);
349 if (NULL == ninePatchChunk) {
350 return NULL;
351 }
352 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
353 NULL);
354 if (NULL == array) {
355 return NULL;
356 }
357 peeker.fPatch->serialize(array);
358 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
359 }
360
361 // detach bitmap from its autotdeleter, since we want to own it now
362 adb.detach();
363
364 if (padding) {
365 if (peeker.fPatchIsValid) {
366 GraphicsJNI::set_jrect(env, padding,
367 peeker.fPatch->paddingLeft,
368 peeker.fPatch->paddingTop,
369 peeker.fPatch->paddingRight,
370 peeker.fPatch->paddingBottom);
371 } else {
372 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
373 }
374 }
375
376 // promise we will never change our pixels (great for sharing and pictures)
377 SkPixelRef* ref = bitmap->pixelRef();
378 SkASSERT(ref);
379 ref->setImmutable();
380
381 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
382}
383
384static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
385 jobject is, // InputStream
386 jbyteArray storage, // byte[]
387 jobject padding,
388 jobject options) { // BitmapFactory$Options
389 jobject bitmap = NULL;
390 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
391
392 if (stream) {
393 bitmap = doDecode(env, stream, padding, options);
394 stream->unref();
395 }
396 return bitmap;
397}
398
399static ssize_t getFDSize(int fd) {
400 off_t curr = ::lseek(fd, 0, SEEK_CUR);
401 if (curr < 0) {
402 return 0;
403 }
404 size_t size = ::lseek(fd, 0, SEEK_END);
405 ::lseek(fd, curr, SEEK_SET);
406 return size;
407}
408
409/** Restore the file descriptor's offset in our destructor
410 */
411class AutoFDSeek {
412public:
413 AutoFDSeek(int fd) : fFD(fd) {
414 fCurr = ::lseek(fd, 0, SEEK_CUR);
415 }
416 ~AutoFDSeek() {
417 if (fCurr >= 0) {
418 ::lseek(fFD, fCurr, SEEK_SET);
419 }
420 }
421private:
422 int fFD;
423 off_t fCurr;
424};
425
426static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
427 jobject fileDescriptor,
428 jobject padding,
429 jobject bitmapFactoryOptions) {
430 NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
431
432 jint descriptor = env->GetIntField(fileDescriptor,
433 gFileDescriptor_descriptor);
434
435#ifdef MIN_SIZE_TO_USE_MMAP
436 // First try to use mmap
437 size_t size = getFDSize(descriptor);
438 if (size >= MIN_SIZE_TO_USE_MMAP) {
439 void* addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, 0);
440// SkDebugf("-------- mmap returned %p %d\n", addr, size);
441 if (MAP_FAILED != addr) {
442 SkMemoryStream strm(addr, size);
443 jobject obj = doDecode(env, &strm, padding, bitmapFactoryOptions);
444 munmap(addr, size);
445 return obj;
446 }
447 }
448#endif
449
450 // we pass false for closeWhenDone, since the caller owns the descriptor
451 SkFDStream file(descriptor, false);
452 if (!file.isValid()) {
453 return NULL;
454 }
455
456 /* Restore our offset when we leave, so the caller doesn't have to.
457 This is a real feature, so we can be called more than once with the
458 same descriptor.
459 */
460 AutoFDSeek as(descriptor);
461
462 return doDecode(env, &file, padding, bitmapFactoryOptions);
463}
464
465static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
466 jint native_asset, // Asset
467 jobject padding, // Rect
468 jobject options) { // BitmapFactory$Options
469 AssetStreamAdaptor mystream((Asset*)native_asset);
470
471 return doDecode(env, &mystream, padding, options);
472}
473
474static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
475 int offset, int length, jobject options) {
476 AutoJavaByteArray ar(env, byteArray);
477 SkMemoryStream stream(ar.ptr() + offset, length);
478
479 return doDecode(env, &stream, NULL, options);
480}
481
482static void nativeRequestCancel(JNIEnv*, jobject joptions) {
483 (void)AutoDecoderCancel::RequestCancel(joptions);
484}
485
486static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
487 jobject padding) {
488
489 jbyte* array = env->GetByteArrayElements(chunkObject, 0);
490 if (array != NULL) {
491 size_t chunkSize = env->GetArrayLength(chunkObject);
492 void* storage = alloca(chunkSize);
493 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
494 memcpy(chunk, array, chunkSize);
495 android::Res_png_9patch::deserialize(chunk);
496
497 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
498 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
499 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
500 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
501
502 for (int i = 0; i < chunk->numXDivs; i++) {
503 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
504 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
505 chunk->xDivs[i]++;
506 }
507 }
508
509 for (int i = 0; i < chunk->numYDivs; i++) {
510 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
511 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
512 chunk->yDivs[i]++;
513 }
514 }
515
516 memcpy(array, chunk, chunkSize);
517
518 if (padding) {
519 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
520 chunk->paddingRight, chunk->paddingBottom);
521 }
522
523 env->ReleaseByteArrayElements(chunkObject, array, 0);
524 }
525 return chunkObject;
526}
527
528///////////////////////////////////////////////////////////////////////////////
529
530static JNINativeMethod gMethods[] = {
531 { "nativeDecodeStream",
532 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
533 (void*)nativeDecodeStream
534 },
535
536 { "nativeDecodeFileDescriptor",
537 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
538 (void*)nativeDecodeFileDescriptor
539 },
540
541 { "nativeDecodeAsset",
542 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
543 (void*)nativeDecodeAsset
544 },
545
546 { "nativeDecodeByteArray",
547 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
548 (void*)nativeDecodeByteArray
549 },
550
551 { "nativeScaleNinePatch",
552 "([BFLandroid/graphics/Rect;)[B",
553 (void*)nativeScaleNinePatch
554 }
555
556};
557
558static JNINativeMethod gOptionsMethods[] = {
559 { "requestCancel", "()V", (void*)nativeRequestCancel }
560};
561
562static jclass make_globalref(JNIEnv* env, const char classname[]) {
563 jclass c = env->FindClass(classname);
564 SkASSERT(c);
565 return (jclass)env->NewGlobalRef(c);
566}
567
568static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
569 const char fieldname[], const char type[]) {
570 jfieldID id = env->GetFieldID(clazz, fieldname, type);
571 SkASSERT(id);
572 return id;
573}
574
575#define kClassPathName "android/graphics/BitmapFactory"
576
577#define RETURN_ERR_IF_NULL(value) \
578 do { if (!(value)) { assert(0); return -1; } } while (false)
579
580int register_android_graphics_BitmapFactory(JNIEnv* env);
581int register_android_graphics_BitmapFactory(JNIEnv* env) {
582 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
583 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
584 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
585 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
586 "Landroid/graphics/Bitmap$Config;");
587 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
588 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
589 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
590 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
591
592 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
593 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
594
595 int ret = AndroidRuntime::registerNativeMethods(env,
596 "android/graphics/BitmapFactory$Options",
597 gOptionsMethods,
598 SK_ARRAY_COUNT(gOptionsMethods));
599 if (ret) {
600 return ret;
601 }
602 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
603 gMethods, SK_ARRAY_COUNT(gMethods));
604}