From e3b0a0117a2ab4118f868a731b238fe8f2430276 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 26 Jun 2013 15:45:41 -0700 Subject: Refcount 9-patches and properly handle GC events This change adds refcounting of Res_png_9patch instances, the native data structure used to represent 9-patches. The Dalvik NinePatch class now holds a native pointer instead of a Dalvik byte[]. This pointer is used whenever we need to draw the 9-patch (software or hardware.) Since we are now tracking garbage collection of NinePatch objects libhwui's PatchCache must keep a list of free blocks in the VBO used to store the meshes. This change also removes unnecessary instances tracking from GLES20DisplayList. Bitmaps and 9-patches are refcounted at the native level and do not need to be tracked by the Dalvik layer. Change-Id: Ib8682d573a538aaf1945f8ec5a9bd5da5d16f74b --- core/jni/android/graphics/NinePatch.cpp | 188 +++++++++++++++----------------- 1 file changed, 89 insertions(+), 99 deletions(-) (limited to 'core/jni/android') diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 684b1c1..7e6aeae 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -21,22 +21,30 @@ #include #include +#include + #include "SkCanvas.h" #include "SkRegion.h" #include "GraphicsJNI.h" #include "JNIHelp.h" -extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, - const SkBitmap& bitmap, const android::Res_png_9patch& chunk, - const SkPaint* paint, SkRegion** outRegion); +extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap, + const android::Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion); 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) - { + static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { if (NULL == obj) { return false; } @@ -45,126 +53,110 @@ public: } const jbyte* array = env->GetByteArrayElements(obj, 0); if (array != NULL) { - const Res_png_9patch* chunk = - reinterpret_cast(array); + const Res_png_9patch* chunk = reinterpret_cast(array); int8_t wasDeserialized = chunk->wasDeserialized; - env->ReleaseByteArrayElements(obj, const_cast(array), - JNI_ABORT); + env->ReleaseByteArrayElements(obj, const_cast(array), JNI_ABORT); return wasDeserialized != -1; } return false; } - static void validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj) - { - if (env->GetArrayLength(obj) < (int) (sizeof(Res_png_9patch))) { + static int8_t* validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj) { + size_t chunkSize = env->GetArrayLength(obj); + if (chunkSize < (int) (sizeof(Res_png_9patch))) { jniThrowRuntimeException(env, "Array too small for chunk."); - return; + return NULL; } - // XXX Also check that dimensions are correct. + int8_t* storage = new int8_t[chunkSize]; + // This call copies the content of the jbyteArray + env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast(storage)); + // Deserialize in place, return the array we just allocated + return (int8_t*) Res_png_9patch::deserialize(storage); } - static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, - const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint, - jint destDensity, jint srcDensity) - { - size_t chunkSize = env->GetArrayLength(chunkObj); - void* storage = alloca(chunkSize); - env->GetByteArrayRegion(chunkObj, 0, chunkSize, - reinterpret_cast(storage)); - if (!env->ExceptionCheck()) { - // need to deserialize the chunk - Res_png_9patch* chunk = static_cast(storage); - assert(chunkSize == chunk->serializedSize()); - // this relies on deserialization being done in place - Res_png_9patch::deserialize(chunk); - - if (destDensity == srcDensity || destDensity == 0 - || srcDensity == 0) { - ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)", - SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), - SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom)); - NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); - } else { - canvas->save(); - - SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity); - canvas->translate(bounds.fLeft, bounds.fTop); - canvas->scale(scale, scale); - - bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale); - bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); - bounds.fLeft = bounds.fTop = 0; - - ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", - SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), - SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom), - srcDensity, destDensity); - - NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); - - canvas->restore(); - } + static void finalize(JNIEnv* env, jobject, int8_t* patch) { +#ifdef USE_OPENGL_RENDERER + if (android::uirenderer::Caches::hasInstance()) { + Res_png_9patch* p = (Res_png_9patch*) patch; + android::uirenderer::Caches::getInstance().resourceCache.destructor(p); + return; + } +#endif // USE_OPENGL_RENDERER + delete[] patch; + } + + static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap, + Res_png_9patch* chunk, const SkPaint* paint, jint destDensity, jint srcDensity) { + if (destDensity == srcDensity || destDensity == 0 || srcDensity == 0) { + ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)", + SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom)); + NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); + } else { + canvas->save(); + + SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity); + canvas->translate(bounds.fLeft, bounds.fTop); + canvas->scale(scale, scale); + + bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale); + bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); + bounds.fLeft = bounds.fTop = 0; + + ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", + SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom), + srcDensity, destDensity); + + NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); + + canvas->restore(); } } static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF, - const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint, - jint destDensity, jint srcDensity) - { + const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint, + jint destDensity, jint srcDensity) { SkASSERT(canvas); SkASSERT(boundsRectF); SkASSERT(bitmap); - SkASSERT(chunkObj); + SkASSERT(chunk); // paint is optional - SkRect bounds; + SkRect bounds; GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds); - draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity); + draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity); } static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect, - const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint, - jint destDensity, jint srcDensity) - { + const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint, + jint destDensity, jint srcDensity) { SkASSERT(canvas); SkASSERT(boundsRect); SkASSERT(bitmap); - SkASSERT(chunkObj); + SkASSERT(chunk); // paint is optional - SkRect bounds; + SkRect bounds; GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); - draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity); + draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity); } - static jint getTransparentRegion(JNIEnv* env, jobject, - const SkBitmap* bitmap, jbyteArray chunkObj, - jobject boundsRect) - { + static jint getTransparentRegion(JNIEnv* env, jobject, const SkBitmap* bitmap, + Res_png_9patch* chunk, jobject boundsRect) { SkASSERT(bitmap); - SkASSERT(chunkObj); + SkASSERT(chunk); SkASSERT(boundsRect); - SkRect bounds; + SkRect bounds; GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); - size_t chunkSize = env->GetArrayLength(chunkObj); - void* storage = alloca(chunkSize); - env->GetByteArrayRegion(chunkObj, 0, chunkSize, - reinterpret_cast(storage)); - if (!env->ExceptionCheck()) { - // need to deserialize the chunk - Res_png_9patch* chunk = static_cast(storage); - assert(chunkSize == chunk->serializedSize()); - // this relies on deserialization being done in place - Res_png_9patch::deserialize(chunk); - SkRegion* region = NULL; - NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); - return (jint)region; - } - return 0; + + SkRegion* region = NULL; + NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); + + return (jint) region; } }; @@ -174,18 +166,16 @@ public: #include static JNINativeMethod gNinePatchMethods[] = { - { "isNinePatchChunk", "([B)Z", (void*)SkNinePatchGlue::isNinePatchChunk }, - { "validateNinePatchChunk", "(I[B)V", (void*)SkNinePatchGlue::validateNinePatchChunk }, - { "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF }, - { "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V", (void*)SkNinePatchGlue::drawI }, - { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I", - (void*)SkNinePatchGlue::getTransparentRegion } + { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk }, + { "validateNinePatchChunk", "(I[B)I", (void*) SkNinePatchGlue::validateNinePatchChunk }, + { "nativeFinalize", "(I)V", (void*) SkNinePatchGlue::finalize }, + { "nativeDraw", "(ILandroid/graphics/RectF;IIIII)V", (void*) SkNinePatchGlue::drawF }, + { "nativeDraw", "(ILandroid/graphics/Rect;IIIII)V", (void*) SkNinePatchGlue::drawI }, + { "nativeGetTransparentRegion", "(IILandroid/graphics/Rect;)I", + (void*) SkNinePatchGlue::getTransparentRegion } }; -int register_android_graphics_NinePatch(JNIEnv* env) -{ +int register_android_graphics_NinePatch(JNIEnv* env) { return android::AndroidRuntime::registerNativeMethods(env, - "android/graphics/NinePatch", - gNinePatchMethods, - SK_ARRAY_COUNT(gNinePatchMethods)); + "android/graphics/NinePatch", gNinePatchMethods, SK_ARRAY_COUNT(gNinePatchMethods)); } -- cgit v1.1