diff options
author | Wei-Ta Chen <weita@google.com> | 2010-09-08 10:44:21 +0800 |
---|---|---|
committer | Wei-Ta Chen <weita@google.com> | 2010-09-08 10:44:21 +0800 |
commit | 340ce75b446f6a6afc12b0582be3fc34ac3a5364 (patch) | |
tree | 87719695a203dfd436ab7066bdf884a132b66a5a | |
parent | d580eee29b50e1f11485b655fe5e6cc5a564615b (diff) | |
download | frameworks_base-340ce75b446f6a6afc12b0582be3fc34ac3a5364.zip frameworks_base-340ce75b446f6a6afc12b0582be3fc34ac3a5364.tar.gz frameworks_base-340ce75b446f6a6afc12b0582be3fc34ac3a5364.tar.bz2 |
Revert "Rename LargeBitmap to BitmapRegionDecoder for having a better API."
This reverts commit 50ba3d2c09a9131f3578d271adf2bc8258ca1742.
-rw-r--r-- | core/jni/Android.mk | 3 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 196 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapRegionDecoder.cpp | 277 | ||||
-rw-r--r-- | core/jni/android/graphics/Graphics.cpp | 16 | ||||
-rw-r--r-- | core/jni/android/graphics/GraphicsJNI.h | 5 | ||||
-rw-r--r-- | core/jni/android/graphics/LargeBitmap.cpp | 138 | ||||
-rw-r--r-- | core/jni/android/graphics/Utils.cpp | 70 | ||||
-rw-r--r-- | core/jni/android/graphics/Utils.h | 61 | ||||
-rw-r--r-- | graphics/java/android/graphics/BitmapRegionDecoder.java | 270 |
10 files changed, 347 insertions, 693 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index cc67e44..42135c5 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -102,12 +102,11 @@ LOCAL_SRC_FILES:= \ android_graphics_PixelFormat.cpp \ android/graphics/Picture.cpp \ android/graphics/PorterDuff.cpp \ - android/graphics/BitmapRegionDecoder.cpp \ + android/graphics/LargeBitmap.cpp \ android/graphics/Rasterizer.cpp \ android/graphics/Region.cpp \ android/graphics/Shader.cpp \ android/graphics/Typeface.cpp \ - android/graphics/Utils.cpp \ android/graphics/Xfermode.cpp \ android/graphics/YuvToJpegEncoder.cpp \ android_media_AudioRecord.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d6b61e9..2751a82 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -53,7 +53,7 @@ extern int register_android_os_Binder(JNIEnv* env); extern int register_android_os_Process(JNIEnv* env); extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); -extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); +extern int register_android_graphics_LargeBitmap(JNIEnv*); extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_Interpolator(JNIEnv* env); @@ -1244,7 +1244,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Bitmap), REG_JNI(register_android_graphics_BitmapFactory), - REG_JNI(register_android_graphics_BitmapRegionDecoder), + REG_JNI(register_android_graphics_LargeBitmap), REG_JNI(register_android_graphics_Camera), REG_JNI(register_android_graphics_Canvas), REG_JNI(register_android_graphics_ColorFilter), diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 48539dc..254d9a4 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -10,7 +10,6 @@ #include "SkUtils.h" #include "CreateJavaOutputStreamAdaptor.h" #include "AutoDecodeCancel.h" -#include "Utils.h" #include <android_runtime/AndroidRuntime.h> #include <utils/Asset.h> @@ -99,6 +98,57 @@ public: } }; +class AssetStreamAdaptor : public SkStream { +public: + AssetStreamAdaptor(Asset* a) : fAsset(a) {} + + virtual bool rewind() { + off_t pos = fAsset->seek(0, SEEK_SET); + if (pos == (off_t)-1) { + SkDebugf("----- fAsset->seek(rewind) failed\n"); + return false; + } + return true; + } + + virtual size_t read(void* buffer, size_t size) { + ssize_t amount; + + if (NULL == buffer) { + if (0 == size) { // caller is asking us for our total length + return fAsset->getLength(); + } + // asset->seek returns new total offset + // we want to return amount that was skipped + + off_t oldOffset = fAsset->seek(0, SEEK_CUR); + if (-1 == oldOffset) { + SkDebugf("---- fAsset->seek(oldOffset) failed\n"); + return 0; + } + off_t newOffset = fAsset->seek(size, SEEK_CUR); + if (-1 == newOffset) { + SkDebugf("---- fAsset->seek(%d) failed\n", size); + return 0; + } + amount = newOffset - oldOffset; + } else { + amount = fAsset->read(buffer, size); + if (amount <= 0) { + SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount); + } + } + + if (amount < 0) { + amount = 0; + } + return amount; + } + +private: + Asset* fAsset; +}; + /////////////////////////////////////////////////////////////////////////////// static inline int32_t validOrNeg1(bool isValid, int32_t value) { @@ -150,6 +200,12 @@ static bool optionsReportSizeToVM(JNIEnv* env, jobject options) { !env->GetBooleanField(options, gOptions_nativeAllocFieldID); } +static jobject nullObjectReturn(const char msg[]) { + if (msg) { + SkDebugf("--- %s\n", msg); + } + return NULL; +} static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, int sampleSize, bool ditherImage) { @@ -320,6 +376,23 @@ static ssize_t getFDSize(int fd) { return size; } +/** Restore the file descriptor's offset in our destructor + */ +class AutoFDSeek { +public: + AutoFDSeek(int fd) : fFD(fd) { + fCurr = ::lseek(fd, 0, SEEK_CUR); + } + ~AutoFDSeek() { + if (fCurr >= 0) { + ::lseek(fFD, fCurr, SEEK_SET); + } + } +private: + int fFD; + off_t fCurr; +}; + static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, jobject padding, @@ -486,6 +559,107 @@ static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) { } } +static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) { + SkImageDecoder* decoder = SkImageDecoder::Factory(stream); + int width, height; + if (NULL == decoder) { + doThrowIOE(env, "Image format not supported"); + return nullObjectReturn("SkImageDecoder::Factory returned null"); + } + + JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); + decoder->setAllocator(javaAllocator); + JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); + decoder->setReporter(javaMemoryReporter); + javaAllocator->unref(); + javaMemoryReporter->unref(); + + if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) { + char msg[1024]; + snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName()); + doThrowIOE(env, msg); + return nullObjectReturn("decoder->buildTileIndex returned false"); + } + + SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height); + + return GraphicsJNI::createLargeBitmap(env, bm); +} + +static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + int offset, int length, jboolean isShareable) { + AutoJavaByteArray ar(env, byteArray); + SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false); + SkAutoUnref aur(stream); + if (isShareable) { + aur.detach(); + } + return doBuildTileIndex(env, stream, isShareable); +} + +static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jboolean isShareable) { + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + jint descriptor = env->GetIntField(fileDescriptor, + gFileDescriptor_descriptor); + bool weOwnTheFD = false; + + if (isShareable) { + int newFD = ::dup(descriptor); + if (-1 != newFD) { + weOwnTheFD = true; + descriptor = newFD; + } + } + + SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); + SkAutoUnref aur(stream); + if (!stream->isValid()) { + return NULL; + } + + if (isShareable) { + aur.detach(); + } + + /* Restore our offset when we leave, so we can be called more than once + with the same descriptor. This is only required if we didn't dup the + file descriptor, but it is OK to do it all the time. + */ + AutoFDSeek as(descriptor); + + return doBuildTileIndex(env, stream, isShareable); +} + +static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz, + jobject is, // InputStream + jbyteArray storage, // byte[] + jboolean isShareable) { + jobject largeBitmap = NULL; + SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024); + + if (stream) { + // for now we don't allow shareable with java inputstreams + largeBitmap = doBuildTileIndex(env, stream, false); + stream->unref(); + } + return largeBitmap; +} + +static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz, + jint native_asset, // Asset + jboolean isShareable) { + SkStream* stream; + Asset* asset = reinterpret_cast<Asset*>(native_asset); + stream = new AssetStreamAdaptor(asset); + SkAutoUnref aur(stream); + if (isShareable) { + aur.detach(); + } + return doBuildTileIndex(env, stream, isShareable); +} + /////////////////////////////////////////////////////////////////////////////// static JNINativeMethod gMethods[] = { @@ -515,6 +689,26 @@ static JNINativeMethod gMethods[] = { }, { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig }, + + { "nativeCreateLargeBitmap", + "([BIIZ)Landroid/graphics/LargeBitmap;", + (void*)nativeCreateLargeBitmapFromByteArray + }, + + { "nativeCreateLargeBitmap", + "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;", + (void*)nativeCreateLargeBitmapFromStream + }, + + { "nativeCreateLargeBitmap", + "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;", + (void*)nativeCreateLargeBitmapFromFileDescriptor + }, + + { "nativeCreateLargeBitmap", + "(IZ)Landroid/graphics/LargeBitmap;", + (void*)nativeCreateLargeBitmapFromAsset + }, }; static JNINativeMethod gOptionsMethods[] = { diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp deleted file mode 100644 index 6a5be44..0000000 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "BitmapRegionDecoder" - -#include "SkBitmap.h" -#include "SkImageEncoder.h" -#include "GraphicsJNI.h" -#include "SkUtils.h" -#include "SkTemplates.h" -#include "SkPixelRef.h" -#include "SkStream.h" -#include "BitmapFactory.h" -#include "AutoDecodeCancel.h" -#include "SkBitmapRegionDecoder.h" -#include "CreateJavaOutputStreamAdaptor.h" -#include "Utils.h" - -#include <android_runtime/AndroidRuntime.h> -#include "android_util_Binder.h" -#include "android_nio_utils.h" -#include "CreateJavaOutputStreamAdaptor.h" - -#include <binder/Parcel.h> -#include <jni.h> -#include <utils/Asset.h> - -static jclass gFileDescriptor_class; -static jfieldID gFileDescriptor_descriptor; - -#if 0 - #define TRACE_BITMAP(code) code -#else - #define TRACE_BITMAP(code) -#endif - -using namespace android; - -static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) { - SkImageDecoder* decoder = SkImageDecoder::Factory(stream); - int width, height; - if (NULL == decoder) { - doThrowIOE(env, "Image format not supported"); - return nullObjectReturn("SkImageDecoder::Factory returned null"); - } - - JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); - decoder->setAllocator(javaAllocator); - JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); - decoder->setReporter(javaMemoryReporter); - javaAllocator->unref(); - javaMemoryReporter->unref(); - - if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) { - char msg[1024]; - snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName()); - doThrowIOE(env, msg); - return nullObjectReturn("decoder->buildTileIndex returned false"); - } - - SkBitmapRegionDecoder *brd = new SkBitmapRegionDecoder(decoder, width, height); - - return GraphicsJNI::createBitmapRegionDecoder(env, brd); -} - -static jobject nativeNewInstanceFromByteArray(JNIEnv* env, - jobject, jbyteArray byteArray, int offset, int length, - jboolean isShareable) { - AutoJavaByteArray ar(env, byteArray); - SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false); - SkAutoUnref aur(stream); - if (isShareable) { - aur.detach(); - } - return doBuildTileIndex(env, stream, isShareable); -} - -static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, - jobject clazz, jobject fileDescriptor, jboolean isShareable) { - NPE_CHECK_RETURN_ZERO(env, fileDescriptor); - - jint descriptor = env->GetIntField(fileDescriptor, - gFileDescriptor_descriptor); - bool weOwnTheFD = false; - - if (isShareable) { - int newFD = ::dup(descriptor); - if (-1 != newFD) { - weOwnTheFD = true; - descriptor = newFD; - } - } - - SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); - SkAutoUnref aur(stream); - if (!stream->isValid()) { - return NULL; - } - - if (isShareable) { - aur.detach(); - } - - // Restore our offset when we leave, so we can be called more than once - // with the same descriptor. This is only required if we didn't dup the - // file descriptor, but it is OK to do it all the time. - AutoFDSeek as(descriptor); - - return doBuildTileIndex(env, stream, isShareable); -} - -static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, - jobject is, // InputStream - jbyteArray storage, // byte[] - jboolean isShareable) { - jobject BitmapRegionDecoder = NULL; - SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024); - - if (stream) { - // for now we don't allow shareable with java inputstreams - BitmapRegionDecoder = doBuildTileIndex(env, stream, false); - stream->unref(); - } - return BitmapRegionDecoder; -} - -static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, - jint native_asset, // Asset - jboolean isShareable) { - SkStream* stream; - Asset* asset = reinterpret_cast<Asset*>(native_asset); - stream = new AssetStreamAdaptor(asset); - SkAutoUnref aur(stream); - if (isShareable) { - aur.detach(); - } - return doBuildTileIndex(env, stream, isShareable); -} - -/* - * nine patch not supported - * - * purgeable not supported - * reportSizeToVM not supported - */ -static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd, - int start_x, int start_y, int width, int height, jobject options) { - SkImageDecoder *decoder = brd->getDecoder(); - int sampleSize = 1; - SkBitmap::Config prefConfig = SkBitmap::kNo_Config; - bool doDither = true; - - if (NULL != options) { - sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); - // initialize these, in case we fail later on - env->SetIntField(options, gOptions_widthFieldID, -1); - env->SetIntField(options, gOptions_heightFieldID, -1); - env->SetObjectField(options, gOptions_mimeFieldID, 0); - - jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); - prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); - doDither = env->GetBooleanField(options, gOptions_ditherFieldID); - } - - decoder->setDitherImage(doDither); - SkBitmap* bitmap = new SkBitmap; - SkAutoTDelete<SkBitmap> adb(bitmap); - AutoDecoderCancel adc(options, decoder); - - // To fix the race condition in case "requestCancelDecode" - // happens earlier than AutoDecoderCancel object is added - // to the gAutoDecoderCancelMutex linked list. - if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { - return nullObjectReturn("gOptions_mCancelID");; - } - - SkIRect region; - region.fLeft = start_x; - region.fTop = start_y; - region.fRight = start_x + width; - region.fBottom = start_y + height; - - if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) { - return nullObjectReturn("decoder->decodeRegion returned false"); - } - - // update options (if any) - if (NULL != options) { - env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); - env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); - // TODO: set the mimeType field with the data from the codec. - // but how to reuse a set of strings, rather than allocating new one - // each time? - env->SetObjectField(options, gOptions_mimeFieldID, - getMimeTypeString(env, decoder->getFormat())); - } - - // detach bitmap from its autotdeleter, since we want to own it now - adb.detach(); - - SkPixelRef* pr; - pr = bitmap->pixelRef(); - // promise we will never change our pixels (great for sharing and pictures) - pr->setImmutable(); - // now create the java bitmap - return GraphicsJNI::createBitmap(env, bitmap, false, NULL); -} - -static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { - return brd->getHeight(); -} - -static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { - return brd->getWidth(); -} - -static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { - delete brd; -} - -/////////////////////////////////////////////////////////////////////////////// - -#include <android_runtime/AndroidRuntime.h> - -static JNINativeMethod gBitmapRegionDecoderMethods[] = { - { "nativeDecodeRegion", - "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", - (void*)nativeDecodeRegion}, - - { "nativeGetHeight", "(I)I", (void*)nativeGetHeight}, - - { "nativeGetWidth", "(I)I", (void*)nativeGetWidth}, - - { "nativeClean", "(I)V", (void*)nativeClean}, - - { "nativeNewInstance", - "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", - (void*)nativeNewInstanceFromByteArray - }, - - { "nativeNewInstance", - "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", - (void*)nativeNewInstanceFromStream - }, - - { "nativeNewInstance", - "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", - (void*)nativeNewInstanceFromFileDescriptor - }, - - { "nativeNewInstance", - "(IZ)Landroid/graphics/BitmapRegionDecoder;", - (void*)nativeNewInstanceFromAsset - }, -}; - -#define kClassPathName "android/graphics/BitmapRegionDecoder" - -int register_android_graphics_BitmapRegionDecoder(JNIEnv* env); -int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) -{ - return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, - gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods)); -} diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index b520c30..578de6f 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -169,8 +169,8 @@ static jmethodID gBitmap_allocBufferMethodID; static jclass gBitmapConfig_class; static jfieldID gBitmapConfig_nativeInstanceID; -static jclass gBitmapRegionDecoder_class; -static jmethodID gBitmapRegionDecoder_constructorMethodID; +static jclass gLargeBitmap_class; +static jmethodID gLargeBitmap_constructorMethodID; static jclass gCanvas_class; static jfieldID gCanvas_nativeInstanceID; @@ -377,18 +377,17 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, } return obj; } - -jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) +jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap) { SkASSERT(bitmap != NULL); - jobject obj = env->AllocObject(gBitmapRegionDecoder_class); + jobject obj = env->AllocObject(gLargeBitmap_class); if (hasException(env)) { obj = NULL; return obj; } if (obj) { - env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap); + env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap); if (hasException(env)) { obj = NULL; } @@ -614,8 +613,8 @@ int register_android_graphics_Graphics(JNIEnv* env) gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(IZ[BI)V"); - gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); - gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); + gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap"); + gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V"); gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config"); gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class, @@ -655,3 +654,4 @@ int register_android_graphics_Graphics(JNIEnv* env) return 0; } + diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index d0f9125..1a43a3e 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -4,7 +4,7 @@ #include "SkPoint.h" #include "SkRect.h" #include "SkBitmap.h" -#include "../images/SkBitmapRegionDecoder.h" +#include "../images/SkLargeBitmap.h" #include "../images/SkImageDecoder.h" #include <jni.h> @@ -56,7 +56,7 @@ public: static jobject createRegion(JNIEnv* env, SkRegion* region); - static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); + static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap); /** Set a pixelref for the bitmap (needs setConfig to already be called) Returns true on success. If it returns false, then it failed, and the @@ -181,3 +181,4 @@ void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0) #endif + diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp new file mode 100644 index 0000000..4cf5dfa --- /dev/null +++ b/core/jni/android/graphics/LargeBitmap.cpp @@ -0,0 +1,138 @@ +#define LOG_TAG "LargeBitmap" + +#include "SkBitmap.h" +#include "SkImageEncoder.h" +#include "SkColorPriv.h" +#include "GraphicsJNI.h" +#include "SkDither.h" +#include "SkUnPreMultiply.h" +#include "SkUtils.h" +#include "SkTemplates.h" +#include "SkPixelRef.h" +#include "BitmapFactory.h" +#include "AutoDecodeCancel.h" +#include "SkLargeBitmap.h" + +#include <binder/Parcel.h> +#include "android_util_Binder.h" +#include "android_nio_utils.h" +#include "CreateJavaOutputStreamAdaptor.h" + +#include <jni.h> + +#if 0 + #define TRACE_BITMAP(code) code +#else + #define TRACE_BITMAP(code) +#endif + +static jobject nullObjectReturn(const char msg[]) { + if (msg) { + SkDebugf("--- %s\n", msg); + } + return NULL; +} + +/* + * nine patch not supported + * + * purgeable not supported + * reportSizeToVM not supported + */ +static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm, + int start_x, int start_y, int width, int height, jobject options) { + SkImageDecoder *decoder = bm->getDecoder(); + int sampleSize = 1; + SkBitmap::Config prefConfig = SkBitmap::kNo_Config; + bool doDither = true; + + if (NULL != options) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + // initialize these, in case we fail later on + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); + doDither = env->GetBooleanField(options, gOptions_ditherFieldID); + } + + decoder->setDitherImage(doDither); + SkBitmap* bitmap = new SkBitmap; + SkAutoTDelete<SkBitmap> adb(bitmap); + AutoDecoderCancel adc(options, decoder); + + // To fix the race condition in case "requestCancelDecode" + // happens earlier than AutoDecoderCancel object is added + // to the gAutoDecoderCancelMutex linked list. + if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { + return nullObjectReturn("gOptions_mCancelID");; + } + + SkIRect region; + region.fLeft = start_x; + region.fTop = start_y; + region.fRight = start_x + width; + region.fBottom = start_y + height; + + if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) { + return nullObjectReturn("decoder->decodeRegion returned false"); + } + + // update options (if any) + if (NULL != options) { + env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); + env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); + // TODO: set the mimeType field with the data from the codec. + // but how to reuse a set of strings, rather than allocating new one + // each time? + env->SetObjectField(options, gOptions_mimeFieldID, + getMimeTypeString(env, decoder->getFormat())); + } + + // detach bitmap from its autotdeleter, since we want to own it now + adb.detach(); + + SkPixelRef* pr; + pr = bitmap->pixelRef(); + // promise we will never change our pixels (great for sharing and pictures) + pr->setImmutable(); + // now create the java bitmap + return GraphicsJNI::createBitmap(env, bitmap, false, NULL); +} + +static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) { + return bm->getHeight(); +} + +static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) { + return bm->getWidth(); +} + +static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) { + delete bm; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include <android_runtime/AndroidRuntime.h> + +static JNINativeMethod gLargeBitmapMethods[] = { + { "nativeDecodeRegion", + "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + (void*)nativeDecodeRegion}, + { "nativeGetHeight", "(I)I", (void*)nativeGetHeight}, + { "nativeGetWidth", "(I)I", (void*)nativeGetWidth}, + { "nativeClean", "(I)V", (void*)nativeClean}, +}; + +#define kClassPathName "android/graphics/LargeBitmap" + +int register_android_graphics_LargeBitmap(JNIEnv* env); +int register_android_graphics_LargeBitmap(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, + gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods)); +} + diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp deleted file mode 100644 index b6ead19..0000000 --- a/core/jni/android/graphics/Utils.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Utils.h" -#include "SkUtils.h" - -using namespace android; - -bool AssetStreamAdaptor::rewind() { - off_t pos = fAsset->seek(0, SEEK_SET); - if (pos == (off_t)-1) { - SkDebugf("----- fAsset->seek(rewind) failed\n"); - return false; - } - return true; -} - -size_t AssetStreamAdaptor::read(void* buffer, size_t size) { - ssize_t amount; - - if (NULL == buffer) { - if (0 == size) { // caller is asking us for our total length - return fAsset->getLength(); - } - // asset->seek returns new total offset - // we want to return amount that was skipped - - off_t oldOffset = fAsset->seek(0, SEEK_CUR); - if (-1 == oldOffset) { - SkDebugf("---- fAsset->seek(oldOffset) failed\n"); - return 0; - } - off_t newOffset = fAsset->seek(size, SEEK_CUR); - if (-1 == newOffset) { - SkDebugf("---- fAsset->seek(%d) failed\n", size); - return 0; - } - amount = newOffset - oldOffset; - } else { - amount = fAsset->read(buffer, size); - if (amount <= 0) { - SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount); - } - } - - if (amount < 0) { - amount = 0; - } - return amount; -} - -jobject android::nullObjectReturn(const char msg[]) { - if (msg) { - SkDebugf("--- %s\n", msg); - } - return NULL; -} diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h deleted file mode 100644 index 2de41a1..0000000 --- a/core/jni/android/graphics/Utils.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UTILS_DEFINED -#define UTILS_DEFINED - -#include "SkStream.h" - -#include "android_util_Binder.h" - -#include <jni.h> -#include <utils/Asset.h> - -namespace android { - -class AssetStreamAdaptor : public SkStream { -public: - AssetStreamAdaptor(Asset* a) : fAsset(a) {} - virtual bool rewind(); - virtual size_t read(void* buffer, size_t size); - -private: - Asset* fAsset; -}; - - -/** Restore the file descriptor's offset in our destructor - */ -class AutoFDSeek { -public: - AutoFDSeek(int fd) : fFD(fd) { - fCurr = ::lseek(fd, 0, SEEK_CUR); - } - ~AutoFDSeek() { - if (fCurr >= 0) { - ::lseek(fFD, fCurr, SEEK_SET); - } - } -private: - int fFD; - off_t fCurr; -}; - -jobject nullObjectReturn(const char msg[]); - -}; // namespace android - -#endif diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java deleted file mode 100644 index bc92b05..0000000 --- a/graphics/java/android/graphics/BitmapRegionDecoder.java +++ /dev/null @@ -1,270 +0,0 @@ -/* Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics; - -import android.content.res.AssetManager; -import android.os.MemoryFile; - -import java.io.BufferedInputStream; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * BitmapRegionDecoder can be used to decode a rectangle region from an image. - * BitmapRegionDecoder is particularly useful when an original image is large and - * you only need parts of the image. - * - * <p>To create a BitmapRegionDecoder, call newInstance(...). - * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly - * to get a decoded Bitmap of the specified region. - * - * @hide - */ -public final class BitmapRegionDecoder { - private int mNativeBitmapRegionDecoder; - private boolean mRecycled; - - /** - * Create a BitmapRegionDecoder from the specified byte array. - * Currently only the Jpeg format is supported. - * - * @param data byte array of compressed image data. - * @param offset offset into data for where the decoder should begin - * parsing. - * @param length the number of bytes, beginning at offset, to parse - * @param isShareable If this is true, then the BitmapRegionDecoder may keep a - * shallow reference to the input. If this is false, - * then the BitmapRegionDecoder will explicitly make a copy of the - * input data, and keep that. Even if sharing is allowed, - * the implementation may still decide to make a deep - * copy of the input data. If an image is progressively encoded, - * allowing sharing may degrade the decoding speed. - * @return BitmapRegionDecoder, or null if the image data could not be decoded. - * @throws IOException if the image format is not supported or can not be decoded. - */ - public static BitmapRegionDecoder newInstance(byte[] data, - int offset, int length, boolean isShareable) throws IOException { - if ((offset | length) < 0 || data.length < offset + length) { - throw new ArrayIndexOutOfBoundsException(); - } - return nativeNewInstance(data, offset, length, isShareable); - } - - /** - * Create a BitmapRegionDecoder from the file descriptor. - * The position within the descriptor will not be changed when - * this returns, so the descriptor can be used again as is. - * Currently only the Jpeg format is supported. - * - * @param fd The file descriptor containing the data to decode - * @param isShareable If this is true, then the BitmapRegionDecoder may keep a - * shallow reference to the input. If this is false, - * then the BitmapRegionDecoder will explicitly make a copy of the - * input data, and keep that. Even if sharing is allowed, - * the implementation may still decide to make a deep - * copy of the input data. If an image is progressively encoded, - * allowing sharing may degrade the decoding speed. - * @return BitmapRegionDecoder, or null if the image data could not be decoded. - * @throws IOException if the image format is not supported or can not be decoded. - */ - public static BitmapRegionDecoder newInstance( - FileDescriptor fd, boolean isShareable) throws IOException { - if (MemoryFile.isMemoryFile(fd)) { - int mappedlength = MemoryFile.getSize(fd); - MemoryFile file = new MemoryFile(fd, mappedlength, "r"); - InputStream is = file.getInputStream(); - return newInstance(is, isShareable); - } - return nativeNewInstance(fd, isShareable); - } - - /** - * Create a BitmapRegionDecoder from an input stream. - * The stream's position will be where ever it was after the encoded data - * was read. - * Currently only the Jpeg format is supported. - * - * @param is The input stream that holds the raw data to be decoded into a - * BitmapRegionDecoder. - * @param isShareable If this is true, then the BitmapRegionDecoder may keep a - * shallow reference to the input. If this is false, - * then the BitmapRegionDecoder will explicitly make a copy of the - * input data, and keep that. Even if sharing is allowed, - * the implementation may still decide to make a deep - * copy of the input data. If an image is progressively encoded, - * allowing sharing may degrade the decoding speed. - * @return BitmapRegionDecoder, or null if the image data could not be decoded. - * @throws IOException if the image format is not supported or can not be decoded. - */ - public static BitmapRegionDecoder newInstance(InputStream is, - boolean isShareable) throws IOException { - // we need mark/reset to work properly in JNI - - if (!is.markSupported()) { - is = new BufferedInputStream(is, 16 * 1024); - } - - if (is instanceof AssetManager.AssetInputStream) { - return nativeNewInstance( - ((AssetManager.AssetInputStream) is).getAssetInt(), - isShareable); - } else { - // pass some temp storage down to the native code. 1024 is made up, - // but should be large enough to avoid too many small calls back - // into is.read(...). - byte [] tempStorage = new byte[16 * 1024]; - return nativeNewInstance(is, tempStorage, isShareable); - } - } - - /** - * Create a BitmapRegionDecoder from a file path. - * Currently only the Jpeg format is supported. - * - * @param pathName complete path name for the file to be decoded. - * @param isShareable If this is true, then the BitmapRegionDecoder may keep a - * shallow reference to the input. If this is false, - * then the BitmapRegionDecoder will explicitly make a copy of the - * input data, and keep that. Even if sharing is allowed, - * the implementation may still decide to make a deep - * copy of the input data. If an image is progressively encoded, - * allowing sharing may degrade the decoding speed. - * @return BitmapRegionDecoder, or null if the image data could not be decoded. - * @throws IOException if the image format is not supported or can not be decoded. - */ - public static BitmapRegionDecoder newInstance(String pathName, - boolean isShareable) throws IOException { - BitmapRegionDecoder decoder = null; - InputStream stream = null; - - try { - stream = new FileInputStream(pathName); - decoder = newInstance(stream, isShareable); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - // do nothing here - } - } - } - return decoder; - } - - /* Private constructor that must receive an already allocated native - region decoder int (pointer). - - This can be called from JNI code. - */ - private BitmapRegionDecoder(int decoder) { - mNativeBitmapRegionDecoder = decoder; - mRecycled = false; - } - - /** - * Decodes a rectangle region in the image specified by rect. - * - * @param rect The rectangle that specified the region to be decode. - * @param options null-ok; Options that control downsampling. - * inPurgeable is not supported. - * @return The decoded bitmap, or null if the image data could not be - * decoded. - */ - public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) { - checkRecycled("decodeRegion called on recycled region decoder"); - if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() - || rect.bottom > getHeight()) - throw new IllegalArgumentException("rectangle is not inside the image"); - return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, options); - } - - /** Returns the original image's width */ - public int getWidth() { - checkRecycled("getWidth called on recycled region decoder"); - return nativeGetWidth(mNativeBitmapRegionDecoder); - } - - /** Returns the original image's height */ - public int getHeight() { - checkRecycled("getHeight called on recycled region decoder"); - return nativeGetHeight(mNativeBitmapRegionDecoder); - } - - /** - * Frees up the memory associated with this region decoder, and mark the - * region decoder as "dead", meaning it will throw an exception if decodeRegion(), - * getWidth() or getHeight() is called. - * - * <p>This operation cannot be reversed, so it should only be called if you are - * sure there are no further uses for the region decoder. This is an advanced call, - * and normally need not be called, since the normal GC process will free up this - * memory when there are no more references to this region decoder. - */ - public void recycle() { - if (!mRecycled) { - nativeClean(mNativeBitmapRegionDecoder); - mRecycled = true; - } - } - - /** - * Returns true if this region decoder has been recycled. - * If so, then it is an error to try use its method. - * - * @return true if the region decoder has been recycled - */ - public final boolean isRecycled() { - return mRecycled; - } - - /** - * Called by methods that want to throw an exception if the region decoder - * has already been recycled. - */ - private void checkRecycled(String errorMessage) { - if (mRecycled) { - throw new IllegalStateException(errorMessage); - } - } - - protected void finalize() throws Throwable { - try { - recycle(); - } finally { - super.finalize(); - } - } - - private static native Bitmap nativeDecodeRegion(int lbm, - int start_x, int start_y, int width, int height, - BitmapFactory.Options options); - private static native int nativeGetWidth(int lbm); - private static native int nativeGetHeight(int lbm); - private static native void nativeClean(int lbm); - - private static native BitmapRegionDecoder nativeNewInstance( - byte[] data, int offset, int length, boolean isShareable); - private static native BitmapRegionDecoder nativeNewInstance( - FileDescriptor fd, boolean isShareable); - private static native BitmapRegionDecoder nativeNewInstance( - InputStream is, byte[] storage, boolean isShareable); - private static native BitmapRegionDecoder nativeNewInstance( - int asset, boolean isShareable); -} |