From f7f93556c8fcc640ab5adef79d021a80a72a645a Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 8 Jul 2010 19:17:03 -0700 Subject: Draw n-patches using OpenGL. Currently only tested with title bars and buttons. Change-Id: I8263a9281898dc0e943b1b8412827fe55639b9d6 --- libs/hwui/Android.mk | 1 + libs/hwui/Layer.h | 4 +- libs/hwui/LayerCache.h | 2 +- libs/hwui/OpenGLRenderer.cpp | 131 +++++++++++++++++++++++++++++++++++++++---- libs/hwui/OpenGLRenderer.h | 35 ++++++------ libs/hwui/Patch.h | 129 ++++++++++++++++++++++++++++++++++++++++++ libs/hwui/PatchCache.cpp | 72 ++++++++++++++++++++++++ libs/hwui/PatchCache.h | 65 +++++++++++++++++++++ libs/hwui/Vertex.h | 49 ++++++++++++++++ 9 files changed, 457 insertions(+), 31 deletions(-) create mode 100644 libs/hwui/Patch.h create mode 100644 libs/hwui/PatchCache.cpp create mode 100644 libs/hwui/PatchCache.h create mode 100644 libs/hwui/Vertex.h (limited to 'libs') diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 9146ba6..2d6263d 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \ LayerCache.cpp \ Matrix.cpp \ OpenGLRenderer.cpp \ + PatchCache.cpp \ Program.cpp \ TextureCache.cpp diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 586a05e..d4db782 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -17,6 +17,8 @@ #ifndef ANDROID_UI_LAYER_H #define ANDROID_UI_LAYER_H +#include + #include #include @@ -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. diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index adc6713..2580551 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -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 diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 027ed79..34da572 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -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, 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); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 9da4b8e..b46e9d3 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -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 { @@ -45,22 +45,6 @@ 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]; -}; // struct TextureVertex - -/** * Structure mapping Skia xfermodes to OpenGL blending factors. */ struct Blender { @@ -198,6 +182,13 @@ private: 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 diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h new file mode 100644 index 0000000..c6af18a --- /dev/null +++ b/libs/hwui/Patch.h @@ -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 + +#include + +#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 diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp new file mode 100644 index 0000000..694e3fd --- /dev/null +++ b/libs/hwui/PatchCache.cpp @@ -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 +#include + +#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 diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h new file mode 100644 index 0000000..de95087 --- /dev/null +++ b/libs/hwui/PatchCache.h @@ -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 { +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 mCache; +}; // class PatchCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_PATCH_CACHE_H diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h new file mode 100644 index 0000000..208d2a8 --- /dev/null +++ b/libs/hwui/Vertex.h @@ -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 -- cgit v1.1