summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorChris Craik <ccraik@google.com>2012-09-17 17:25:49 -0700
committerChris Craik <ccraik@google.com>2012-09-20 13:08:20 -0700
commit710f46d9d6a5bf9ea1c1833384caf61e1934124f (patch)
tree5dbc56cf9b6bb957f9cbdaaff7283d44897b265d /libs
parent5ca88a103c2841b64bfa01b4848aec4b8aa64687 (diff)
downloadframeworks_base-710f46d9d6a5bf9ea1c1833384caf61e1934124f.zip
frameworks_base-710f46d9d6a5bf9ea1c1833384caf61e1934124f.tar.gz
frameworks_base-710f46d9d6a5bf9ea1c1833384caf61e1934124f.tar.bz2
Polygonal rendering of simple fill shapes
bug:4419017 Change-Id: If0428e1732139786cba15f54b285d880e4a56b89
Diffstat (limited to 'libs')
-rw-r--r--libs/hwui/Android.mk3
-rw-r--r--libs/hwui/LayerRenderer.cpp9
-rw-r--r--libs/hwui/Matrix.cpp8
-rw-r--r--libs/hwui/Matrix.h8
-rw-r--r--libs/hwui/OpenGLRenderer.cpp233
-rw-r--r--libs/hwui/OpenGLRenderer.h30
-rw-r--r--libs/hwui/PathRenderer.cpp304
-rw-r--r--libs/hwui/PathRenderer.h111
-rw-r--r--libs/hwui/Program.h8
-rw-r--r--libs/hwui/ProgramCache.cpp74
10 files changed, 581 insertions, 207 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index e032ae4..549edd2 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -21,6 +21,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LayerRenderer.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
+ PathRenderer.cpp \
Patch.cpp \
PatchCache.cpp \
PathCache.cpp \
@@ -34,7 +35,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
Stencil.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
-
+
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/../../include/utils \
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 5d59a4c..794c80a 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -345,7 +345,7 @@ void LayerRenderer::flushLayer(Layer* layer) {
bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
Caches& caches = Caches::getInstance();
- if (layer && layer->isTextureLayer() && bitmap->width() <= caches.maxTextureSize &&
+ if (layer && bitmap->width() <= caches.maxTextureSize &&
bitmap->height() <= caches.maxTextureSize) {
GLuint fbo = caches.fboCache.get();
@@ -358,6 +358,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
GLuint texture;
GLuint previousFbo;
+ GLuint previousViewport[4];
GLenum format;
GLenum type;
@@ -387,11 +388,13 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
float alpha = layer->getAlpha();
SkXfermode::Mode mode = layer->getMode();
+ GLuint previousLayerFbo = layer->getFbo();
layer->setAlpha(255, SkXfermode::kSrc_Mode);
layer->setFbo(fbo);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
+ glGetIntegerv(GL_VIEWPORT, (GLint*) &previousViewport);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenTextures(1, &texture);
@@ -459,9 +462,11 @@ error:
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
layer->setAlpha(alpha, mode);
- layer->setFbo(0);
+ layer->setFbo(previousLayerFbo);
glDeleteTextures(1, &texture);
caches.fboCache.put(fbo);
+ glViewport(previousViewport[0], previousViewport[1],
+ previousViewport[2], previousViewport[3]);
return status;
}
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 7348f4d..87add17 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -55,21 +55,21 @@ void Matrix4::loadIdentity() {
mSimpleMatrix = true;
}
-bool Matrix4::changesBounds() {
+bool Matrix4::changesBounds() const {
return !(data[0] == 1.0f && data[1] == 0.0f && data[2] == 0.0f && data[4] == 0.0f &&
data[5] == 1.0f && data[6] == 0.0f && data[8] == 0.0f && data[9] == 0.0f &&
data[10] == 1.0f);
}
-bool Matrix4::isPureTranslate() {
+bool Matrix4::isPureTranslate() const {
return mSimpleMatrix && data[kScaleX] == 1.0f && data[kScaleY] == 1.0f;
}
-bool Matrix4::isSimple() {
+bool Matrix4::isSimple() const {
return mSimpleMatrix;
}
-bool Matrix4::isIdentity() {
+bool Matrix4::isIdentity() const {
return mIsIdentity;
}
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 22220a9..02b781e 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -112,11 +112,11 @@ public:
multiply(u);
}
- bool isPureTranslate();
- bool isSimple();
- bool isIdentity();
+ bool isPureTranslate() const;
+ bool isSimple() const;
+ bool isIdentity() const;
- bool changesBounds();
+ bool changesBounds() const;
void copyTo(float* v) const;
void copyTo(SkMatrix& v) const;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 9b9ca12..44b153e 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -33,6 +33,7 @@
#include "OpenGLRenderer.h"
#include "DisplayListRenderer.h"
+#include "PathRenderer.h"
#include "Vector.h"
namespace android {
@@ -473,15 +474,7 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
if (p) {
alpha = p->getAlpha();
- if (!mCaches.extensions.hasFramebufferFetch()) {
- const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
- if (!isMode) {
- // Assume SRC_OVER
- mode = SkXfermode::kSrcOver_Mode;
- }
- } else {
- mode = getXfermode(p->getXfermode());
- }
+ mode = getXfermode(p->getXfermode());
} else {
mode = SkXfermode::kSrcOver_Mode;
}
@@ -1193,12 +1186,12 @@ void OpenGLRenderer::setupDrawNoTexture() {
mCaches.disbaleTexCoordsVertexArray();
}
-void OpenGLRenderer::setupDrawAALine() {
+void OpenGLRenderer::setupDrawAA() {
mDescription.isAA = true;
}
-void OpenGLRenderer::setupDrawAARect() {
- mDescription.isAARect = true;
+void OpenGLRenderer::setupDrawVertexShape() {
+ mDescription.isVertexShape = true;
}
void OpenGLRenderer::setupDrawPoint(float pointSize) {
@@ -1805,97 +1798,48 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
* a fragment shader to compute the translucency of the color from its position, we simply use a
* varying parameter to define how far a given pixel is into the region.
*/
-void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode) {
- float inverseScaleX = 1.0f;
- float inverseScaleY = 1.0f;
+void OpenGLRenderer::drawConvexPath(const SkPath& path, int color, SkXfermode::Mode mode, bool isAA) {
+ VertexBuffer vertexBuffer;
+ // TODO: try clipping large paths to viewport
+ PathRenderer::convexPathFillVertices(path, mSnapshot->transform, vertexBuffer, isAA);
- // The quad that we use needs to account for scaling.
- if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) {
- Matrix4 *mat = mSnapshot->transform;
- float m00 = mat->data[Matrix4::kScaleX];
- float m01 = mat->data[Matrix4::kSkewY];
- float m10 = mat->data[Matrix4::kSkewX];
- float m11 = mat->data[Matrix4::kScaleY];
- float scaleX = sqrt(m00 * m00 + m01 * m01);
- float scaleY = sqrt(m10 * m10 + m11 * m11);
- inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
- inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
- }
-
- float boundarySizeX = .5 * inverseScaleX;
- float boundarySizeY = .5 * inverseScaleY;
-
- float innerLeft = left + boundarySizeX;
- float innerRight = right - boundarySizeX;
- float innerTop = top + boundarySizeY;
- float innerBottom = bottom - boundarySizeY;
-
- // Adjust the rect by the AA boundary padding
- left -= boundarySizeX;
- right += boundarySizeX;
- top -= boundarySizeY;
- bottom += boundarySizeY;
-
- if (!quickReject(left, top, right, bottom)) {
- setupDraw();
- setupDrawNoTexture();
- setupDrawAARect();
- setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(true, mode);
- setupDrawProgram();
- setupDrawModelViewIdentity(true);
- setupDrawColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderIdentityUniforms();
+ setupDraw();
+ setupDrawNoTexture();
+ if (isAA) setupDrawAA();
+ setupDrawVertexShape();
+ setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
+ setupDrawColorFilter();
+ setupDrawShader();
+ setupDrawBlending(isAA, mode);
+ setupDrawProgram();
+ setupDrawModelViewIdentity(true);
+ setupDrawColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderIdentityUniforms();
- AlphaVertex rects[14];
- AlphaVertex* aVertices = &rects[0];
- void* alphaCoords = ((GLbyte*) aVertices) + gVertexAlphaOffset;
+ void* vertices = vertexBuffer.getBuffer();
+ bool force = mCaches.unbindMeshBuffer();
+ mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
+ vertices, isAA ? gAlphaVertexStride : gVertexStride);
+ mCaches.resetTexCoordsVertexPointer();
+ mCaches.unbindIndicesBuffer();
- bool force = mCaches.unbindMeshBuffer();
- mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
- aVertices, gAlphaVertexStride);
- mCaches.resetTexCoordsVertexPointer();
- mCaches.unbindIndicesBuffer();
+ int alphaSlot = -1;
+ if (isAA) {
+ void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
+ alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
- int alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
+ // TODO: avoid enable/disable in back to back uses of the alpha attribute
glEnableVertexAttribArray(alphaSlot);
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
+ }
- // draw left
- AlphaVertex::set(aVertices++, left, bottom, 0);
- AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
- AlphaVertex::set(aVertices++, left, top, 0);
- AlphaVertex::set(aVertices++, innerLeft, innerTop, 1);
-
- // draw top
- AlphaVertex::set(aVertices++, right, top, 0);
- AlphaVertex::set(aVertices++, innerRight, innerTop, 1);
-
- // draw right
- AlphaVertex::set(aVertices++, right, bottom, 0);
- AlphaVertex::set(aVertices++, innerRight, innerBottom, 1);
-
- // draw bottom
- AlphaVertex::set(aVertices++, left, bottom, 0);
- AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
-
- // draw inner rect (repeating last vertex to create degenerate bridge triangles)
- // TODO: also consider drawing the inner rect without the blending-forced shader, if
- // blending is expensive. Note: can't use drawColorRect() since it doesn't use vertex
- // buffers like below, resulting in slightly different transformed coordinates.
- AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
- AlphaVertex::set(aVertices++, innerLeft, innerTop, 1);
- AlphaVertex::set(aVertices++, innerRight, innerBottom, 1);
- AlphaVertex::set(aVertices++, innerRight, innerTop, 1);
+ SkRect bounds = path.getBounds();
+ dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *mSnapshot->transform);
- dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 14);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getSize());
+ if (isAA) {
glDisableVertexAttribArray(alphaSlot);
}
}
@@ -1973,7 +1917,7 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
setupDraw();
setupDrawNoTexture();
if (isAA) {
- setupDrawAALine();
+ setupDrawAA();
}
setupDrawColor(paint->getColor(), alpha);
setupDrawColorFilter();
@@ -2259,30 +2203,62 @@ status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* tex
}
status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+ float rx, float ry, SkPaint* p) {
+ if (mSnapshot->isIgnored() || quickReject(left, top, right, bottom)) {
+ return DrawGlInfo::kStatusDone;
+ }
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
- right - left, bottom - top, rx, ry, paint);
- return drawShape(left, top, texture, paint);
+ if (p->getStyle() != SkPaint::kFill_Style) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
+ right - left, bottom - top, rx, ry, p);
+ return drawShape(left, top, texture, p);
+ }
+
+ SkPath path;
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ path.addRoundRect(rect, rx, ry);
+ drawConvexPath(path, p->getColor(), getXfermode(p->getXfermode()), p->isAntiAlias());
+
+ return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) {
+ if (mSnapshot->isIgnored() || quickReject(x - radius, y - radius, x + radius, y + radius)) {
+ return DrawGlInfo::kStatusDone;
+ }
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, paint);
- return drawShape(x - radius, y - radius, texture, paint);
+ if (p->getStyle() != SkPaint::kFill_Style) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, p);
+ return drawShape(x - radius, y - radius, texture, p);
+ }
+
+ SkPath path;
+ path.addCircle(x, y, radius);
+ drawConvexPath(path, p->getColor(), getXfermode(p->getXfermode()), p->isAntiAlias());
+
+ return DrawGlInfo::kStatusDrew;
}
status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
- SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+ SkPaint* p) {
+ if (mSnapshot->isIgnored() || quickReject(left, top, right, bottom)) {
+ return DrawGlInfo::kStatusDone;
+ }
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, paint);
- return drawShape(left, top, texture, paint);
+ if (p->getStyle() != SkPaint::kFill_Style) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, p);
+ return drawShape(left, top, texture, p);
+ }
+
+ SkPath path;
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ path.addOval(rect);
+ drawConvexPath(path, p->getColor(), getXfermode(p->getXfermode()), p->isAntiAlias());
+
+ return DrawGlInfo::kStatusDrew;
}
status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
@@ -2299,40 +2275,23 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto
return drawShape(left, top, texture, paint);
}
-status_t OpenGLRenderer::drawRectAsShape(float left, float top, float right, float bottom,
- SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
-
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.rectShapeCache.getRect(right - left, bottom - top, paint);
- return drawShape(left, top, texture, paint);
-}
-
status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
- if (p->getStyle() != SkPaint::kFill_Style) {
- return drawRectAsShape(left, top, right, bottom, p);
- }
-
- if (quickReject(left, top, right, bottom)) {
+ if (mSnapshot->isIgnored() || quickReject(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
- SkXfermode::Mode mode;
- if (!mCaches.extensions.hasFramebufferFetch()) {
- const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
- if (!isMode) {
- // Assume SRC_OVER
- mode = SkXfermode::kSrcOver_Mode;
- }
- } else {
- mode = getXfermode(p->getXfermode());
+ if (p->getStyle() != SkPaint::kFill_Style) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture = mCaches.rectShapeCache.getRect(right - left, bottom - top, p);
+ return drawShape(left, top, texture, p);
}
- int color = p->getColor();
if (p->isAntiAlias() && !mSnapshot->transform->isSimple()) {
- drawAARect(left, top, right, bottom, color, mode);
+ SkPath path;
+ path.addRect(left, top, right, bottom);
+ drawConvexPath(path, p->getColor(), getXfermode(p->getXfermode()), true);
} else {
- drawColorRect(left, top, right, bottom, color, mode);
+ drawColorRect(left, top, right, bottom, p->getColor(), getXfermode(p->getXfermode()));
}
return DrawGlInfo::kStatusDrew;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 10ba86e..4348300 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -484,19 +484,6 @@ private:
status_t drawShape(float left, float top, const PathTexture* texture, SkPaint* paint);
/**
- * Renders the rect defined by the specified bounds as a shape.
- * This will render the rect using a path texture, which is used to render
- * rects with stroke effects.
- *
- * @param left The left coordinate of the rect to draw
- * @param top The top coordinate of the rect to draw
- * @param right The right coordinate of the rect to draw
- * @param bottom The bottom coordinate of the rect to draw
- * @param p The paint to draw the rect with
- */
- status_t drawRectAsShape(float left, float top, float right, float bottom, SkPaint* p);
-
- /**
* Draws the specified texture as an alpha bitmap. Alpha bitmaps obey
* different compositing rules.
*
@@ -508,17 +495,14 @@ private:
void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint);
/**
- * Renders the rect defined by the specified bounds as an anti-aliased rect.
+ * Renders the convex hull defined by the specified path as a strip of polygons.
*
- * @param left The left coordinate of the rect to draw
- * @param top The top coordinate of the rect to draw
- * @param right The right coordinate of the rect to draw
- * @param bottom The bottom coordinate of the rect to draw
+ * @param path The hull of the path to draw
* @param color The color of the rect
- * @param mode The blending mode to draw the rect
+ * @param mode The blending mode to draw the path
+ * @param isAA True if the drawing should be anti-aliased
*/
- void drawAARect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode);
+ void drawConvexPath(const SkPath& path, int color, SkXfermode::Mode mode, bool isAA);
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
@@ -679,8 +663,8 @@ private:
void setupDrawWithTexture(bool isAlpha8 = false);
void setupDrawWithExternalTexture();
void setupDrawNoTexture();
- void setupDrawAALine();
- void setupDrawAARect();
+ void setupDrawAA();
+ void setupDrawVertexShape();
void setupDrawPoint(float pointSize);
void setupDrawColor(int color);
void setupDrawColor(int color, int alpha);
diff --git a/libs/hwui/PathRenderer.cpp b/libs/hwui/PathRenderer.cpp
new file mode 100644
index 0000000..d222009
--- /dev/null
+++ b/libs/hwui/PathRenderer.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2012 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 "PathRenderer"
+#define LOG_NDEBUG 1
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#define VERTEX_DEBUG 0
+
+#include <SkPath.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "PathRenderer.h"
+#include "Matrix.h"
+#include "Vector.h"
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+#define THRESHOLD 0.5f
+
+void PathRenderer::computeInverseScales(const mat4 *transform,
+ float &inverseScaleX, float& inverseScaleY) {
+ inverseScaleX = 1.0f;
+ inverseScaleY = 1.0f;
+ if (CC_UNLIKELY(!transform->isPureTranslate())) {
+ float m00 = transform->data[Matrix4::kScaleX];
+ float m01 = transform->data[Matrix4::kSkewY];
+ float m10 = transform->data[Matrix4::kSkewX];
+ float m11 = transform->data[Matrix4::kScaleY];
+ float scaleX = sqrt(m00 * m00 + m01 * m01);
+ float scaleY = sqrt(m10 * m10 + m11 * m11);
+ inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
+ inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+ }
+}
+
+void PathRenderer::convexPathFillVertices(const SkPath &path, const mat4 *transform,
+ VertexBuffer &vertexBuffer, bool isAA) {
+ ATRACE_CALL();
+ float inverseScaleX;
+ float inverseScaleY;
+ computeInverseScales(transform, inverseScaleX, inverseScaleY);
+
+ Vector<Vertex> tempVertices;
+ float thresholdx = THRESHOLD * inverseScaleX;
+ float thresholdy = THRESHOLD * inverseScaleY;
+ convexPathVertices(path,
+ thresholdx * thresholdx,
+ thresholdy * thresholdy,
+ tempVertices);
+
+#if VERTEX_DEBUG
+ for (unsigned int i = 0; i < tempVertices.size(); i++) {
+ ALOGD("orig path: point at %f %f",
+ tempVertices[i].position[0],
+ tempVertices[i].position[1]);
+ }
+#endif
+ int currentIndex = 0;
+ if (!isAA) {
+ Vertex* buffer = vertexBuffer.alloc<Vertex>(tempVertices.size());
+
+ // zig zag between all previous points on the inside of the hull to create a
+ // triangle strip that fills the hull
+ int srcAindex = 0;
+ int srcBindex = tempVertices.size() - 1;
+ while (srcAindex <= srcBindex) {
+ Vertex::set(&buffer[currentIndex++],
+ tempVertices.editArray()[srcAindex].position[0],
+ tempVertices.editArray()[srcAindex].position[1]);
+ if (srcAindex == srcBindex) break;
+ Vertex::set(&buffer[currentIndex++],
+ tempVertices.editArray()[srcBindex].position[0],
+ tempVertices.editArray()[srcBindex].position[1]);
+ srcAindex++;
+ srcBindex--;
+ }
+ return;
+ }
+ AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(tempVertices.size() * 3 + 2);
+
+ // generate alpha points - fill Alpha vertex gaps in between each point with
+ // alpha 0 vertex, offset by a scaled normal.
+ Vertex* last = &(tempVertices.editArray()[tempVertices.size()-1]);
+
+ for (unsigned int i = 0; i<tempVertices.size(); i++) {
+ Vertex* current = &(tempVertices.editArray()[i]);
+ Vertex* next = &(tempVertices.editArray()[i + 1 >= tempVertices.size() ? 0 : i + 1]);
+
+ vec2 lastNormal(current->position[1] - last->position[1],
+ last->position[0] - current->position[0]);
+ lastNormal.normalize();
+ vec2 nextNormal(next->position[1] - current->position[1],
+ current->position[0] - next->position[0]);
+ nextNormal.normalize();
+
+ // AA point offset from original point is that point's normal, such that
+ // each side is offset by .5 pixels
+ vec2 totalOffset = (lastNormal + nextNormal) / (2 * (1 + lastNormal.dot(nextNormal)));
+ totalOffset.x *= inverseScaleX;
+ totalOffset.y *= inverseScaleY;
+
+ AlphaVertex::set(&buffer[currentIndex++],
+ current->position[0] + totalOffset.x,
+ current->position[1] + totalOffset.y,
+ 0.0f);
+ AlphaVertex::set(&buffer[currentIndex++],
+ current->position[0] - totalOffset.x,
+ current->position[1] - totalOffset.y,
+ 1.0f);
+ last = current;
+ }
+
+ // wrap around to beginning
+ AlphaVertex::set(&buffer[currentIndex++],
+ buffer[0].position[0],
+ buffer[0].position[1], 0.0f);
+ AlphaVertex::set(&buffer[currentIndex++],
+ buffer[1].position[0],
+ buffer[1].position[1], 1.0f);
+
+ // zig zag between all previous points on the inside of the hull to create a
+ // triangle strip that fills the hull, repeating the first inner point to
+ // create degenerate tris to start inside path
+ int srcAindex = 0;
+ int srcBindex = tempVertices.size() - 1;
+ while (srcAindex <= srcBindex) {
+ AlphaVertex::set(&buffer[currentIndex++],
+ buffer[srcAindex * 2 + 1].position[0],
+ buffer[srcAindex * 2 + 1].position[1],
+ 1.0f);
+ if (srcAindex == srcBindex) break;
+ AlphaVertex::set(&buffer[currentIndex++],
+ buffer[srcBindex * 2 + 1].position[0],
+ buffer[srcBindex * 2 + 1].position[1],
+ 1.0f);
+ srcAindex++;
+ srcBindex--;
+ }
+
+#if VERTEX_DEBUG
+ for (unsigned int i = 0; i < vertexBuffer.mSize; i++) {
+ ALOGD("point at %f %f",
+ buffer[i].position[0],
+ buffer[i].position[1]);
+ }
+#endif
+}
+
+
+void PathRenderer::convexPathVertices(const SkPath &path, float thresholdx, float thresholdy,
+ Vector<Vertex> &outputVertices) {
+ ATRACE_CALL();
+
+ SkPath::Iter iter(path, true);
+ SkPoint pos;
+ SkPoint pts[4];
+ SkPath::Verb v;
+ Vertex* newVertex = 0;
+ while (SkPath::kDone_Verb != (v = iter.next(pts))) {
+ switch (v) {
+ case SkPath::kMove_Verb:
+ pos = pts[0];
+ ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
+ break;
+ case SkPath::kClose_Verb:
+ ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
+ break;
+ case SkPath::kLine_Verb:
+ ALOGV("kLine_Verb %f %f -> %f %f",
+ pts[0].x(), pts[0].y(),
+ pts[1].x(), pts[1].y());
+
+ // TODO: make this not yuck
+ outputVertices.push();
+ newVertex = &(outputVertices.editArray()[outputVertices.size()-1]);
+ Vertex::set(newVertex, pts[1].x(), pts[1].y());
+ break;
+ case SkPath::kQuad_Verb:
+ ALOGV("kQuad_Verb");
+ recursiveQuadraticBezierVertices(
+ pts[0].x(), pts[0].y(),
+ pts[2].x(), pts[2].y(),
+ pts[1].x(), pts[1].y(),
+ thresholdx, thresholdy,
+ outputVertices);
+ break;
+ case SkPath::kCubic_Verb:
+ ALOGV("kCubic_Verb");
+ recursiveCubicBezierVertices(
+ pts[0].x(), pts[0].y(),
+ pts[1].x(), pts[1].y(),
+ pts[3].x(), pts[3].y(),
+ pts[2].x(), pts[2].y(),
+ thresholdx, thresholdy, outputVertices);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void PathRenderer::recursiveCubicBezierVertices(
+ float p1x, float p1y, float c1x, float c1y,
+ float p2x, float p2y, float c2x, float c2y,
+ float thresholdx, float thresholdy, Vector<Vertex> &outputVertices) {
+ float dx = p2x - p1x;
+ float dy = p2y - p1y;
+ float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
+ float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
+ float d = d1 + d2;
+
+ if (d * d < (thresholdx * (dx * dx) + thresholdy * (dy * dy))) {
+ // below thresh, draw line by adding endpoint
+ // TODO: make this not yuck
+ outputVertices.push();
+ Vertex* newVertex = &(outputVertices.editArray()[outputVertices.size()-1]);
+ Vertex::set(newVertex, p2x, p2y);
+ } else {
+ float p1c1x = (p1x + c1x) * 0.5f;
+ float p1c1y = (p1y + c1y) * 0.5f;
+ float p2c2x = (p2x + c2x) * 0.5f;
+ float p2c2y = (p2y + c2y) * 0.5f;
+
+ float c1c2x = (c1x + c2x) * 0.5f;
+ float c1c2y = (c1y + c2y) * 0.5f;
+
+ float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
+ float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
+
+ float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
+ float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
+
+ float mx = (p1c1c2x + p2c1c2x) * 0.5f;
+ float my = (p1c1c2y + p2c1c2y) * 0.5f;
+
+ recursiveCubicBezierVertices(
+ p1x, p1y, p1c1x, p1c1y,
+ mx, my, p1c1c2x, p1c1c2y,
+ thresholdx, thresholdy,
+ outputVertices);
+ recursiveCubicBezierVertices(
+ mx, my, p2c1c2x, p2c1c2y,
+ p2x, p2y, p2c2x, p2c2y,
+ thresholdx, thresholdy,
+ outputVertices);
+ }
+}
+
+void PathRenderer::recursiveQuadraticBezierVertices(
+ float ax, float ay,
+ float bx, float by,
+ float cx, float cy,
+ float thresholdx, float thresholdy, Vector<Vertex> &outputVertices) {
+ float dx = bx - ax;
+ float dy = by - ay;
+ float d = (cx - bx) * dy - (cy - by) * dx;
+
+ if (d * d < (thresholdx * (dx * dx) + thresholdy * (dy * dy))) {
+ // below thresh, draw line by adding endpoint
+ // TODO: make this not yuck
+ outputVertices.push();
+ Vertex* newVertex = &(outputVertices.editArray()[outputVertices.size()-1]);
+ Vertex::set(newVertex, bx, by);
+ } else {
+ float acx = (ax + cx) * 0.5f;
+ float bcx = (bx + cx) * 0.5f;
+ float acy = (ay + cy) * 0.5f;
+ float bcy = (by + cy) * 0.5f;
+
+ // midpoint
+ float mx = (acx + bcx) * 0.5f;
+ float my = (acy + bcy) * 0.5f;
+
+ recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
+ thresholdx, thresholdy, outputVertices);
+ recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
+ thresholdx, thresholdy, outputVertices);
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PathRenderer.h b/libs/hwui/PathRenderer.h
new file mode 100644
index 0000000..1354f16
--- /dev/null
+++ b/libs/hwui/PathRenderer.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 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_HWUI_PATH_RENDERER_H
+#define ANDROID_HWUI_PATH_RENDERER_H
+
+#include <utils/Vector.h>
+
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+class Matrix4;
+typedef Matrix4 mat4;
+
+class VertexBuffer {
+public:
+ VertexBuffer():
+ mBuffer(0),
+ mSize(0),
+ mCleanupMethod(0)
+ {}
+
+ ~VertexBuffer()
+ {
+ if (mCleanupMethod)
+ mCleanupMethod(mBuffer);
+ }
+
+ template <class TYPE>
+ TYPE* alloc(int size)
+ {
+ mSize = size;
+ mBuffer = (void*)new TYPE[size];
+ mCleanupMethod = &(cleanup<TYPE>);
+
+ return (TYPE*)mBuffer;
+ }
+
+ void* getBuffer() { return mBuffer; }
+ unsigned int getSize() { return mSize; }
+
+private:
+ template <class TYPE>
+ static void cleanup(void* buffer)
+ {
+ delete[] (TYPE*)buffer;
+ }
+
+ void* mBuffer;
+ unsigned int mSize;
+ void (*mCleanupMethod)(void*);
+};
+
+class PathRenderer {
+public:
+ static void computeInverseScales(
+ const mat4 *transform, float &inverseScaleX, float& inverseScaleY);
+
+ static void convexPathFillVertices(
+ const SkPath &path, const mat4 *transform,
+ VertexBuffer &vertexBuffer, bool isAA);
+
+private:
+ static void convexPathVertices(
+ const SkPath &path,
+ float thresholdx, float thresholdy,
+ Vector<Vertex> &outputVertices);
+
+/*
+ endpoints a & b,
+ control c
+ */
+ static void recursiveQuadraticBezierVertices(
+ float ax, float ay,
+ float bx, float by,
+ float cx, float cy,
+ float thresholdx, float thresholdy,
+ Vector<Vertex> &outputVertices);
+
+/*
+ endpoints p1, p2
+ control c1, c2
+ */
+ static void recursiveCubicBezierVertices(
+ float p1x, float p1y,
+ float c1x, float c1y,
+ float p2x, float p2y,
+ float c2x, float c2y,
+ float thresholdx, float thresholdy,
+ Vector<Vertex> &outputVertices);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATH_RENDERER_H
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index a821a9c..b1cb446 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -81,7 +81,7 @@ namespace uirenderer {
#define PROGRAM_IS_SIMPLE_GRADIENT 41
-#define PROGRAM_IS_AA_RECT_SHIFT 42
+#define PROGRAM_IS_VERTEX_SHAPE_SHIFT 42
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -130,7 +130,7 @@ struct ProgramDescription {
bool isBitmapNpot;
bool isAA;
- bool isAARect;
+ bool isVertexShape;
bool hasGradient;
Gradient gradientType;
@@ -168,7 +168,7 @@ struct ProgramDescription {
hasTextureTransform = false;
isAA = false;
- isAARect = false;
+ isVertexShape = false;
modulate = false;
@@ -263,7 +263,7 @@ struct ProgramDescription {
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
- if (isAARect) key |= programid(0x1) << PROGRAM_IS_AA_RECT_SHIFT;
+ if (isVertexShape) key |= programid(0x1) << PROGRAM_IS_VERTEX_SHAPE_SHIFT;
return key;
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 6baf448..de7afed 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -40,10 +40,10 @@ const char* gVS_Header_Attributes =
"attribute vec4 position;\n";
const char* gVS_Header_Attributes_TexCoords =
"attribute vec2 texCoords;\n";
-const char* gVS_Header_Attributes_AAParameters =
+const char* gVS_Header_Attributes_AALineParameters =
"attribute float vtxWidth;\n"
"attribute float vtxLength;\n";
-const char* gVS_Header_Attributes_AARectParameters =
+const char* gVS_Header_Attributes_AAVertexShapeParameters =
"attribute float vtxAlpha;\n";
const char* gVS_Header_Uniforms_TextureTransform =
"uniform mat4 mainTextureTransform;\n";
@@ -64,10 +64,10 @@ const char* gVS_Header_Uniforms_HasBitmap =
"uniform mediump vec2 textureDimension;\n";
const char* gVS_Header_Varyings_HasTexture =
"varying vec2 outTexCoords;\n";
-const char* gVS_Header_Varyings_IsAA =
+const char* gVS_Header_Varyings_IsAALine =
"varying float widthProportion;\n"
"varying float lengthProportion;\n";
-const char* gVS_Header_Varyings_IsAARect =
+const char* gVS_Header_Varyings_IsAAVertexShape =
"varying float alpha;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying highp vec2 outBitmapTexCoords;\n";
@@ -113,10 +113,10 @@ const char* gVS_Main_Position =
" gl_Position = transform * position;\n";
const char* gVS_Main_PointSize =
" gl_PointSize = pointSize;\n";
-const char* gVS_Main_AA =
+const char* gVS_Main_AALine =
" widthProportion = vtxWidth;\n"
" lengthProportion = vtxLength;\n";
-const char* gVS_Main_AARect =
+const char* gVS_Main_AAVertexShape =
" alpha = vtxAlpha;\n";
const char* gVS_Footer =
"}\n\n";
@@ -133,7 +133,7 @@ const char* gFS_Header =
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
"uniform vec4 color;\n";
-const char* gFS_Uniforms_AA =
+const char* gFS_Uniforms_AALine =
"uniform float boundaryWidth;\n"
"uniform float boundaryLength;\n";
const char* gFS_Header_Uniforms_PointHasBitmap =
@@ -243,10 +243,10 @@ const char* gFS_Main_FetchColor =
" fragColor = color;\n";
const char* gFS_Main_ModulateColor =
" fragColor *= color.a;\n";
-const char* gFS_Main_AccountForAA =
+const char* gFS_Main_AccountForAALine =
" fragColor *= (1.0 - smoothstep(boundaryWidth, 0.5, abs(0.5 - widthProportion)))\n"
" * (1.0 - smoothstep(boundaryLength, 0.5, abs(0.5 - lengthProportion)));\n";
-const char* gFS_Main_AccountForAARect =
+const char* gFS_Main_AccountForAAVertexShape =
" fragColor *= alpha;\n";
const char* gFS_Main_FetchTexture[2] = {
@@ -456,10 +456,12 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Attributes_TexCoords);
}
- if (description.isAARect) {
- shader.append(gVS_Header_Attributes_AARectParameters);
- } else if (description.isAA) {
- shader.append(gVS_Header_Attributes_AAParameters);
+ if (description.isAA) {
+ if (description.isVertexShape) {
+ shader.append(gVS_Header_Attributes_AAVertexShapeParameters);
+ } else {
+ shader.append(gVS_Header_Attributes_AALineParameters);
+ }
}
// Uniforms
shader.append(gVS_Header_Uniforms);
@@ -479,10 +481,12 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
- if (description.isAARect) {
- shader.append(gVS_Header_Varyings_IsAARect);
- } else if (description.isAA) {
- shader.append(gVS_Header_Varyings_IsAA);
+ if (description.isAA) {
+ if (description.isVertexShape) {
+ shader.append(gVS_Header_Varyings_IsAAVertexShape);
+ } else {
+ shader.append(gVS_Header_Varyings_IsAALine);
+ }
}
if (description.hasGradient) {
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
@@ -500,10 +504,12 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
} else if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Main_OutTexCoords);
}
- if (description.isAARect) {
- shader.append(gVS_Main_AARect);
- } else if (description.isAA) {
- shader.append(gVS_Main_AA);
+ if (description.isAA) {
+ if (description.isVertexShape) {
+ shader.append(gVS_Main_AAVertexShape);
+ } else {
+ shader.append(gVS_Main_AALine);
+ }
}
if (description.hasGradient) {
shader.append(gVS_Main_OutGradient[gradientIndex(description)]);
@@ -552,10 +558,12 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
- if (description.isAARect) {
- shader.append(gVS_Header_Varyings_IsAARect);
- } else if (description.isAA) {
- shader.append(gVS_Header_Varyings_IsAA);
+ if (description.isAA) {
+ if (description.isVertexShape) {
+ shader.append(gVS_Header_Varyings_IsAAVertexShape);
+ } else {
+ shader.append(gVS_Header_Varyings_IsAALine);
+ }
}
if (description.hasGradient) {
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
@@ -580,8 +588,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
} else if (description.hasExternalTexture) {
shader.append(gFS_Uniforms_ExternalTextureSampler);
}
- if (description.isAA) {
- shader.append(gFS_Uniforms_AA);
+ if (description.isAA && !description.isVertexShape) {
+ shader.append(gFS_Uniforms_AALine);
}
if (description.hasGradient) {
shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
@@ -596,7 +604,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Optimization for common cases
if (!description.isAA && !blendFramebuffer &&
description.colorOp == ProgramDescription::kColorNone &&
- !description.isPoint && !description.isAARect) {
+ !description.isPoint && !description.isVertexShape) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -730,10 +738,12 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Apply the color op if needed
shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
- if (description.isAARect) {
- shader.append(gFS_Main_AccountForAARect);
- } else if (description.isAA) {
- shader.append(gFS_Main_AccountForAA);
+ if (description.isAA) {
+ if (description.isVertexShape) {
+ shader.append(gFS_Main_AccountForAAVertexShape);
+ } else {
+ shader.append(gFS_Main_AccountForAALine);
+ }
}
// Output the fragment