diff options
| -rw-r--r-- | api/current.txt | 3 | ||||
| -rw-r--r-- | core/java/android/view/GLES20Canvas.java | 6 | ||||
| -rw-r--r-- | core/java/android/view/GLES20DisplayList.java | 20 | ||||
| -rw-r--r-- | core/java/android/view/GLES20RecordingCanvas.java | 220 | ||||
| -rw-r--r-- | core/jni/android/graphics/NinePatch.cpp | 188 | ||||
| -rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 10 | ||||
| -rw-r--r-- | graphics/java/android/graphics/NinePatch.java | 162 | ||||
| -rw-r--r-- | libs/hwui/Caches.cpp | 1 | ||||
| -rw-r--r-- | libs/hwui/DisplayList.cpp | 12 | ||||
| -rw-r--r-- | libs/hwui/DisplayList.h | 4 | ||||
| -rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 6 | ||||
| -rw-r--r-- | libs/hwui/DisplayListRenderer.h | 11 | ||||
| -rw-r--r-- | libs/hwui/PatchCache.cpp | 152 | ||||
| -rw-r--r-- | libs/hwui/PatchCache.h | 55 | ||||
| -rw-r--r-- | libs/hwui/PathCache.cpp | 25 | ||||
| -rw-r--r-- | libs/hwui/PathCache.h | 2 | ||||
| -rw-r--r-- | libs/hwui/ResourceCache.cpp | 52 | ||||
| -rw-r--r-- | libs/hwui/ResourceCache.h | 11 |
18 files changed, 511 insertions, 429 deletions
diff --git a/api/current.txt b/api/current.txt index 187334d594d5..92aa4946714d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9156,8 +9156,11 @@ package android.graphics { method public void draw(android.graphics.Canvas, android.graphics.RectF); method public void draw(android.graphics.Canvas, android.graphics.Rect); method public void draw(android.graphics.Canvas, android.graphics.Rect, android.graphics.Paint); + method public android.graphics.Bitmap getBitmap(); method public int getDensity(); method public int getHeight(); + method public java.lang.String getName(); + method public android.graphics.Paint getPaint(); method public final android.graphics.Region getTransparentRegion(android.graphics.Rect); method public int getWidth(); method public final boolean hasAlpha(); diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 1f35c1d81ece..a441c39bb307 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -781,7 +781,7 @@ class GLES20Canvas extends HardwareCanvas { int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE; try { final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mChunk, + nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } finally { if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); @@ -796,14 +796,14 @@ class GLES20Canvas extends HardwareCanvas { int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE; try { final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mChunk, + nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } finally { if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); } } - private static native void nDrawPatch(int renderer, int bitmap, byte[] chunks, + private static native void nDrawPatch(int renderer, int bitmap, int chunk, float left, float top, float right, float bottom, int paint); @Override diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index 9c5b33d35e30..8b2a2ef8b6e0 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -16,9 +16,7 @@ package android.view; -import android.graphics.Bitmap; import android.graphics.Matrix; -import android.graphics.NinePatch; import java.util.ArrayList; @@ -26,12 +24,6 @@ import java.util.ArrayList; * An implementation of display list for OpenGL ES 2.0. */ class GLES20DisplayList extends DisplayList { - // These lists ensure that any Bitmaps and DisplayLists recorded by a DisplayList are kept - // alive as long as the DisplayList is alive. The Bitmap and DisplayList lists - // are populated by the GLES20RecordingCanvas during appropriate drawing calls and are - // cleared at the start of a new drawing frame or when the view is detached from the window. - private ArrayList<Bitmap> mBitmaps; - private ArrayList<NinePatch> mNinePatches; private ArrayList<DisplayList> mChildDisplayLists; private GLES20RecordingCanvas mCanvas; @@ -89,21 +81,9 @@ class GLES20DisplayList extends DisplayList { } void clearReferences() { - if (mBitmaps != null) mBitmaps.clear(); - if (mNinePatches != null) mNinePatches.clear(); if (mChildDisplayLists != null) mChildDisplayLists.clear(); } - ArrayList<Bitmap> getBitmaps() { - if (mBitmaps == null) mBitmaps = new ArrayList<Bitmap>(5); - return mBitmaps; - } - - ArrayList<NinePatch> getNinePatches() { - if (mNinePatches == null) mNinePatches = new ArrayList<NinePatch>(5); - return mNinePatches; - } - ArrayList<DisplayList> getChildDisplayLists() { if (mChildDisplayLists == null) mChildDisplayLists = new ArrayList<DisplayList>(); return mChildDisplayLists; diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java index 273947fbc452..b6fc38d892f5 100644 --- a/core/java/android/view/GLES20RecordingCanvas.java +++ b/core/java/android/view/GLES20RecordingCanvas.java @@ -16,15 +16,7 @@ package android.view; -import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Matrix; -import android.graphics.NinePatch; -import android.graphics.Paint; -import android.graphics.Path; import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Shader; import android.util.Pools.SynchronizedPool; /** @@ -70,222 +62,10 @@ class GLES20RecordingCanvas extends GLES20Canvas { return getDisplayList(nativeDisplayList); } - private void recordShaderBitmap(Paint paint) { - if (paint != null) { - final Shader shader = paint.getShader(); - if (shader instanceof BitmapShader) { - mDisplayList.getBitmaps().add(((BitmapShader) shader).mBitmap); - } - } - } - - @Override - public void drawPatch(NinePatch patch, RectF dst, Paint paint) { - super.drawPatch(patch, dst, paint); - mDisplayList.getBitmaps().add(patch.getBitmap()); - mDisplayList.getNinePatches().add(patch); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { - super.drawBitmap(bitmap, left, top, paint); - mDisplayList.getBitmaps().add(bitmap); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { - super.drawBitmap(bitmap, matrix, paint); - mDisplayList.getBitmaps().add(bitmap); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { - super.drawBitmap(bitmap, src, dst, paint); - mDisplayList.getBitmaps().add(bitmap); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { - super.drawBitmap(bitmap, src, dst, paint); - mDisplayList.getBitmaps().add(bitmap); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawBitmap(int[] colors, int offset, int stride, float x, float y, int width, - int height, boolean hasAlpha, Paint paint) { - super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width, - int height, boolean hasAlpha, Paint paint) { - super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, - int vertOffset, int[] colors, int colorOffset, Paint paint) { - super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, - colors, colorOffset, paint); - mDisplayList.getBitmaps().add(bitmap); - // Shaders in the Paint are ignored when drawing a Bitmap - } - - @Override - public void drawCircle(float cx, float cy, float radius, Paint paint) { - super.drawCircle(cx, cy, radius, paint); - recordShaderBitmap(paint); - } - @Override public int drawDisplayList(DisplayList displayList, Rect dirty, int flags) { int status = super.drawDisplayList(displayList, dirty, flags); mDisplayList.getChildDisplayLists().add(displayList); return status; } - - @Override - public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) { - super.drawLine(startX, startY, stopX, stopY, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawLines(float[] pts, int offset, int count, Paint paint) { - super.drawLines(pts, offset, count, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawLines(float[] pts, Paint paint) { - super.drawLines(pts, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawOval(RectF oval, Paint paint) { - super.drawOval(oval, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawPaint(Paint paint) { - super.drawPaint(paint); - recordShaderBitmap(paint); - } - - @Override - public void drawPath(Path path, Paint paint) { - super.drawPath(path, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawPoint(float x, float y, Paint paint) { - super.drawPoint(x, y, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawPoints(float[] pts, int offset, int count, Paint paint) { - super.drawPoints(pts, offset, count, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawPoints(float[] pts, Paint paint) { - super.drawPoints(pts, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) { - super.drawPosText(text, index, count, pos, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawPosText(String text, float[] pos, Paint paint) { - super.drawPosText(text, pos, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawRect(float left, float top, float right, float bottom, Paint paint) { - super.drawRect(left, top, right, bottom, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) { - super.drawRoundRect(rect, rx, ry, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { - super.drawText(text, index, count, x, y, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) { - super.drawText(text, start, end, x, y, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawText(String text, int start, int end, float x, float y, Paint paint) { - super.drawText(text, start, end, x, y, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawText(String text, float x, float y, Paint paint) { - super.drawText(text, x, y, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, - float vOffset, Paint paint) { - super.drawTextOnPath(text, index, count, path, hOffset, vOffset, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) { - super.drawTextOnPath(text, path, hOffset, vOffset, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, - float x, float y, int dir, Paint paint) { - super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, dir, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawTextRun(CharSequence text, int start, int end, int contextStart, - int contextEnd, float x, float y, int dir, Paint paint) { - super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, dir, paint); - recordShaderBitmap(paint); - } - - @Override - public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset, - float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, - int indexOffset, int indexCount, Paint paint) { - super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors, - colorOffset, indices, indexOffset, indexCount, paint); - recordShaderBitmap(paint); - } } diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 684b1c1d3579..7e6aeae0ac7c 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -21,22 +21,30 @@ #include <androidfw/ResourceTypes.h> #include <utils/Log.h> +#include <Caches.h> + #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<const Res_png_9patch*>(array); + 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); + env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(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<jbyte*>(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<jbyte*>(storage)); - if (!env->ExceptionCheck()) { - // need to deserialize the chunk - Res_png_9patch* chunk = static_cast<Res_png_9patch*>(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<jbyte*>(storage)); - if (!env->ExceptionCheck()) { - // need to deserialize the chunk - Res_png_9patch* chunk = static_cast<Res_png_9patch*>(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 <android_runtime/AndroidRuntime.h> 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)); } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 5b6cff0adf0a..87ebbd26987d 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -432,15 +432,9 @@ static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz, } static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz, - OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks, + OpenGLRenderer* renderer, SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint) { - jbyte* storage = env->GetByteArrayElements(chunks, NULL); - Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage); - Res_png_9patch::deserialize(patch); - renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint); - - env->ReleaseByteArrayElements(chunks, storage, 0); } static void android_view_GLES20Canvas_drawColor(JNIEnv* env, jobject clazz, @@ -1031,7 +1025,7 @@ static JNINativeMethod gMethods[] = { { "nDrawBitmapMesh", "(IIII[FI[III)V", (void*) android_view_GLES20Canvas_drawBitmapMesh }, - { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch }, + { "nDrawPatch", "(IIIFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch }, { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java index ab1c328db5ea..528d9def0dcc 100644 --- a/graphics/java/android/graphics/NinePatch.java +++ b/graphics/java/android/graphics/NinePatch.java @@ -18,11 +18,8 @@ package android.graphics; /** - * The NinePatch class permits drawing a bitmap in nine sections. - * The four corners are unscaled; the four edges are scaled in one axis, - * and the middle is scaled in both axes. Normally, the middle is - * transparent so that the patch can provide a selection about a rectangle. - * Essentially, it allows the creation of custom graphics that will scale the + * The NinePatch class permits drawing a bitmap in nine or more sections. + * Essentially, it allows the creation of custom graphics that will scale the * way that you define, when content added within the image exceeds the normal * bounds of the graphic. For a thorough explanation of a NinePatch image, * read the discussion in the @@ -36,19 +33,23 @@ package android.graphics; */ public class NinePatch { private final Bitmap mBitmap; + /** + * Used by native code. This pointer is an instance of Res_png_9patch*. + * * @hide */ - public final byte[] mChunk; + public final int mNativeChunk; + private Paint mPaint; - private String mSrcName; // Useful for debugging + private String mSrcName; /** * Create a drawable projection from a bitmap to nine patches. * - * @param bitmap The bitmap describing the patches. - * @param chunk The 9-patch data chunk describing how the underlying - * bitmap is split apart and drawn. + * @param bitmap The bitmap describing the patches. + * @param chunk The 9-patch data chunk describing how the underlying bitmap + * is split apart and drawn. */ public NinePatch(Bitmap bitmap, byte[] chunk) { this(bitmap, chunk, null); @@ -57,16 +58,15 @@ public class NinePatch { /** * Create a drawable projection from a bitmap to nine patches. * - * @param bitmap The bitmap describing the patches. - * @param chunk The 9-patch data chunk describing how the underlying - * bitmap is split apart and drawn. - * @param srcName The name of the source for the bitmap. Might be null. + * @param bitmap The bitmap describing the patches. + * @param chunk The 9-patch data chunk describing how the underlying + * bitmap is split apart and drawn. + * @param srcName The name of the source for the bitmap. Might be null. */ public NinePatch(Bitmap bitmap, byte[] chunk, String srcName) { mBitmap = bitmap; - mChunk = chunk; mSrcName = srcName; - validateNinePatchChunk(mBitmap.ni(), chunk); + mNativeChunk = validateNinePatchChunk(mBitmap.ni(), chunk); } /** @@ -74,69 +74,102 @@ public class NinePatch { */ public NinePatch(NinePatch patch) { mBitmap = patch.mBitmap; - mChunk = patch.mChunk; mSrcName = patch.mSrcName; if (patch.mPaint != null) { mPaint = new Paint(patch.mPaint); } - validateNinePatchChunk(mBitmap.ni(), mChunk); + // No need to validate the 9patch chunk again, it was done by + // the instance we're copying from + mNativeChunk = patch.mNativeChunk; + } + + @Override + protected void finalize() throws Throwable { + try { + nativeFinalize(mNativeChunk); + } finally { + super.finalize(); + } } + /** + * Returns the name of this NinePatch object if one was specified + * when calling the constructor. + */ + public String getName() { + return mSrcName; + } + + /** + * Returns the paint used to draw this NinePatch. The paint can be null. + * + * @see #setPaint(Paint) + * @see #draw(Canvas, Rect) + * @see #draw(Canvas, RectF) + */ + public Paint getPaint() { + return mPaint; + } + + /** + * Sets the paint to use when drawing the NinePatch. + * + * @param p The paint that will be used to draw this NinePatch. + * + * @see #getPaint() + * @see #draw(Canvas, Rect) + * @see #draw(Canvas, RectF) + */ public void setPaint(Paint p) { mPaint = p; } /** - * @hide + * Returns the bitmap used to draw this NinePatch. */ public Bitmap getBitmap() { return mBitmap; } /** - * Draw a bitmap of nine patches. + * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}. * - * @param canvas A container for the current matrix and clip used to draw the bitmap. - * @param location Where to draw the bitmap. + * @param canvas A container for the current matrix and clip used to draw the NinePatch. + * @param location Where to draw the NinePatch. */ public void draw(Canvas canvas, RectF location) { canvas.drawPatch(this, location, mPaint); } /** - * Draw a bitmap of nine patches. + * Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}. * - * @param canvas A container for the current matrix and clip used to draw the bitmap. - * @param location Where to draw the bitmap. + * @param canvas A container for the current matrix and clip used to draw the NinePatch. + * @param location Where to draw the NinePatch. */ public void draw(Canvas canvas, Rect location) { canvas.drawPatch(this, location, mPaint); } /** - * Draw a bitmap of nine patches. + * Draws the NinePatch. This method will ignore the paint returned + * by {@link #getPaint()} and use the specified paint instead. * - * @param canvas A container for the current matrix and clip used to draw the bitmap. - * @param location Where to draw the bitmap. - * @param paint The Paint to draw through. + * @param canvas A container for the current matrix and clip used to draw the NinePatch. + * @param location Where to draw the NinePatch. + * @param paint The Paint to draw through. */ public void draw(Canvas canvas, Rect location, Paint paint) { canvas.drawPatch(this, location, paint); } - /** - * @hide - */ void drawSoftware(Canvas canvas, RectF location, Paint paint) { - nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mChunk, + nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk, paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity); } - /** - * @hide - */ void drawSoftware(Canvas canvas, Rect location, Paint paint) { - nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mChunk, + nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk, paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity); } @@ -147,32 +180,67 @@ public class NinePatch { public int getDensity() { return mBitmap.mDensity; } - + + /** + * Returns the intrinsic width, in pixels, of this NinePatch. This is equivalent + * to querying the width of the underlying bitmap returned by {@link #getBitmap()}. + */ public int getWidth() { return mBitmap.getWidth(); } + /** + * Returns the intrinsic height, in pixels, of this NinePatch. This is equivalent + * to querying the height of the underlying bitmap returned by {@link #getBitmap()}. + */ public int getHeight() { return mBitmap.getHeight(); } + /** + * Indicates whether this NinePatch contains transparent or translucent pixels. + * This is equivalent to calling <code>getBitmap().hasAlpha()</code> on this + * NinePatch. + */ public final boolean hasAlpha() { return mBitmap.hasAlpha(); } - public final Region getTransparentRegion(Rect location) { - int r = nativeGetTransparentRegion(mBitmap.ni(), mChunk, location); + /** + * Returns a {@link Region} representing the parts of the NinePatch that are + * completely transparent. + * + * @param bounds The location and size of the NinePatch. + * + * @return null if the NinePatch has no transparent region to + * report, else a {@link Region} holding the parts of the specified bounds + * that are transparent. + */ + public final Region getTransparentRegion(Rect bounds) { + int r = nativeGetTransparentRegion(mBitmap.ni(), mNativeChunk, bounds); return r != 0 ? new Region(r) : null; } - + + /** + * Verifies that the specified byte array is a valid 9-patch data chunk. + * + * @param chunk A byte array representing a 9-patch data chunk. + * + * @return True if the specified byte array represents a 9-patch data chunk, + * false otherwise. + */ public native static boolean isNinePatchChunk(byte[] chunk); - private static native void validateNinePatchChunk(int bitmap, byte[] chunk); + /** + * Validates the 9-patch chunk and throws an exception if the chunk is invalid. + * If validation is successful, this method returns a native Res_png_9patch* + * object used by the renderers. + */ + private static native int validateNinePatchChunk(int bitmap, byte[] chunk); + private static native void nativeFinalize(int chunk); private static native void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance, - byte[] c, int paint_instance_or_null, - int destDensity, int srcDensity); + int c, int paint_instance_or_null, int destDensity, int srcDensity); private static native void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance, - byte[] c, int paint_instance_or_null, - int destDensity, int srcDensity); - private static native int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location); + int c, int paint_instance_or_null, int destDensity, int srcDensity); + private static native int nativeGetTransparentRegion(int bitmap, int chunk, Rect location); } diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 1089b7c370d5..6de8c8c0891a 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -286,6 +286,7 @@ void Caches::dumpMemoryUsage(String8 &log) { void Caches::clearGarbage() { textureCache.clearGarbage(); pathCache.clearGarbage(); + patchCache.clearGarbage(); Vector<DisplayList*> displayLists; Vector<Layer*> layers; diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 0d4dd1a9b66a..cebfd268bbd2 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -100,6 +100,10 @@ void DisplayList::clearResources() { caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i)); } + for (size_t i = 0; i < mPatchResources.size(); i++) { + caches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i)); + } + for (size_t i = 0; i < mShaders.size(); i++) { caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i)); caches.resourceCache.destructorLocked(mShaders.itemAt(i)); @@ -134,6 +138,7 @@ void DisplayList::clearResources() { mBitmapResources.clear(); mOwnedBitmapResources.clear(); mFilterResources.clear(); + mPatchResources.clear(); mShaders.clear(); mSourcePaths.clear(); mPaints.clear(); @@ -201,6 +206,13 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde caches.resourceCache.incrementRefcountLocked(resource); } + const Vector<Res_png_9patch*>& patchResources = recorder.getPatchResources(); + for (size_t i = 0; i < patchResources.size(); i++) { + Res_png_9patch* resource = patchResources.itemAt(i); + mPatchResources.add(resource); + caches.resourceCache.incrementRefcountLocked(resource); + } + const Vector<SkiaShader*>& shaders = recorder.getShaders(); for (size_t i = 0; i < shaders.size(); i++) { SkiaShader* resource = shaders.itemAt(i); diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 1417df76b881..194be9e6cd91 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -30,8 +30,11 @@ #include <utils/SortedVector.h> #include <utils/String8.h> #include <utils/Vector.h> + #include <cutils/compiler.h> +#include <androidfw/ResourceTypes.h> + #include "utils/LinearAllocator.h" #include "Debug.h" @@ -484,6 +487,7 @@ private: Vector<SkBitmap*> mBitmapResources; Vector<SkBitmap*> mOwnedBitmapResources; Vector<SkiaColorFilter*> mFilterResources; + Vector<Res_png_9patch*> mPatchResources; Vector<SkPaint*> mPaints; Vector<SkPath*> mPaths; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 5d23e1d574f7..9113092b46eb 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -57,6 +57,10 @@ void DisplayListRenderer::reset() { mCaches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i)); } + for (size_t i = 0; i < mPatchResources.size(); i++) { + mCaches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i)); + } + for (size_t i = 0; i < mShaders.size(); i++) { mCaches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i)); } @@ -74,6 +78,7 @@ void DisplayListRenderer::reset() { mBitmapResources.clear(); mOwnedBitmapResources.clear(); mFilterResources.clear(); + mPatchResources.clear(); mSourcePaths.clear(); mShaders.clear(); @@ -318,6 +323,7 @@ status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, in status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint) { bitmap = refBitmap(bitmap); + patch = refPatch(patch); paint = refPaint(paint); addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, paint)); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 85d610776388..03f50c8d8fad 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -156,6 +156,10 @@ public: return mFilterResources; } + const Vector<Res_png_9patch*>& getPatchResources() const { + return mPatchResources; + } + const Vector<SkiaShader*>& getShaders() const { return mShaders; } @@ -315,9 +319,16 @@ private: return colorFilter; } + inline Res_png_9patch* refPatch(Res_png_9patch* patch) { + mPatchResources.add(patch); + mCaches.resourceCache.incrementRefcount(patch); + return patch; + } + Vector<SkBitmap*> mBitmapResources; Vector<SkBitmap*> mOwnedBitmapResources; Vector<SkiaColorFilter*> mFilterResources; + Vector<Res_png_9patch*> mPatchResources; Vector<SkPaint*> mPaints; DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap; diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index dc69d7f193ca..dc0d98c2cb8d 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -32,7 +32,7 @@ namespace uirenderer { PatchCache::PatchCache(): mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity), - mMeshBuffer(0), mGenerationId(0) { + mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) { char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) { INIT_LOGD(" Setting patch cache size to %skB", property); @@ -97,14 +97,130 @@ void PatchCache::clearCache() { delete i.value(); } mCache.clear(); + + BufferBlock* block = mFreeBlocks; + while (block) { + BufferBlock* next = block->next; + delete block; + block = next; + } + mFreeBlocks = NULL; +} + +void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) { + LruCache<PatchDescription, Patch*>::Iterator i(mCache); + while (i.next()) { + const PatchDescription& key = i.key(); + if (key.getPatch() == patch) { + patchesToRemove.push(patch_pair_t(&key, i.value())); + } + } +} + +void PatchCache::removeDeferred(Res_png_9patch* patch) { + Mutex::Autolock _l(mLock); + mGarbage.push(patch); +} + +void PatchCache::clearGarbage() { + Vector<patch_pair_t> patchesToRemove; + + { // scope for the mutex + Mutex::Autolock _l(mLock); + size_t count = mGarbage.size(); + for (size_t i = 0; i < count; i++) { + remove(patchesToRemove, mGarbage[i]); + } + mGarbage.clear(); + } + + // TODO: We could sort patchesToRemove by offset to merge + // adjacent free blocks + for (size_t i = 0; i < patchesToRemove.size(); i++) { + const patch_pair_t& pair = patchesToRemove[i]; + + // Add a new free block to the list + const Patch* patch = pair.getSecond(); + BufferBlock* block = new BufferBlock(patch->offset, patch->getSize()); + block->next = mFreeBlocks; + mFreeBlocks = block; + + mSize -= patch->getSize(); + + mCache.remove(*pair.getFirst()); + } + +#if DEBUG_PATCHES + if (patchesToRemove.size() > 0) { + dumpFreeBlocks("Removed garbage"); + } +#endif } void PatchCache::createVertexBuffer() { glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); mSize = 0; + mFreeBlocks = new BufferBlock(0, mMaxSize); mGenerationId++; } +/** + * Sets the mesh's offsets and copies its associated vertices into + * the mesh buffer (VBO). + */ +void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { + // This call ensures the VBO exists and that it is bound + init(Caches::getInstance()); + + // If we're running out of space, let's clear the entire cache + uint32_t size = newMesh->getSize(); + if (mSize + size > mMaxSize) { + clearCache(); + createVertexBuffer(); + } + + // Find a block where we can fit the mesh + BufferBlock* previous = NULL; + BufferBlock* block = mFreeBlocks; + while (block) { + // The mesh fits + if (block->size >= size) { + break; + } + previous = block; + block = block->next; + } + + // We have enough space left in the buffer, but it's + // too fragmented, let's clear the cache + if (!block) { + clearCache(); + createVertexBuffer(); + previous = NULL; + block = mFreeBlocks; + } + + // Copy the 9patch mesh in the VBO + newMesh->offset = (GLintptr) (block->offset); + newMesh->textureOffset = newMesh->offset + gMeshTextureOffset; + glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); + + // Remove the block since we've used it entirely + if (block->size == size) { + if (previous) { + previous->next = block->next; + } else { + mFreeBlocks = block->next; + } + } else { + // Resize the block now that it's occupied + block->offset += size; + block->size -= size; + } + + mSize += size; +} + const Patch* PatchCache::get(const AssetAtlas::Entry* entry, const uint32_t bitmapWidth, const uint32_t bitmapHeight, const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { @@ -117,6 +233,7 @@ const Patch* PatchCache::get(const AssetAtlas::Entry* entry, TextureVertex* vertices; if (entry) { + // An atlas entry has a UV mapper vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, entry->uvMapper, patch); } else { @@ -125,24 +242,13 @@ const Patch* PatchCache::get(const AssetAtlas::Entry* entry, } if (vertices) { - // This call ensures the VBO exists and that it is bound - init(Caches::getInstance()); - - // TODO: Simply remove the oldest items until we have enough room - // This will require to keep a list of free blocks in the VBO - uint32_t size = newMesh->getSize(); - if (mSize + size > mMaxSize) { - clearCache(); - createVertexBuffer(); - } - - newMesh->offset = (GLintptr) mSize; - newMesh->textureOffset = newMesh->offset + gMeshTextureOffset; - mSize += size; - - glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); + setupMesh(newMesh, vertices); } +#if DEBUG_PATCHES + dumpFreeBlocks("Adding patch"); +#endif + mCache.put(description, newMesh); return newMesh; } @@ -150,5 +256,17 @@ const Patch* PatchCache::get(const AssetAtlas::Entry* entry, return mesh; } +#if DEBUG_PATCHES +void PatchCache::dumpFreeBlocks(const char* prefix) { + String8 dump; + BufferBlock* block = mFreeBlocks; + while (block) { + dump.appendFormat("->(%d, %d)", block->offset, block->size); + block = block->next; + } + ALOGD("%s: Free blocks%s", prefix, dump.string()); +} +#endif + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index 1829b89dbfc0..9f2c9a59387b 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -26,6 +26,7 @@ #include "AssetAtlas.h" #include "Debug.h" #include "Patch.h" +#include "utils/Pair.h" namespace android { namespace uirenderer { @@ -74,10 +75,20 @@ public: return mGenerationId; } -private: - void clearCache(); - void createVertexBuffer(); + /** + * Removes the entries associated with the specified 9-patch. This is meant + * to be called from threads that are not the EGL context thread (GC thread + * on the VM side for instance.) + */ + void removeDeferred(Res_png_9patch* patch); + /** + * Process deferred removals. + */ + void clearGarbage(); + + +private: struct PatchDescription { PatchDescription(): mPatch(NULL), mBitmapWidth(0), mBitmapHeight(0), mPixelWidth(0), mPixelHeight(0) { @@ -91,6 +102,8 @@ private: hash_t hash() const; + const Res_png_9patch* getPatch() const { return mPatch; } + static int compare(const PatchDescription& lhs, const PatchDescription& rhs); bool operator==(const PatchDescription& other) const { @@ -124,14 +137,50 @@ private: }; // struct PatchDescription + /** + * A buffer block represents an empty range in the mesh buffer + * that can be used to store vertices. + * + * The patch cache maintains a linked-list of buffer blocks + * to track available regions of memory in the VBO. + */ + struct BufferBlock { + BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(NULL) { + } + + uint32_t offset; + uint32_t size; + + BufferBlock* next; + }; // struct BufferBlock + + typedef Pair<const PatchDescription*, Patch*> patch_pair_t; + + void clearCache(); + void createVertexBuffer(); + + void setupMesh(Patch* newMesh, TextureVertex* vertices); + + void remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch); + +#if DEBUG_PATCHES + void dumpFreeBlocks(const char* prefix); +#endif + uint32_t mMaxSize; uint32_t mSize; LruCache<PatchDescription, Patch*> mCache; GLuint mMeshBuffer; + // First available free block inside the mesh buffer + BufferBlock* mFreeBlocks; uint32_t mGenerationId; + + // Garbage tracking, required to handle GC events on the VM side + Vector<Res_png_9patch*> mGarbage; + mutable Mutex mLock; }; // class PatchCache }; // namespace uirenderer diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 3ab40dae9c6f..70ab6e7df66a 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -350,8 +350,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { // Paths /////////////////////////////////////////////////////////////////////////////// -void PathCache::remove(const path_pair_t& pair) { - Vector<PathDescription> pathsToRemove; +void PathCache::remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair) { LruCache<PathDescription, PathTexture*>::Iterator i(mCache); while (i.next()) { @@ -362,10 +361,6 @@ void PathCache::remove(const path_pair_t& pair) { pathsToRemove.push(key); } } - - for (size_t i = 0; i < pathsToRemove.size(); i++) { - mCache.remove(pathsToRemove.itemAt(i)); - } } void PathCache::removeDeferred(SkPath* path) { @@ -374,12 +369,20 @@ void PathCache::removeDeferred(SkPath* path) { } void PathCache::clearGarbage() { - Mutex::Autolock l(mLock); - size_t count = mGarbage.size(); - for (size_t i = 0; i < count; i++) { - remove(mGarbage.itemAt(i)); + Vector<PathDescription> pathsToRemove; + + { // scope for the mutex + Mutex::Autolock l(mLock); + size_t count = mGarbage.size(); + for (size_t i = 0; i < count; i++) { + remove(pathsToRemove, mGarbage.itemAt(i)); + } + mGarbage.clear(); + } + + for (size_t i = 0; i < pathsToRemove.size(); i++) { + mCache.remove(pathsToRemove.itemAt(i)); } - mGarbage.clear(); } /** diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index a191f0e5e2fc..16d20a81c199 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -269,7 +269,7 @@ private: * Removes an entry. * The pair must define first=path, second=sourcePath */ - void remove(const path_pair_t& pair); + void remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair); /** * Ensures there is enough space in the cache for a texture of the specified diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 347bd78ca93b..58fa21c08a3b 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "OpenGLRenderer" + #include <SkPixelRef.h> #include "ResourceCache.h" #include "Caches.h" @@ -79,6 +81,10 @@ void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) { incrementRefcount((void*) filterResource, kColorFilter); } +void ResourceCache::incrementRefcount(Res_png_9patch* patchResource) { + incrementRefcount((void*) patchResource, kNinePatch); +} + void ResourceCache::incrementRefcount(Layer* layerResource) { incrementRefcount((void*) layerResource, kLayer); } @@ -113,6 +119,10 @@ void ResourceCache::incrementRefcountLocked(SkiaColorFilter* filterResource) { incrementRefcountLocked((void*) filterResource, kColorFilter); } +void ResourceCache::incrementRefcountLocked(Res_png_9patch* patchResource) { + incrementRefcountLocked((void*) patchResource, kNinePatch); +} + void ResourceCache::incrementRefcountLocked(Layer* layerResource) { incrementRefcountLocked((void*) layerResource, kLayer); } @@ -142,6 +152,10 @@ void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) { decrementRefcount((void*) filterResource); } +void ResourceCache::decrementRefcount(Res_png_9patch* patchResource) { + decrementRefcount((void*) patchResource); +} + void ResourceCache::decrementRefcount(Layer* layerResource) { decrementRefcount((void*) layerResource); } @@ -179,6 +193,10 @@ void ResourceCache::decrementRefcountLocked(SkiaColorFilter* filterResource) { decrementRefcountLocked((void*) filterResource); } +void ResourceCache::decrementRefcountLocked(Res_png_9patch* patchResource) { + decrementRefcountLocked((void*) patchResource); +} + void ResourceCache::decrementRefcountLocked(Layer* layerResource) { decrementRefcountLocked((void*) layerResource); } @@ -265,6 +283,30 @@ void ResourceCache::destructorLocked(SkiaColorFilter* resource) { } } +void ResourceCache::destructor(Res_png_9patch* resource) { + Mutex::Autolock _l(mLock); + destructorLocked(resource); +} + +void ResourceCache::destructorLocked(Res_png_9patch* resource) { + ssize_t index = mCache->indexOfKey(resource); + ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; + if (ref == NULL) { + if (Caches::hasInstance()) { + Caches::getInstance().patchCache.removeDeferred(resource); + } + // If we're not tracking this resource, just delete it + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + delete[] (int8_t*) resource; + return; + } + ref->destroyed = true; + if (ref->refCount == 0) { + deleteResourceReferenceLocked(resource, ref); + } +} + /** * Return value indicates whether resource was actually recycled, which happens when RefCnt * reaches 0. @@ -335,6 +377,16 @@ void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceRefere delete filter; } break; + case kNinePatch: { + if (Caches::hasInstance()) { + Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource); + } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + int8_t* patch = (int8_t*) resource; + delete[] patch; + } + break; case kLayer: { Layer* layer = (Layer*) resource; Caches::getInstance().deleteLayerDeferred(layer); diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index ab493e54e0b3..ea0c1b5bef71 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -22,7 +22,11 @@ #include <SkBitmap.h> #include <SkiaColorFilter.h> #include <SkiaShader.h> + #include <utils/KeyedVector.h> + +#include <androidfw/ResourceTypes.h> + #include "Layer.h" namespace android { @@ -35,6 +39,7 @@ enum ResourceType { kBitmap, kShader, kColorFilter, + kNinePatch, kPath, kLayer }; @@ -69,35 +74,41 @@ public: void incrementRefcount(SkBitmap* resource); void incrementRefcount(SkiaShader* resource); void incrementRefcount(SkiaColorFilter* resource); + void incrementRefcount(Res_png_9patch* resource); void incrementRefcount(Layer* resource); void incrementRefcountLocked(SkPath* resource); void incrementRefcountLocked(SkBitmap* resource); void incrementRefcountLocked(SkiaShader* resource); void incrementRefcountLocked(SkiaColorFilter* resource); + void incrementRefcountLocked(Res_png_9patch* resource); void incrementRefcountLocked(Layer* resource); void decrementRefcount(SkBitmap* resource); void decrementRefcount(SkPath* resource); void decrementRefcount(SkiaShader* resource); void decrementRefcount(SkiaColorFilter* resource); + void decrementRefcount(Res_png_9patch* resource); void decrementRefcount(Layer* resource); void decrementRefcountLocked(SkBitmap* resource); void decrementRefcountLocked(SkPath* resource); void decrementRefcountLocked(SkiaShader* resource); void decrementRefcountLocked(SkiaColorFilter* resource); + void decrementRefcountLocked(Res_png_9patch* resource); void decrementRefcountLocked(Layer* resource); void destructor(SkPath* resource); void destructor(SkBitmap* resource); void destructor(SkiaShader* resource); void destructor(SkiaColorFilter* resource); + void destructor(Res_png_9patch* resource); void destructorLocked(SkPath* resource); void destructorLocked(SkBitmap* resource); void destructorLocked(SkiaShader* resource); void destructorLocked(SkiaColorFilter* resource); + void destructorLocked(Res_png_9patch* resource); bool recycle(SkBitmap* resource); bool recycleLocked(SkBitmap* resource); |