Loading core/jni/Android.bp +2 −0 Original line number Original line Diff line number Diff line Loading @@ -123,6 +123,7 @@ cc_library_shared { "android_graphics_Picture.cpp", "android_graphics_Picture.cpp", "android/graphics/Bitmap.cpp", "android/graphics/Bitmap.cpp", "android/graphics/BitmapFactory.cpp", "android/graphics/BitmapFactory.cpp", "android/graphics/ByteBufferStreamAdaptor.cpp", "android/graphics/Camera.cpp", "android/graphics/Camera.cpp", "android/graphics/CanvasProperty.cpp", "android/graphics/CanvasProperty.cpp", "android/graphics/ColorFilter.cpp", "android/graphics/ColorFilter.cpp", Loading @@ -134,6 +135,7 @@ cc_library_shared { "android/graphics/GraphicBuffer.cpp", "android/graphics/GraphicBuffer.cpp", "android/graphics/Graphics.cpp", "android/graphics/Graphics.cpp", "android/graphics/HarfBuzzNGFaceSkia.cpp", "android/graphics/HarfBuzzNGFaceSkia.cpp", "android/graphics/ImageDecoder.cpp", "android/graphics/Interpolator.cpp", "android/graphics/Interpolator.cpp", "android/graphics/MaskFilter.cpp", "android/graphics/MaskFilter.cpp", "android/graphics/Matrix.cpp", "android/graphics/Matrix.cpp", Loading core/jni/AndroidRuntime.cpp +4 −0 Original line number Original line Diff line number Diff line Loading @@ -57,10 +57,12 @@ extern int register_android_os_Process(JNIEnv* env); extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_GraphicBuffer(JNIEnv* env); extern int register_android_graphics_GraphicBuffer(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_ImageDecoder(JNIEnv*); extern int register_android_graphics_Interpolator(JNIEnv* env); extern int register_android_graphics_Interpolator(JNIEnv* env); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); Loading Loading @@ -1380,6 +1382,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Bitmap), REG_JNI(register_android_graphics_Bitmap), REG_JNI(register_android_graphics_BitmapFactory), REG_JNI(register_android_graphics_BitmapFactory), REG_JNI(register_android_graphics_BitmapRegionDecoder), REG_JNI(register_android_graphics_BitmapRegionDecoder), REG_JNI(register_android_graphics_ByteBufferStreamAdaptor), REG_JNI(register_android_graphics_Camera), REG_JNI(register_android_graphics_Camera), REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor), REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor), REG_JNI(register_android_graphics_CanvasProperty), REG_JNI(register_android_graphics_CanvasProperty), Loading @@ -1387,6 +1390,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_DrawFilter), REG_JNI(register_android_graphics_DrawFilter), REG_JNI(register_android_graphics_FontFamily), REG_JNI(register_android_graphics_FontFamily), REG_JNI(register_android_graphics_GraphicBuffer), REG_JNI(register_android_graphics_GraphicBuffer), REG_JNI(register_android_graphics_ImageDecoder), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_MaskFilter), REG_JNI(register_android_graphics_MaskFilter), REG_JNI(register_android_graphics_Matrix), REG_JNI(register_android_graphics_Matrix), Loading core/jni/android/graphics/BitmapFactory.cpp +3 −59 Original line number Original line Diff line number Diff line Loading @@ -47,9 +47,6 @@ jfieldID gOptions_bitmapFieldID; jfieldID gBitmap_ninePatchInsetsFieldID; jfieldID gBitmap_ninePatchInsetsFieldID; jclass gInsetStruct_class; jmethodID gInsetStruct_constructorMethodID; jclass gBitmapConfig_class; jclass gBitmapConfig_class; jmethodID gBitmapConfig_nativeToConfigMethodID; jmethodID gBitmapConfig_nativeToConfigMethodID; Loading Loading @@ -99,43 +96,6 @@ jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) { return jstr; return jstr; } } static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) { for (int i = 0; i < count; i++) { divs[i] = int32_t(divs[i] * scale + 0.5f); if (i > 0 && divs[i] == divs[i - 1]) { divs[i]++; // avoid collisions } } if (CC_UNLIKELY(divs[count - 1] > maxValue)) { // if the collision avoidance above put some divs outside the bounds of the bitmap, // slide outer stretchable divs inward to stay within bounds int highestAvailable = maxValue; for (int i = count - 1; i >= 0; i--) { divs[i] = highestAvailable; if (i > 0 && divs[i] <= divs[i-1]){ // keep shifting highestAvailable = divs[i] - 1; } else { break; } } } } static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale, int scaledWidth, int scaledHeight) { chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); // The max value for the divRange is one pixel less than the actual max to ensure that the size // of the last div is not zero. A div of size 0 is considered invalid input and will not render. scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth - 1); scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight - 1); } class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { public: public: ScaleCheckingAllocator(float scale, int size) ScaleCheckingAllocator(float scale, int size) Loading Loading @@ -428,7 +388,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, jbyteArray ninePatchChunk = NULL; jbyteArray ninePatchChunk = NULL; if (peeker.mPatch != NULL) { if (peeker.mPatch != NULL) { if (willScale) { if (willScale) { scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight); peeker.scale(scale, scale, scaledWidth, scaledHeight); } } size_t ninePatchArraySize = peeker.mPatch->serializedSize(); size_t ninePatchArraySize = peeker.mPatch->serializedSize(); Loading @@ -448,12 +408,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, jobject ninePatchInsets = NULL; jobject ninePatchInsets = NULL; if (peeker.mHasInsets) { if (peeker.mHasInsets) { ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, ninePatchInsets = peeker.createNinePatchInsets(env, scale); peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], peeker.mOutlineRadius, peeker.mOutlineAlpha, scale); if (ninePatchInsets == NULL) { if (ninePatchInsets == NULL) { return nullObjectReturn("nine patch insets == null"); return nullObjectReturn("nine patch insets == null"); } } Loading Loading @@ -508,13 +463,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, } } if (padding) { if (padding) { if (peeker.mPatch != NULL) { peeker.getPadding(env, padding); GraphicsJNI::set_jrect(env, padding, peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop, peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom); } else { GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); } } } // If we get here, the outputBitmap should have an installed pixelref. // If we get here, the outputBitmap should have an installed pixelref. Loading Loading @@ -705,11 +654,6 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", "Landroid/graphics/NinePatch$InsetStruct;"); "Landroid/graphics/NinePatch$InsetStruct;"); gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/NinePatch$InsetStruct")); gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>", "(IIIIIIIIFIF)V"); gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap$Config")); "android/graphics/Bitmap$Config")); gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, Loading core/jni/android/graphics/ByteBufferStreamAdaptor.cpp 0 → 100644 +323 −0 Original line number Original line Diff line number Diff line #include "ByteBufferStreamAdaptor.h" #include "core_jni_helpers.h" #include <SkStream.h> using namespace android; static jmethodID gByteBuffer_getMethodID; static jmethodID gByteBuffer_setPositionMethodID; static JNIEnv* get_env_or_die(JavaVM* jvm) { JNIEnv* env; if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); } return env; } class ByteBufferStream : public SkStreamAsset { private: ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, jbyteArray storage) : mJvm(jvm) , mByteBuffer(jbyteBuffer) , mPosition(0) , mInitialPosition(initialPosition) , mLength(length) , mStorage(storage) {} public: static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer, size_t position, size_t length) { // This object outlives its native method call. jbyteBuffer = env->NewGlobalRef(jbyteBuffer); if (!jbyteBuffer) { return nullptr; } jbyteArray storage = env->NewByteArray(kStorageSize); if (!storage) { env->DeleteGlobalRef(jbyteBuffer); return nullptr; } // This object outlives its native method call. storage = static_cast<jbyteArray>(env->NewGlobalRef(storage)); if (!storage) { env->DeleteGlobalRef(jbyteBuffer); return nullptr; } return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage); } ~ByteBufferStream() override { auto* env = get_env_or_die(mJvm); env->DeleteGlobalRef(mByteBuffer); env->DeleteGlobalRef(mStorage); } size_t read(void* buffer, size_t size) override { if (size > mLength - mPosition) { size = mLength - mPosition; } if (!size) { return 0; } if (!buffer) { return this->setPosition(mPosition + size); } auto* env = get_env_or_die(mJvm); size_t bytesRead = 0; do { const size_t requested = (size > kStorageSize) ? kStorageSize : size; const jint jrequested = static_cast<jint>(requested); env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested); if (env->ExceptionCheck()) { ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return bytesRead; } env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast<jbyte*>(buffer)); if (env->ExceptionCheck()) { ALOGE("Internal error in ByteBufferStream::read"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return bytesRead; } mPosition += requested; buffer = reinterpret_cast<void*>(reinterpret_cast<char*>(buffer) + requested); bytesRead += requested; size -= requested; } while (size); return bytesRead; } bool isAtEnd() const override { return mLength == mPosition; } // SkStreamRewindable overrides bool rewind() override { return this->setPosition(0); } SkStreamAsset* onDuplicate() const override { // SkStreamRewindable requires overriding this, but it is not called by // decoders, so does not need a true implementation. A proper // implementation would require duplicating the ByteBuffer, which has // its own internal position state. return nullptr; } // SkStreamSeekable overrides size_t getPosition() const override { return mPosition; } bool seek(size_t position) override { return this->setPosition(position > mLength ? mLength : position); } bool move(long offset) override { long newPosition = mPosition + offset; if (newPosition < 0) { return this->setPosition(0); } return this->seek(static_cast<size_t>(newPosition)); } SkStreamAsset* onFork() const override { // SkStreamSeekable requires overriding this, but it is not called by // decoders, so does not need a true implementation. A proper // implementation would require duplicating the ByteBuffer, which has // its own internal position state. return nullptr; } // SkStreamAsset overrides size_t getLength() const override { return mLength; } private: JavaVM* mJvm; jobject mByteBuffer; // Logical position of the SkStream, between 0 and mLength. size_t mPosition; // Initial position of mByteBuffer, treated as mPosition 0. const size_t mInitialPosition; // Logical length of the SkStream, from mInitialPosition to // mByteBuffer.limit(). const size_t mLength; // Range has already been checked by the caller. bool setPosition(size_t newPosition) { auto* env = get_env_or_die(mJvm); env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID, newPosition + mInitialPosition); if (env->ExceptionCheck()) { ALOGE("Internal error in ByteBufferStream::setPosition"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return false; } mPosition = newPosition; return true; } // FIXME: This is an arbitrary storage size, which should be plenty for // some formats (png, gif, many bmps). But for jpeg, the more we can supply // in one call the better, and webp really wants all of the data. How to // best choose the amount of storage used? static constexpr size_t kStorageSize = 4096; jbyteArray mStorage; }; class ByteArrayStream : public SkStreamAsset { private: ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length) : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {} public: static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset, size_t length) { // This object outlives its native method call. jarray = static_cast<jbyteArray>(env->NewGlobalRef(jarray)); if (!jarray) { return nullptr; } return new ByteArrayStream(jvm, jarray, offset, length); } ~ByteArrayStream() override { auto* env = get_env_or_die(mJvm); env->DeleteGlobalRef(mByteArray); } size_t read(void* buffer, size_t size) override { if (size > mLength - mPosition) { size = mLength - mPosition; } if (!size) { return 0; } auto* env = get_env_or_die(mJvm); if (buffer) { env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size, reinterpret_cast<jbyte*>(buffer)); if (env->ExceptionCheck()) { ALOGE("Internal error in ByteArrayStream::read"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return 0; } } mPosition += size; return size; } bool isAtEnd() const override { return mLength == mPosition; } // SkStreamRewindable overrides bool rewind() override { mPosition = 0; return true; } SkStreamAsset* onDuplicate() const override { // SkStreamRewindable requires overriding this, but it is not called by // decoders, so does not need a true implementation. Note that a proper // implementation is fairly straightforward return nullptr; } // SkStreamSeekable overrides size_t getPosition() const override { return mPosition; } bool seek(size_t position) override { mPosition = (position > mLength) ? mLength : position; return true; } bool move(long offset) override { long newPosition = mPosition + offset; if (newPosition < 0) { return this->seek(0); } return this->seek(static_cast<size_t>(newPosition)); } SkStreamAsset* onFork() const override { // SkStreamSeekable requires overriding this, but it is not called by // decoders, so does not need a true implementation. Note that a proper // implementation is fairly straightforward return nullptr; } // SkStreamAsset overrides size_t getLength() const override { return mLength; } private: JavaVM* mJvm; jbyteArray mByteArray; // Offset in mByteArray. Only used when communicating with Java. const size_t mOffset; // Logical position of the SkStream, between 0 and mLength. size_t mPosition; const size_t mLength; }; struct release_proc_context { JavaVM* jvm; jobject jbyteBuffer; }; std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer, size_t position, size_t limit) { JavaVM* jvm; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); const size_t length = limit - position; void* addr = env->GetDirectBufferAddress(jbyteBuffer); if (addr) { addr = reinterpret_cast<void*>(reinterpret_cast<char*>(addr) + position); jbyteBuffer = env->NewGlobalRef(jbyteBuffer); if (!jbyteBuffer) { return nullptr; } auto* context = new release_proc_context{jvm, jbyteBuffer}; auto releaseProc = [](const void*, void* context) { auto* c = reinterpret_cast<release_proc_context*>(context); JNIEnv* env = get_env_or_die(c->jvm); env->DeleteGlobalRef(c->jbyteBuffer); delete c; }; auto data = SkData::MakeWithProc(addr, length, releaseProc, context); // The new SkMemoryStream will read directly from addr. return std::unique_ptr<SkStream>(new SkMemoryStream(std::move(data))); } // Non-direct, or direct access is not supported. return std::unique_ptr<SkStream>(ByteBufferStream::Create(jvm, env, jbyteBuffer, position, length)); } std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset, size_t length) { JavaVM* jvm; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); return std::unique_ptr<SkStream>(ByteArrayStream::Create(jvm, env, array, offset, length)); } int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) { jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer"); gByteBuffer_getMethodID = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;"); gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;"); return true; } core/jni/android/graphics/ByteBufferStreamAdaptor.h 0 → 100644 +37 −0 Original line number Original line Diff line number Diff line #ifndef _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ #define _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ #include <jni.h> #include <memory> class SkStream; /** * Create an adaptor for treating a java.nio.ByteBuffer as an SkStream. * * This will special case direct ByteBuffers, but not the case where a byte[] * can be used directly. For that, use CreateByteArrayStreamAdaptor. * * @param jbyteBuffer corresponding to the java ByteBuffer. This method will * add a global ref. * @param initialPosition returned by ByteBuffer.position(). Decoding starts * from here. * @param limit returned by ByteBuffer.limit(). * * Returns null on failure. */ std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv*, jobject jbyteBuffer, size_t initialPosition, size_t limit); /** * Create an adaptor for treating a Java byte[] as an SkStream. * * @param offset into the byte[] of the beginning of the data to use. * @param length of data to use, starting from offset. * * Returns null on failure. */ std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv*, jbyteArray array, size_t offset, size_t length); #endif // _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ Loading
core/jni/Android.bp +2 −0 Original line number Original line Diff line number Diff line Loading @@ -123,6 +123,7 @@ cc_library_shared { "android_graphics_Picture.cpp", "android_graphics_Picture.cpp", "android/graphics/Bitmap.cpp", "android/graphics/Bitmap.cpp", "android/graphics/BitmapFactory.cpp", "android/graphics/BitmapFactory.cpp", "android/graphics/ByteBufferStreamAdaptor.cpp", "android/graphics/Camera.cpp", "android/graphics/Camera.cpp", "android/graphics/CanvasProperty.cpp", "android/graphics/CanvasProperty.cpp", "android/graphics/ColorFilter.cpp", "android/graphics/ColorFilter.cpp", Loading @@ -134,6 +135,7 @@ cc_library_shared { "android/graphics/GraphicBuffer.cpp", "android/graphics/GraphicBuffer.cpp", "android/graphics/Graphics.cpp", "android/graphics/Graphics.cpp", "android/graphics/HarfBuzzNGFaceSkia.cpp", "android/graphics/HarfBuzzNGFaceSkia.cpp", "android/graphics/ImageDecoder.cpp", "android/graphics/Interpolator.cpp", "android/graphics/Interpolator.cpp", "android/graphics/MaskFilter.cpp", "android/graphics/MaskFilter.cpp", "android/graphics/Matrix.cpp", "android/graphics/Matrix.cpp", Loading
core/jni/AndroidRuntime.cpp +4 −0 Original line number Original line Diff line number Diff line Loading @@ -57,10 +57,12 @@ extern int register_android_os_Process(JNIEnv* env); extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_GraphicBuffer(JNIEnv* env); extern int register_android_graphics_GraphicBuffer(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_ImageDecoder(JNIEnv*); extern int register_android_graphics_Interpolator(JNIEnv* env); extern int register_android_graphics_Interpolator(JNIEnv* env); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); Loading Loading @@ -1380,6 +1382,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Bitmap), REG_JNI(register_android_graphics_Bitmap), REG_JNI(register_android_graphics_BitmapFactory), REG_JNI(register_android_graphics_BitmapFactory), REG_JNI(register_android_graphics_BitmapRegionDecoder), REG_JNI(register_android_graphics_BitmapRegionDecoder), REG_JNI(register_android_graphics_ByteBufferStreamAdaptor), REG_JNI(register_android_graphics_Camera), REG_JNI(register_android_graphics_Camera), REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor), REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor), REG_JNI(register_android_graphics_CanvasProperty), REG_JNI(register_android_graphics_CanvasProperty), Loading @@ -1387,6 +1390,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_DrawFilter), REG_JNI(register_android_graphics_DrawFilter), REG_JNI(register_android_graphics_FontFamily), REG_JNI(register_android_graphics_FontFamily), REG_JNI(register_android_graphics_GraphicBuffer), REG_JNI(register_android_graphics_GraphicBuffer), REG_JNI(register_android_graphics_ImageDecoder), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_MaskFilter), REG_JNI(register_android_graphics_MaskFilter), REG_JNI(register_android_graphics_Matrix), REG_JNI(register_android_graphics_Matrix), Loading
core/jni/android/graphics/BitmapFactory.cpp +3 −59 Original line number Original line Diff line number Diff line Loading @@ -47,9 +47,6 @@ jfieldID gOptions_bitmapFieldID; jfieldID gBitmap_ninePatchInsetsFieldID; jfieldID gBitmap_ninePatchInsetsFieldID; jclass gInsetStruct_class; jmethodID gInsetStruct_constructorMethodID; jclass gBitmapConfig_class; jclass gBitmapConfig_class; jmethodID gBitmapConfig_nativeToConfigMethodID; jmethodID gBitmapConfig_nativeToConfigMethodID; Loading Loading @@ -99,43 +96,6 @@ jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) { return jstr; return jstr; } } static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) { for (int i = 0; i < count; i++) { divs[i] = int32_t(divs[i] * scale + 0.5f); if (i > 0 && divs[i] == divs[i - 1]) { divs[i]++; // avoid collisions } } if (CC_UNLIKELY(divs[count - 1] > maxValue)) { // if the collision avoidance above put some divs outside the bounds of the bitmap, // slide outer stretchable divs inward to stay within bounds int highestAvailable = maxValue; for (int i = count - 1; i >= 0; i--) { divs[i] = highestAvailable; if (i > 0 && divs[i] <= divs[i-1]){ // keep shifting highestAvailable = divs[i] - 1; } else { break; } } } } static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale, int scaledWidth, int scaledHeight) { chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); // The max value for the divRange is one pixel less than the actual max to ensure that the size // of the last div is not zero. A div of size 0 is considered invalid input and will not render. scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth - 1); scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight - 1); } class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { public: public: ScaleCheckingAllocator(float scale, int size) ScaleCheckingAllocator(float scale, int size) Loading Loading @@ -428,7 +388,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, jbyteArray ninePatchChunk = NULL; jbyteArray ninePatchChunk = NULL; if (peeker.mPatch != NULL) { if (peeker.mPatch != NULL) { if (willScale) { if (willScale) { scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight); peeker.scale(scale, scale, scaledWidth, scaledHeight); } } size_t ninePatchArraySize = peeker.mPatch->serializedSize(); size_t ninePatchArraySize = peeker.mPatch->serializedSize(); Loading @@ -448,12 +408,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, jobject ninePatchInsets = NULL; jobject ninePatchInsets = NULL; if (peeker.mHasInsets) { if (peeker.mHasInsets) { ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, ninePatchInsets = peeker.createNinePatchInsets(env, scale); peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], peeker.mOutlineRadius, peeker.mOutlineAlpha, scale); if (ninePatchInsets == NULL) { if (ninePatchInsets == NULL) { return nullObjectReturn("nine patch insets == null"); return nullObjectReturn("nine patch insets == null"); } } Loading Loading @@ -508,13 +463,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, } } if (padding) { if (padding) { if (peeker.mPatch != NULL) { peeker.getPadding(env, padding); GraphicsJNI::set_jrect(env, padding, peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop, peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom); } else { GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); } } } // If we get here, the outputBitmap should have an installed pixelref. // If we get here, the outputBitmap should have an installed pixelref. Loading Loading @@ -705,11 +654,6 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", "Landroid/graphics/NinePatch$InsetStruct;"); "Landroid/graphics/NinePatch$InsetStruct;"); gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/NinePatch$InsetStruct")); gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>", "(IIIIIIIIFIF)V"); gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap$Config")); "android/graphics/Bitmap$Config")); gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, Loading
core/jni/android/graphics/ByteBufferStreamAdaptor.cpp 0 → 100644 +323 −0 Original line number Original line Diff line number Diff line #include "ByteBufferStreamAdaptor.h" #include "core_jni_helpers.h" #include <SkStream.h> using namespace android; static jmethodID gByteBuffer_getMethodID; static jmethodID gByteBuffer_setPositionMethodID; static JNIEnv* get_env_or_die(JavaVM* jvm) { JNIEnv* env; if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); } return env; } class ByteBufferStream : public SkStreamAsset { private: ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, jbyteArray storage) : mJvm(jvm) , mByteBuffer(jbyteBuffer) , mPosition(0) , mInitialPosition(initialPosition) , mLength(length) , mStorage(storage) {} public: static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer, size_t position, size_t length) { // This object outlives its native method call. jbyteBuffer = env->NewGlobalRef(jbyteBuffer); if (!jbyteBuffer) { return nullptr; } jbyteArray storage = env->NewByteArray(kStorageSize); if (!storage) { env->DeleteGlobalRef(jbyteBuffer); return nullptr; } // This object outlives its native method call. storage = static_cast<jbyteArray>(env->NewGlobalRef(storage)); if (!storage) { env->DeleteGlobalRef(jbyteBuffer); return nullptr; } return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage); } ~ByteBufferStream() override { auto* env = get_env_or_die(mJvm); env->DeleteGlobalRef(mByteBuffer); env->DeleteGlobalRef(mStorage); } size_t read(void* buffer, size_t size) override { if (size > mLength - mPosition) { size = mLength - mPosition; } if (!size) { return 0; } if (!buffer) { return this->setPosition(mPosition + size); } auto* env = get_env_or_die(mJvm); size_t bytesRead = 0; do { const size_t requested = (size > kStorageSize) ? kStorageSize : size; const jint jrequested = static_cast<jint>(requested); env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested); if (env->ExceptionCheck()) { ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return bytesRead; } env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast<jbyte*>(buffer)); if (env->ExceptionCheck()) { ALOGE("Internal error in ByteBufferStream::read"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return bytesRead; } mPosition += requested; buffer = reinterpret_cast<void*>(reinterpret_cast<char*>(buffer) + requested); bytesRead += requested; size -= requested; } while (size); return bytesRead; } bool isAtEnd() const override { return mLength == mPosition; } // SkStreamRewindable overrides bool rewind() override { return this->setPosition(0); } SkStreamAsset* onDuplicate() const override { // SkStreamRewindable requires overriding this, but it is not called by // decoders, so does not need a true implementation. A proper // implementation would require duplicating the ByteBuffer, which has // its own internal position state. return nullptr; } // SkStreamSeekable overrides size_t getPosition() const override { return mPosition; } bool seek(size_t position) override { return this->setPosition(position > mLength ? mLength : position); } bool move(long offset) override { long newPosition = mPosition + offset; if (newPosition < 0) { return this->setPosition(0); } return this->seek(static_cast<size_t>(newPosition)); } SkStreamAsset* onFork() const override { // SkStreamSeekable requires overriding this, but it is not called by // decoders, so does not need a true implementation. A proper // implementation would require duplicating the ByteBuffer, which has // its own internal position state. return nullptr; } // SkStreamAsset overrides size_t getLength() const override { return mLength; } private: JavaVM* mJvm; jobject mByteBuffer; // Logical position of the SkStream, between 0 and mLength. size_t mPosition; // Initial position of mByteBuffer, treated as mPosition 0. const size_t mInitialPosition; // Logical length of the SkStream, from mInitialPosition to // mByteBuffer.limit(). const size_t mLength; // Range has already been checked by the caller. bool setPosition(size_t newPosition) { auto* env = get_env_or_die(mJvm); env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID, newPosition + mInitialPosition); if (env->ExceptionCheck()) { ALOGE("Internal error in ByteBufferStream::setPosition"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return false; } mPosition = newPosition; return true; } // FIXME: This is an arbitrary storage size, which should be plenty for // some formats (png, gif, many bmps). But for jpeg, the more we can supply // in one call the better, and webp really wants all of the data. How to // best choose the amount of storage used? static constexpr size_t kStorageSize = 4096; jbyteArray mStorage; }; class ByteArrayStream : public SkStreamAsset { private: ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length) : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {} public: static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset, size_t length) { // This object outlives its native method call. jarray = static_cast<jbyteArray>(env->NewGlobalRef(jarray)); if (!jarray) { return nullptr; } return new ByteArrayStream(jvm, jarray, offset, length); } ~ByteArrayStream() override { auto* env = get_env_or_die(mJvm); env->DeleteGlobalRef(mByteArray); } size_t read(void* buffer, size_t size) override { if (size > mLength - mPosition) { size = mLength - mPosition; } if (!size) { return 0; } auto* env = get_env_or_die(mJvm); if (buffer) { env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size, reinterpret_cast<jbyte*>(buffer)); if (env->ExceptionCheck()) { ALOGE("Internal error in ByteArrayStream::read"); env->ExceptionDescribe(); env->ExceptionClear(); mPosition = mLength; return 0; } } mPosition += size; return size; } bool isAtEnd() const override { return mLength == mPosition; } // SkStreamRewindable overrides bool rewind() override { mPosition = 0; return true; } SkStreamAsset* onDuplicate() const override { // SkStreamRewindable requires overriding this, but it is not called by // decoders, so does not need a true implementation. Note that a proper // implementation is fairly straightforward return nullptr; } // SkStreamSeekable overrides size_t getPosition() const override { return mPosition; } bool seek(size_t position) override { mPosition = (position > mLength) ? mLength : position; return true; } bool move(long offset) override { long newPosition = mPosition + offset; if (newPosition < 0) { return this->seek(0); } return this->seek(static_cast<size_t>(newPosition)); } SkStreamAsset* onFork() const override { // SkStreamSeekable requires overriding this, but it is not called by // decoders, so does not need a true implementation. Note that a proper // implementation is fairly straightforward return nullptr; } // SkStreamAsset overrides size_t getLength() const override { return mLength; } private: JavaVM* mJvm; jbyteArray mByteArray; // Offset in mByteArray. Only used when communicating with Java. const size_t mOffset; // Logical position of the SkStream, between 0 and mLength. size_t mPosition; const size_t mLength; }; struct release_proc_context { JavaVM* jvm; jobject jbyteBuffer; }; std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer, size_t position, size_t limit) { JavaVM* jvm; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); const size_t length = limit - position; void* addr = env->GetDirectBufferAddress(jbyteBuffer); if (addr) { addr = reinterpret_cast<void*>(reinterpret_cast<char*>(addr) + position); jbyteBuffer = env->NewGlobalRef(jbyteBuffer); if (!jbyteBuffer) { return nullptr; } auto* context = new release_proc_context{jvm, jbyteBuffer}; auto releaseProc = [](const void*, void* context) { auto* c = reinterpret_cast<release_proc_context*>(context); JNIEnv* env = get_env_or_die(c->jvm); env->DeleteGlobalRef(c->jbyteBuffer); delete c; }; auto data = SkData::MakeWithProc(addr, length, releaseProc, context); // The new SkMemoryStream will read directly from addr. return std::unique_ptr<SkStream>(new SkMemoryStream(std::move(data))); } // Non-direct, or direct access is not supported. return std::unique_ptr<SkStream>(ByteBufferStream::Create(jvm, env, jbyteBuffer, position, length)); } std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset, size_t length) { JavaVM* jvm; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); return std::unique_ptr<SkStream>(ByteArrayStream::Create(jvm, env, array, offset, length)); } int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) { jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer"); gByteBuffer_getMethodID = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;"); gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;"); return true; }
core/jni/android/graphics/ByteBufferStreamAdaptor.h 0 → 100644 +37 −0 Original line number Original line Diff line number Diff line #ifndef _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ #define _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ #include <jni.h> #include <memory> class SkStream; /** * Create an adaptor for treating a java.nio.ByteBuffer as an SkStream. * * This will special case direct ByteBuffers, but not the case where a byte[] * can be used directly. For that, use CreateByteArrayStreamAdaptor. * * @param jbyteBuffer corresponding to the java ByteBuffer. This method will * add a global ref. * @param initialPosition returned by ByteBuffer.position(). Decoding starts * from here. * @param limit returned by ByteBuffer.limit(). * * Returns null on failure. */ std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv*, jobject jbyteBuffer, size_t initialPosition, size_t limit); /** * Create an adaptor for treating a Java byte[] as an SkStream. * * @param offset into the byte[] of the beginning of the data to use. * @param length of data to use, starting from offset. * * Returns null on failure. */ std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv*, jbyteArray array, size_t offset, size_t length); #endif // _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_