Draw n-patches using OpenGL.

Currently only tested with title bars and buttons.

Change-Id: I8263a9281898dc0e943b1b8412827fe55639b9d6
This commit is contained in:
Romain Guy
2010-07-08 19:17:03 -07:00
parent 26be805f42
commit f7f93556c8
11 changed files with 506 additions and 31 deletions

View File

@@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
LayerCache.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
PatchCache.cpp \
Program.cpp \
TextureCache.cpp

View File

@@ -17,6 +17,8 @@
#ifndef ANDROID_UI_LAYER_H
#define ANDROID_UI_LAYER_H
#include <sys/types.h>
#include <GLES2/gl2.h>
#include <SkXfermode.h>
@@ -54,7 +56,7 @@ struct LayerSize {
bool operator==(const LayerSize& rhs) const {
return width == rhs.width && height == rhs.height;
}
};
}; // struct LayerSize
/**
* A layer has dimensions and is backed by an OpenGL texture.

View File

@@ -50,7 +50,7 @@ public:
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
void operator()(LayerSize& bitmap, Layer*& texture);
void operator()(LayerSize& size, Layer*& layer);
/**
* Returns the layer of specified dimensions. If not suitable layer

View File

@@ -40,6 +40,7 @@ namespace uirenderer {
#define DEFAULT_TEXTURE_CACHE_SIZE 20
#define DEFAULT_LAYER_CACHE_SIZE 10
#define DEFAULT_PATCH_CACHE_SIZE 100
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
@@ -95,7 +96,8 @@ static const Blender gBlends[] = {
OpenGLRenderer::OpenGLRenderer():
mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)) {
mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
mPatchCache(DEFAULT_PATCH_CACHE_SIZE) {
LOGD("Create OpenGLRenderer");
char property[PROPERTY_VALUE_MAX];
@@ -298,7 +300,6 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
LayerSize size(right - left, bottom - top);
// TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
Layer* layer = mLayerCache.get(size, previousFbo);
if (!layer) {
return false;
@@ -472,8 +473,110 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, const SkPaint* paint) {
// TODO: Implement
LOGD("Draw 9patch, paddingLeft=%d", patch->paddingLeft);
const Texture* texture = mTextureCache.get(bitmap);
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
const uint32_t width = patch->numXDivs;
const uint32_t height = patch->numYDivs;
Patch* mesh = mPatchCache.get(patch);
const uint32_t xStretchCount = (width + 1) >> 1;
const uint32_t yStretchCount = (height + 1) >> 1;
const int32_t* xDivs = &patch->xDivs[0];
const int32_t* yDivs = &patch->yDivs[0];
float xStretch = 0;
float yStretch = 0;
float xStretchTex = 0;
float yStretchTex = 0;
const float meshWidth = right - left;
const float bitmapWidth = float(bitmap->width());
const float bitmapHeight = float(bitmap->height());
if (xStretchCount > 0) {
uint32_t stretchSize = 0;
for (uint32_t i = 1; i < width; i += 2) {
stretchSize += xDivs[i] - xDivs[i - 1];
}
xStretchTex = (stretchSize / bitmapWidth) / xStretchCount;
const float fixed = bitmapWidth - stretchSize;
xStretch = (right - left - fixed) / xStretchCount;
}
if (yStretchCount > 0) {
uint32_t stretchSize = 0;
for (uint32_t i = 1; i < height; i += 2) {
stretchSize += yDivs[i] - yDivs[i - 1];
}
yStretchTex = (stretchSize / bitmapHeight) / yStretchCount;
const float fixed = bitmapHeight - stretchSize;
yStretch = (bottom - top - fixed) / yStretchCount;
}
float vy = 0.0f;
float ty = 0.0f;
TextureVertex* vertex = mesh->vertices;
generateVertices(vertex, 0.0f, 0.0f, xDivs, width, xStretch, xStretchTex,
meshWidth, bitmapWidth);
vertex += width + 2;
for (uint32_t y = 0; y < height; y++) {
if (y & 1) {
vy += yStretch;
ty += yStretchTex;
} else {
const float step = float(yDivs[y]);
vy += step;
ty += step / bitmapHeight;
}
generateVertices(vertex, vy, ty, xDivs, width, xStretch, xStretchTex,
meshWidth, bitmapWidth);
vertex += width + 2;
}
generateVertices(vertex, bottom - top, 1.0f, xDivs, width, xStretch, xStretchTex,
meshWidth, bitmapWidth);
// Specify right and bottom as +1.0f from left/top to prevent scaling since the
// patch mesh already defines the final size
drawTextureMesh(left, top, left + 1.0f, top + 1.0f, texture->id, alpha, mode, texture->blend,
true, &mesh->vertices[0].position[0], &mesh->vertices[0].texture[0], mesh->indices,
mesh->indicesCount);
}
void OpenGLRenderer::generateVertices(TextureVertex* vertex, float y, float v,
const int32_t xDivs[], uint32_t xCount, float xStretch, float xStretchTex,
float width, float widthTex) {
float vx = 0.0f;
float tx = 0.0f;
TextureVertex::set(vertex, vx, y, tx, v);
vertex++;
for (uint32_t x = 0; x < xCount; x++) {
if (x & 1) {
vx += xStretch;
tx += xStretchTex;
} else {
const float step = float(xDivs[x]);
vx += step;
tx += step / widthTex;
}
TextureVertex::set(vertex, vx, y, tx, v);
vertex++;
}
TextureVertex::set(vertex, width, y, 1.0f, v);
vertex++;
}
void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -538,6 +641,13 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied) {
drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend, isPremultiplied,
&mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
}
void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied,
GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) {
mModelView.loadTranslate(left, top, 0.0f);
mModelView.scale(right - left, bottom - top, 1.0f);
@@ -560,20 +670,21 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b
glActiveTexture(GL_TEXTURE0);
glUniform1i(mDrawTextureShader->sampler, 0);
const GLvoid* p = &mDrawTextureVertices[0].position[0];
const GLvoid* t = &mDrawTextureVertices[0].texture[0];
glEnableVertexAttribArray(mDrawTextureShader->position);
glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE,
gDrawTextureVertexStride, p);
gDrawTextureVertexStride, vertices);
glEnableVertexAttribArray(mDrawTextureShader->texCoords);
glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE,
gDrawTextureVertexStride, t);
gDrawTextureVertexStride, texCoords);
glVertexAttrib4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
if (!indices) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
} else {
glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
}
glDisableVertexAttribArray(mDrawTextureShader->position);
glDisableVertexAttribArray(mDrawTextureShader->texCoords);

View File

@@ -32,10 +32,10 @@
#include "Program.h"
#include "Rect.h"
#include "Snapshot.h"
#include "Texture.h"
#include "Layer.h"
#include "TextureCache.h"
#include "LayerCache.h"
#include "PatchCache.h"
#include "Vertex.h"
namespace android {
namespace uirenderer {
@@ -44,22 +44,6 @@ namespace uirenderer {
// Support
///////////////////////////////////////////////////////////////////////////////
/**
* Simple structure to describe a vertex with a position.
* This is used to draw filled rectangles without a texture.
*/
struct SimpleVertex {
float position[2];
}; // struct SimpleVertex
/**
* Simple structure to describe a vertex with a position and a texture.
*/
struct TextureVertex {
float position[2];
float texture[2];
}; // struct TextureVertex
/**
* Structure mapping Skia xfermodes to OpenGL blending factors.
*/
@@ -197,6 +181,13 @@ private:
void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied = false);
/**
* TODO: documentation
*/
void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture,
float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied,
GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0);
/**
* Resets the texture coordinates stored in mDrawTextureVertices. Setting the values
* back to default is achieved by calling:
@@ -220,6 +211,12 @@ private:
*/
inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
/**
* TODO: documentation
*/
inline void generateVertices(TextureVertex* vertex, float y, float v, const int32_t xDivs[],
uint32_t xCount, float xStretch, float xStretchTex, float width, float widthTex);
// Dimensions of the drawing surface
int mWidth, mHeight;
@@ -243,9 +240,9 @@ private:
// Used to draw textured quads
TextureVertex mDrawTextureVertices[4];
// Used to cache all drawBitmap textures
TextureCache mTextureCache;
LayerCache mLayerCache;
PatchCache mPatchCache;
}; // class OpenGLRenderer
}; // namespace uirenderer

