diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Android.mk | 3 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.cpp | 9 | ||||
-rw-r--r-- | libs/hwui/Matrix.cpp | 8 | ||||
-rw-r--r-- | libs/hwui/Matrix.h | 8 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 233 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 30 | ||||
-rw-r--r-- | libs/hwui/PathRenderer.cpp | 304 | ||||
-rw-r--r-- | libs/hwui/PathRenderer.h | 111 | ||||
-rw-r--r-- | libs/hwui/Program.h | 8 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 74 |
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 |