Optimize 9patch rendering.

This change detects empty quads in 9patches and removes them from
the mesh to avoid unnecessary blending.

Change-Id: I4500566fb4cb6845d64dcb59b522c0be7a0ec704
This commit is contained in:
Romain Guy
2010-10-12 15:59:26 -07:00
parent 5a3b8e698e
commit 4bb942083a
15 changed files with 171 additions and 55 deletions

View File

@@ -257,8 +257,9 @@ static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject canvas,
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);
}

View File

@@ -86,7 +86,7 @@ public class NinePatch {
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 @@ public class NinePatch {
canvas.mDensity, mBitmap.mDensity);
} else {
mRect.set(location);
canvas.drawPatch(mBitmap, mChunk, mRect, null);
canvas.drawPatch(mBitmap, mChunk, mRect, mPaint);
}
}

View File

@@ -236,16 +236,19 @@ void DisplayList::replay(OpenGLRenderer& renderer) {
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::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcT
}
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);
}

View File

@@ -169,6 +169,11 @@ private:
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 @@ public:
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 @@ private:
}
}
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);
}

View File

@@ -24,6 +24,7 @@
#include <SkMatrix.h>
#include "utils/Compare.h"
#include "Matrix.h"
namespace android {
@@ -53,9 +54,6 @@ void Matrix4::loadIdentity() {
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) &&

View File

@@ -72,11 +72,11 @@ void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcT
}
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);
}

View File

@@ -46,8 +46,8 @@ public:
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);

View File

@@ -689,8 +689,8 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
}
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 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int
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

View File

@@ -101,8 +101,8 @@ public:
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);

View File

@@ -18,6 +18,8 @@
#include <cmath>
#include <utils/Log.h>
#include "Patch.h"
namespace android {
@@ -27,9 +29,9 @@ namespace uirenderer {
// 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 @@ Patch::~Patch() {
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 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
}
TextureVertex* vertex = vertices;
uint32_t quadCount = 0;
float previousStepY = 0.0f;
@@ -94,7 +98,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
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 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
}
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 @@ inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float
}
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 @@ inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float
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);

View File

@@ -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 @@ struct PatchDescription {
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 @@ struct PatchDescription {
* 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 @@ struct Patch {
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

View File

@@ -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 @@ PatchCache::~PatchCache() {
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 @@ void PatchCache::clear() {
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 @@ Patch* PatchCache::get(const float bitmapWidth, const float bitmapHeight,
}
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);

View File

@@ -29,7 +29,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Debug
#define DEBUG_PATCHES 0
#define DEBUG_PATCHES 1
// Debug
#if DEBUG_PATCHES
@@ -50,8 +50,8 @@ public:
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:

View File

@@ -22,6 +22,7 @@
#include <SkPath.h>
#include "Texture.h"
#include "utils/Compare.h"
#include "utils/GenerationCache.h"
namespace android {
@@ -63,7 +64,18 @@ struct PathCacheEntry {
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

40
libs/hwui/utils/Compare.h Normal file
View File

@@ -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