diff options
Diffstat (limited to 'libs/hwui/jni/NinePatch.cpp')
-rw-r--r-- | libs/hwui/jni/NinePatch.cpp | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp new file mode 100644 index 000000000000..6942017d5f27 --- /dev/null +++ b/libs/hwui/jni/NinePatch.cpp @@ -0,0 +1,168 @@ +/* +** +** Copyright 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. +*/ + +#undef LOG_TAG +#define LOG_TAG "9patch" +#define LOG_NDEBUG 1 + +#include <androidfw/ResourceTypes.h> +#include <hwui/Canvas.h> +#include <hwui/Paint.h> +#include <utils/Log.h> + +#include "SkCanvas.h" +#include "SkLatticeIter.h" +#include "SkRegion.h" +#include "GraphicsJNI.h" +#include "NinePatchPeeker.h" +#include "NinePatchUtils.h" + +jclass gInsetStruct_class; +jmethodID gInsetStruct_constructorMethodID; + +using namespace android; + +/** + * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes + * or as a Res_png_9patch instance. It is important to note that the size of the + * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch). + * The code below manipulates chunks as Res_png_9patch* types to draw and as + * int8_t* to allocate and free the backing storage. + */ + +class SkNinePatchGlue { +public: + static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { + if (NULL == obj) { + return JNI_FALSE; + } + if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) { + return JNI_FALSE; + } + const jbyte* array = env->GetByteArrayElements(obj, 0); + if (array != NULL) { + const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array); + int8_t wasDeserialized = chunk->wasDeserialized; + env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT); + return (wasDeserialized != -1) ? JNI_TRUE : JNI_FALSE; + } + return JNI_FALSE; + } + + static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { + size_t chunkSize = env->GetArrayLength(obj); + if (chunkSize < (int) (sizeof(Res_png_9patch))) { + jniThrowRuntimeException(env, "Array too small for chunk."); + return NULL; + } + + int8_t* storage = new int8_t[chunkSize]; + // This call copies the content of the jbyteArray + env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage)); + // Deserialize in place, return the array we just allocated + return reinterpret_cast<jlong>(Res_png_9patch::deserialize(storage)); + } + + static void finalize(JNIEnv* env, jobject, jlong patchHandle) { + int8_t* patch = reinterpret_cast<int8_t*>(patchHandle); + delete[] patch; + } + + static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapPtr, + jlong chunkHandle, jobject dstRect) { + Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle); + SkASSERT(chunk); + + SkBitmap bitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + SkRect dst; + GraphicsJNI::jrect_to_rect(env, dstRect, &dst); + + SkCanvas::Lattice lattice; + SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + lattice.fBounds = &src; + NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height()); + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; + + SkRegion* region = nullptr; + if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) { + SkLatticeIter iter(lattice, dst); + if (iter.numRectsToDraw() == chunk->numColors) { + SkRect dummy; + SkRect iterDst; + int index = 0; + while (iter.next(&dummy, &iterDst)) { + if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) { + if (!region) { + region = new SkRegion(); + } + + region->op(iterDst.round(), SkRegion::kUnion_Op); + } + } + } + } + + return reinterpret_cast<jlong>(region); + } + +}; + +jobject NinePatchPeeker::createNinePatchInsets(JNIEnv* env, float scale) const { + if (!mHasInsets) { + return nullptr; + } + + return env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, + mOpticalInsets[0], mOpticalInsets[1], + mOpticalInsets[2], mOpticalInsets[3], + mOutlineInsets[0], mOutlineInsets[1], + mOutlineInsets[2], mOutlineInsets[3], + mOutlineRadius, mOutlineAlpha, scale); +} + +void NinePatchPeeker::getPadding(JNIEnv* env, jobject outPadding) const { + if (mPatch) { + GraphicsJNI::set_jrect(env, outPadding, + mPatch->paddingLeft, mPatch->paddingTop, + mPatch->paddingRight, mPatch->paddingBottom); + + } else { + GraphicsJNI::set_jrect(env, outPadding, -1, -1, -1, -1); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gNinePatchMethods[] = { + { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk }, + { "validateNinePatchChunk", "([B)J", + (void*) SkNinePatchGlue::validateNinePatchChunk }, + { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize }, + { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J", + (void*) SkNinePatchGlue::getTransparentRegion } +}; + +int register_android_graphics_NinePatch(JNIEnv* env) { + gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/NinePatch$InsetStruct")); + gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>", + "(IIIIIIIIFIF)V"); + return android::RegisterMethodsOrDie(env, + "android/graphics/NinePatch", gNinePatchMethods, NELEM(gNinePatchMethods)); +} |