129
libs/hwui/Patch.h Normal file
View File

@@ -0,0 +1,129 @@
/*
* 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_PATCH_H
#define ANDROID_UI_PATCH_H
#include <sys/types.h>
#include <cstring>
#include "Vertex.h"
namespace android {
namespace uirenderer {
/**
* Description of a patch.
*/
struct PatchDescription {
PatchDescription(): xCount(0), yCount(0) { }
PatchDescription(const uint32_t xCount, const uint32_t yCount):
xCount(xCount), yCount(yCount) { }
PatchDescription(const PatchDescription& description):
xCount(description.xCount), yCount(description.yCount) { }
uint32_t xCount;
uint32_t yCount;
bool operator<(const PatchDescription& rhs) const {
if (xCount == rhs.xCount) {
return yCount < rhs.yCount;
}
return xCount < rhs.xCount;
}
bool operator==(const PatchDescription& rhs) const {
return xCount == rhs.xCount && yCount == rhs.yCount;
}
}; // struct PatchDescription
/**
* An OpenGL patch. This contains an array of vertices and an array of
* indices to render the vertices.
*/
struct Patch {
Patch(const uint32_t xCount, const uint32_t yCount): xCount(xCount + 2), yCount(yCount + 2) {
verticesCount = (xCount + 2) * (yCount + 2);
vertices = new TextureVertex[verticesCount];
// 2 triangles per patch, 3 vertices per triangle
indicesCount = (xCount + 1) * (yCount + 1) * 2 * 3;
indices = new uint16_t[indicesCount];
const uint32_t xNum = xCount + 1;
const uint32_t yNum = yCount + 1;
uint16_t* startIndices = indices;
uint32_t n = 0;
for (uint32_t y = 0; y < yNum; y++) {
for (uint32_t x = 0; x < xNum; x++) {
*startIndices++ = n;
*startIndices++ = n + 1;
*startIndices++ = n + xNum + 2;
*startIndices++ = n;
*startIndices++ = n + xNum + 2;
*startIndices++ = n + xNum + 1;
n += 1;
}
n += 1;
}
}
~Patch() {
delete indices;
delete vertices;
}
void dump() {
LOGD("Vertices [");
for (uint32_t y = 0; y < yCount; y++) {
char buffer[512];
buffer[0] = '\0';
uint32_t offset = 0;
for (uint32_t x = 0; x < xCount; x++) {
TextureVertex* v = &vertices[y * xCount + x];
offset += sprintf(&buffer[offset], " (%.4f,%.4f)-(%.4f,%.4f)",
v->position[0], v->position[1], v->texture[0], v->texture[1]);
}
LOGD(" [%s ]", buffer);
}
LOGD("]\nIndices [ ");
char buffer[4096];
buffer[0] = '\0';
uint32_t offset = 0;
for (uint32_t i = 0; i < indicesCount; i++) {
offset += sprintf(&buffer[offset], "%d ", indices[i]);
}
LOGD(" %s\n]", buffer);
}
uint32_t xCount;
uint32_t yCount;
uint16_t* indices;
uint32_t indicesCount;
TextureVertex* vertices;
uint32_t verticesCount;
}; // struct Patch
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_UI_PATCH_H

