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
diff --git a/api/current.txt b/api/current.txt
index facaabe..ee4ef67 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9155,8 +9155,11 @@
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 1f35c1d..a441c39 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -781,7 +781,7 @@
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 @@
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 9c5b33d..8b2a2ef 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 @@
* 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 @@
}
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 273947f..b6fc38d 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 @@
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 684b1c1..7e6aeae 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 @@
}
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);
+ 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;
+ }
- 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();
+ 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);
+ 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;
+ 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);
+ 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);
+ NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
- canvas->restore();
- }
+ 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 @@
#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 5b6cff0..87ebbd2 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -432,15 +432,9 @@
}
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 @@
{ "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 ab1c328..528d9de 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -18,11 +18,8 @@
/**
- * 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 @@
*/
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 @@
/**
* 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 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 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 1089b7c..6de8c8c 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -286,6 +286,7 @@
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 0d4dd1a..cebfd26 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -100,6 +100,10 @@
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 @@
mBitmapResources.clear();
mOwnedBitmapResources.clear();
mFilterResources.clear();
+ mPatchResources.clear();
mShaders.clear();
mSourcePaths.clear();
mPaints.clear();
@@ -201,6 +206,13 @@
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 1417df7..194be9e 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 @@
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 5d23e1d..9113092 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -57,6 +57,10 @@
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 @@
mBitmapResources.clear();
mOwnedBitmapResources.clear();
mFilterResources.clear();
+ mPatchResources.clear();
mSourcePaths.clear();
mShaders.clear();
@@ -318,6 +323,7 @@
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 85d6107..03f50c8 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -156,6 +156,10 @@
return mFilterResources;
}
+ const Vector<Res_png_9patch*>& getPatchResources() const {
+ return mPatchResources;
+ }
+
const Vector<SkiaShader*>& getShaders() const {
return mShaders;
}
@@ -315,9 +319,16 @@
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 dc69d7f..dc0d98c 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -32,7 +32,7 @@
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 @@
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 @@
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 @@
}
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 @@
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 1829b89..9f2c9a5 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 @@
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 @@
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 @@
}; // 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 3ab40da..70ab6e7 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -350,8 +350,7 @@
// 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 @@
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::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();
}
- mGarbage.clear();
+
+ for (size_t i = 0; i < pathsToRemove.size(); i++) {
+ mCache.remove(pathsToRemove.itemAt(i));
+ }
}
/**
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index a191f0e..16d20a8 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -269,7 +269,7 @@
* 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 347bd78..58fa21c 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 @@
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 @@
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 @@
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 @@
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::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 @@
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 ab493e5..ea0c1b5 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 @@
kBitmap,
kShader,
kColorFilter,
+ kNinePatch,
kPath,
kLayer
};
@@ -69,35 +74,41 @@
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);