Optimize 9patch rendering.
This change detects empty quads in 9patches and removes them from
the mesh to avoid unnecessary blending.
Change-Id: I4500566fb4cb6845d64dcb59b522c0be7a0ec704
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 2001919..dfa614c 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -257,8 +257,9 @@
Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage);
Res_png_9patch::deserialize(patch);
- renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0],
- patch->numXDivs, patch->numYDivs, left, top, right, bottom, paint);
+ renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0], &patch->colors[0],
+ patch->numXDivs, patch->numYDivs, patch->numColors,
+ left, top, right, bottom, paint);
env->ReleaseByteArrayElements(chunks, storage, 0);
}
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index df6feba..6de4d84 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -86,7 +86,7 @@
mPaint != null ? mPaint.mNativePaint : 0,
canvas.mDensity, mBitmap.mDensity);
} else {
- canvas.drawPatch(mBitmap, mChunk, location, null);
+ canvas.drawPatch(mBitmap, mChunk, location, mPaint);
}
}
@@ -104,7 +104,7 @@
canvas.mDensity, mBitmap.mDensity);
} else {
mRect.set(location);
- canvas.drawPatch(mBitmap, mChunk, mRect, null);
+ canvas.drawPatch(mBitmap, mChunk, mRect, mPaint);
}
}
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 61e5408..b3517c4 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -236,16 +236,19 @@
case DrawPatch: {
int32_t* xDivs = NULL;
int32_t* yDivs = NULL;
+ uint32_t* colors = NULL;
uint32_t xDivsCount = 0;
uint32_t yDivsCount = 0;
+ int8_t numColors = 0;
SkBitmap* bitmap = getBitmap();
xDivs = getInts(xDivsCount);
yDivs = getInts(yDivsCount);
+ colors = getUInts(numColors);
- renderer.drawPatch(bitmap, xDivs, yDivs, xDivsCount, yDivsCount,
- getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+ renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount,
+ numColors, getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
}
break;
case DrawColor: {
@@ -450,12 +453,13 @@
}
void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- uint32_t width, uint32_t height, float left, float top, float right, float bottom,
- const SkPaint* paint) {
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, const SkPaint* paint) {
addOp(DisplayList::DrawPatch);
addBitmap(bitmap);
addInts(xDivs, width);
addInts(yDivs, height);
+ addUInts(colors, numColors);
addBounds(left, top, right, bottom);
addPaint(paint);
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 0fbfce1..9e6d5b1 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -169,6 +169,11 @@
return (int32_t*) mReader.skip(count * sizeof(int32_t));
}
+ uint32_t* getUInts(int8_t& count) {
+ count = getInt();
+ return (uint32_t*) mReader.skip(count * sizeof(uint32_t));
+ }
+
float* getFloats(int& count) {
count = getInt();
return (float*) mReader.skip(count * sizeof(float));
@@ -236,8 +241,8 @@
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint);
void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- uint32_t width, uint32_t height, float left, float top, float right, float bottom,
- const SkPaint* paint);
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, const SkPaint* paint);
void drawColor(int color, SkXfermode::Mode mode);
void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
void drawPath(SkPath* path, SkPaint* paint);
@@ -291,6 +296,13 @@
}
}
+ void addUInts(const uint32_t* values, int8_t count) {
+ mWriter.writeInt(count);
+ for (int8_t i = 0; i < count; i++) {
+ mWriter.writeInt(values[i]);
+ }
+ }
+
inline void addFloat(float value) {
mWriter.writeScalar(value);
}
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 219fd5e..5502e66 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -24,6 +24,7 @@
#include <SkMatrix.h>
+#include "utils/Compare.h"
#include "Matrix.h"
namespace android {
@@ -53,9 +54,6 @@
mSimpleMatrix = true;
}
-#define EPSILON 0.00001f
-#define almost(u, v) (fabs((u) - (v)) < EPSILON)
-
bool Matrix4::changesBounds() {
return !(almost(data[0], 1.0f) && almost(data[1], 0.0f) && almost(data[2], 0.0f) &&
almost(data[4], 0.0f) && almost(data[5], 1.0f) && almost(data[6], 0.0f) &&
diff --git a/libs/hwui/OpenGLDebugRenderer.cpp b/libs/hwui/OpenGLDebugRenderer.cpp
index b9583e5..d492e23 100644
--- a/libs/hwui/OpenGLDebugRenderer.cpp
+++ b/libs/hwui/OpenGLDebugRenderer.cpp
@@ -72,11 +72,11 @@
}
void OpenGLDebugRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- uint32_t width, uint32_t height, float left, float top, float right, float bottom,
- const SkPaint* paint) {
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, const SkPaint* paint) {
mPrimitivesCount++;
StopWatch w("drawPatch");
- OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, width, height,
+ OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
left, top, right, bottom, paint);
}
diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h
index 2ac19ae..4997ef3 100644
--- a/libs/hwui/OpenGLDebugRenderer.h
+++ b/libs/hwui/OpenGLDebugRenderer.h
@@ -46,8 +46,8 @@
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint);
void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- uint32_t width, uint32_t height, float left, float top, float right, float bottom,
- const SkPaint* paint);
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, const SkPaint* paint);
void drawColor(int color, SkXfermode::Mode mode);
void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
void drawPath(SkPath* path, SkPaint* paint);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cb23680..e965e61 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -689,8 +689,8 @@
}
void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- uint32_t width, uint32_t height, float left, float top, float right, float bottom,
- const SkPaint* paint) {
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, const SkPaint* paint) {
if (quickReject(left, top, right, bottom)) {
return;
}
@@ -705,7 +705,7 @@
getAlphaAndMode(paint, &alpha, &mode);
const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
- right - left, bottom - top, xDivs, yDivs, width, height);
+ right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
// Specify right and bottom as +1.0f from left/top to prevent scaling since the
// patch mesh already defines the final size
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4caa8fb..9900a27 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -101,8 +101,8 @@
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint);
virtual void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- uint32_t width, uint32_t height, float left, float top, float right, float bottom,
- const SkPaint* paint);
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, const SkPaint* paint);
virtual void drawColor(int color, SkXfermode::Mode mode);
virtual void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
virtual void drawPath(SkPath* path, SkPaint* paint);
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index ca0a0b1..560decf 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -18,6 +18,8 @@
#include <cmath>
+#include <utils/Log.h>
+
#include "Patch.h"
namespace android {
@@ -27,9 +29,9 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Patch::Patch(const uint32_t xCount, const uint32_t yCount) {
+Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads) {
// 2 triangles per patch, 3 vertices per triangle
- verticesCount = (xCount + 1) * (yCount + 1) * 2 * 3;
+ verticesCount = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3;
vertices = new TextureVertex[verticesCount];
}
@@ -43,7 +45,8 @@
void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
float left, float top, float right, float bottom,
- const int32_t* xDivs, const int32_t* yDivs, const uint32_t width, const uint32_t height) {
+ const int32_t* xDivs, const int32_t* yDivs,
+ const uint32_t width, const uint32_t height, const uint32_t colorKey) {
const uint32_t xStretchCount = (width + 1) >> 1;
const uint32_t yStretchCount = (height + 1) >> 1;
@@ -75,6 +78,7 @@
}
TextureVertex* vertex = vertices;
+ uint32_t quadCount = 0;
float previousStepY = 0.0f;
@@ -94,7 +98,7 @@
float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight;
generateRow(vertex, y1, y2, v1, v2, xDivs, width, stretchX,
- right - left, bitmapWidth);
+ right - left, bitmapWidth, quadCount, colorKey);
y1 = y2;
v1 = (stepY + 0.5f) / bitmapHeight;
@@ -103,11 +107,12 @@
}
generateRow(vertex, y1, bottom - top, v1, 1.0f, xDivs, width, stretchX,
- right - left, bitmapWidth);
+ right - left, bitmapWidth, quadCount, colorKey);
}
inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
- const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth) {
+ const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth,
+ uint32_t& quadCount, const uint32_t colorKey) {
float previousStepX = 0.0f;
float x1 = 0.0f;
@@ -126,7 +131,7 @@
}
float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth;
- generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2);
+ generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount, colorKey);
x1 = x2;
u1 = (stepX + 0.5f) / bitmapWidth;
@@ -134,11 +139,15 @@
previousStepX = stepX;
}
- generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2);
+ generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount, colorKey);
}
inline void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
- float u1, float v1, float u2, float v2) {
+ float u1, float v1, float u2, float v2, uint32_t& quadCount, const uint32_t colorKey) {
+ if (((colorKey >> quadCount++) & 0x1) == 1) {
+ return;
+ }
+
// Left triangle
TextureVertex::set(vertex++, x1, y1, u1, v1);
TextureVertex::set(vertex++, x2, y1, u2, v1);
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index a9109cd..54c9d6c 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -18,29 +18,36 @@
#define ANDROID_UI_PATCH_H
#include <sys/types.h>
-#include <cstring>
#include "Vertex.h"
+#include "utils/Compare.h"
namespace android {
namespace uirenderer {
+///////////////////////////////////////////////////////////////////////////////
+// 9-patch structures
+///////////////////////////////////////////////////////////////////////////////
+
/**
* Description of a patch.
*/
struct PatchDescription {
- PatchDescription(): bitmapWidth(0), bitmapHeight(0),
- pixelWidth(0), pixelHeight(0), xCount(0), yCount(0) { }
+ PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0),
+ xCount(0), yCount(0), emptyCount(0), colorKey(0) { }
PatchDescription(const float bitmapWidth, const float bitmapHeight,
const float pixelWidth, const float pixelHeight,
- const uint32_t xCount, const uint32_t yCount):
+ const uint32_t xCount, const uint32_t yCount,
+ const int8_t emptyCount, const uint32_t colorKey):
bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight),
pixelWidth(pixelWidth), pixelHeight(pixelHeight),
- xCount(xCount), yCount(yCount) { }
+ xCount(xCount), yCount(yCount),
+ emptyCount(emptyCount), colorKey(colorKey) { }
PatchDescription(const PatchDescription& description):
bitmapWidth(description.bitmapWidth), bitmapHeight(description.bitmapHeight),
pixelWidth(description.pixelWidth), pixelHeight(description.pixelHeight),
- xCount(description.xCount), yCount(description.yCount) { }
+ xCount(description.xCount), yCount(description.yCount),
+ emptyCount(description.emptyCount), colorKey(description.colorKey) { }
float bitmapWidth;
float bitmapHeight;
@@ -48,9 +55,26 @@
float pixelHeight;
uint32_t xCount;
uint32_t yCount;
+ int8_t emptyCount;
+ uint32_t colorKey;
bool operator<(const PatchDescription& rhs) const {
- return memcmp(this, &rhs, sizeof(PatchDescription)) < 0;
+ compare(bitmapWidth) {
+ compare(bitmapHeight) {
+ compare(pixelWidth) {
+ compare(pixelHeight) {
+ compareI(xCount) {
+ compareI(yCount) {
+ compareI(emptyCount) {
+ compareI(colorKey) return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
}
}; // struct PatchDescription
@@ -59,13 +83,14 @@
* indices to render the vertices.
*/
struct Patch {
- Patch(const uint32_t xCount, const uint32_t yCount);
+ Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads = 0);
~Patch();
void updateVertices(const float bitmapWidth, const float bitmapHeight,
float left, float top, float right, float bottom,
const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t width, const uint32_t height);
+ const uint32_t width, const uint32_t height,
+ const uint32_t colorKey = 0);
TextureVertex* vertices;
uint32_t verticesCount;
@@ -73,10 +98,12 @@
private:
static inline void generateRow(TextureVertex*& vertex, float y1, float y2,
float v1, float v2, const int32_t xDivs[], uint32_t xCount,
- float stretchX, float width, float bitmapWidth);
+ float stretchX, float width, float bitmapWidth,
+ uint32_t& quadCount, const uint32_t colorKey);
static inline void generateQuad(TextureVertex*& vertex,
float x1, float y1, float x2, float y2,
- float u1, float v1, float u2, float v2);
+ float u1, float v1, float u2, float v2,
+ uint32_t& quadCount, const uint32_t colorKey);
}; // struct Patch
}; // namespace uirenderer
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index eca5e4d..4762d21 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "OpenGLRenderer"
#include <utils/Log.h>
-#include <utils/ResourceTypes.h>
#include "PatchCache.h"
#include "Properties.h"
@@ -45,7 +44,7 @@
void PatchCache::clear() {
size_t count = mCache.size();
- for (int i = 0; i < count; i++) {
+ for (size_t i = 0; i < count; i++) {
delete mCache.valueAt(i);
}
mCache.clear();
@@ -53,11 +52,23 @@
Patch* PatchCache::get(const float bitmapWidth, const float bitmapHeight,
const float pixelWidth, const float pixelHeight,
- const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t width, const uint32_t height) {
+ const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
+ const uint32_t width, const uint32_t height, const int8_t numColors) {
+
+ int8_t transparentQuads = 0;
+ uint32_t colorKey = 0;
+
+ if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
+ for (int8_t i = 0; i < numColors; i++) {
+ if (colors[i] == 0x0) {
+ transparentQuads++;
+ colorKey |= 0x1 << i;
+ }
+ }
+ }
const PatchDescription description(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, width, height);
+ pixelWidth, pixelHeight, width, height, transparentQuads, colorKey);
ssize_t index = mCache.indexOfKey(description);
Patch* mesh = NULL;
@@ -66,11 +77,13 @@
}
if (!mesh) {
- PATCH_LOGD("Creating new patch mesh, w=%d h=%d", width, height);
+ PATCH_LOGD("New patch mesh "
+ "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f",
+ width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight);
- mesh = new Patch(width, height);
+ mesh = new Patch(width, height, transparentQuads);
mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f,
- pixelWidth, pixelHeight, xDivs, yDivs, width, height);
+ pixelWidth, pixelHeight, xDivs, yDivs, width, height, colorKey);
if (mCache.size() >= mMaxEntries) {
delete mCache.valueAt(mCache.size() - 1);
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 08f78a3..2a8ac82 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -29,7 +29,7 @@
///////////////////////////////////////////////////////////////////////////////
// Debug
-#define DEBUG_PATCHES 0
+#define DEBUG_PATCHES 1
// Debug
#if DEBUG_PATCHES
@@ -50,8 +50,8 @@
Patch* get(const float bitmapWidth, const float bitmapHeight,
const float pixelWidth, const float pixelHeight,
- const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t width, const uint32_t height);
+ const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
+ const uint32_t width, const uint32_t height, const int8_t numColors);
void clear();
private:
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 596dfab..0c74261 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -22,6 +22,7 @@
#include <SkPath.h>
#include "Texture.h"
+#include "utils/Compare.h"
#include "utils/GenerationCache.h"
namespace android {
@@ -63,7 +64,18 @@
float strokeWidth;
bool operator<(const PathCacheEntry& rhs) const {
- return memcmp(this, &rhs, sizeof(PathCacheEntry)) < 0;
+ compareI(path) {
+ compareI(join) {
+ compareI(cap) {
+ compareI(style) {
+ compare(miter) {
+ compare(strokeWidth) return false;
+ }
+ }
+ }
+ }
+ }
+ return false;
}
}; // struct PathCacheEntry
diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h
new file mode 100644
index 0000000..754b470
--- /dev/null
+++ b/libs/hwui/utils/Compare.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_COMPARE_H
+#define ANDROID_UI_COMPARE_H
+
+#include <cmath>
+
+#define EPSILON 0.00001f
+
+#define almost(u, v) (fabs((u) - (v)) < EPSILON)
+
+/**
+ * Compare floats.
+ */
+#define compare(a) \
+ if (a < rhs.a) return true; \
+ if (almost(a, rhs.a))
+
+/**
+ * Compare integers.
+ */
+#define compareI(a) \
+ if (a < rhs.a) return true; \
+ if (a == rhs.a)
+
+#endif // ANDROID_UI_COMPARE_H