72
libs/hwui/PatchCache.cpp Normal file
View File

@@ -0,0 +1,72 @@
/*
* 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.
*/
#define LOG_TAG "OpenGLRenderer"
#include <utils/Log.h>
#include <utils/ResourceTypes.h>
#include "PatchCache.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
PatchCache::PatchCache(uint32_t maxEntries): mCache(maxEntries) {
}
PatchCache::~PatchCache() {
clear();
}
///////////////////////////////////////////////////////////////////////////////
// Callbacks
///////////////////////////////////////////////////////////////////////////////
void PatchCache::operator()(PatchDescription& description, Patch*& mesh) {
if (mesh) delete mesh;
}
///////////////////////////////////////////////////////////////////////////////
// Caching
///////////////////////////////////////////////////////////////////////////////
void PatchCache::clear() {
mCache.setOnEntryRemovedListener(this);
mCache.clear();
mCache.setOnEntryRemovedListener(NULL);
}
Patch* PatchCache::get(const Res_png_9patch* patch) {
const uint32_t width = patch->numXDivs;
const uint32_t height = patch->numYDivs;
const PatchDescription description(width, height);
Patch* mesh = mCache.get(description);
if (!mesh) {
PATCH_LOGD("Creating new patch mesh, w=%d h=%d", width, height);
mesh = new Patch(width, height);
mCache.put(description, mesh);
}
return mesh;
}
}; // namespace uirenderer
}; // namespace android

65
libs/hwui/PatchCache.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* 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_PATCH_CACHE_H
#define ANDROID_UI_PATCH_CACHE_H
#include "Patch.h"
#include "GenerationCache.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
// Debug
#define DEBUG_PATCHES 0
// Debug
#if DEBUG_PATCHES
#define PATCH_LOGD(...) LOGD(__VA_ARGS__)
#else
#define PATCH_LOGD(...)
#endif
///////////////////////////////////////////////////////////////////////////////
// Cache
///////////////////////////////////////////////////////////////////////////////
class PatchCache: public OnEntryRemoved<PatchDescription, Patch*> {
public:
PatchCache(uint32_t maxCapacity);
~PatchCache();
/**
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
void operator()(PatchDescription& description, Patch*& mesh);
Patch* get(const Res_png_9patch* patch);
void clear();
private:
GenerationCache<PatchDescription, Patch*> mCache;
}; // class PatchCache
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_UI_PATCH_CACHE_H

49
libs/hwui/Vertex.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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_VERTEX_H
#define ANDROID_UI_VERTEX_H
namespace android {
namespace uirenderer {
/**
* Simple structure to describe a vertex with a position.
* This is used to draw filled rectangles without a texture.
*/
struct SimpleVertex {
float position[2];
}; // struct SimpleVertex
/**
* Simple structure to describe a vertex with a position and a texture.
*/
struct TextureVertex {
float position[2];
float texture[2];
static inline void set(TextureVertex* vertex, float x, float y, float u, float v) {
vertex[0].position[0] = x;
vertex[0].position[1] = y;
vertex[0].texture[0] = u;
vertex[0].texture[1] = v;
}
}; // struct TextureVertex
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_UI_VERTEX_H

View File

@@ -69,6 +69,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="NinePatchesActivity"
android:label="_9patch">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

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.
*/
package com.google.android.test.hwui;
import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.Button;
import android.widget.FrameLayout;
@SuppressWarnings({"UnusedDeclaration"})
public class NinePatchesActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout layout = new FrameLayout(this);
Button b = new Button(this);
b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
b.setText("9 patches");
layout.addView(b);
setContentView(layout);
}
}