From 5b0200bd47e8a9a4dc8d2e6c3a110d522b30bf82 Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Wed, 13 Apr 2011 17:58:08 -0700 Subject: Enable anti-aliasing for hw-accelerated lines Draw anti-aliased lines with OpenGL by constructing a quad with a border that fades out (to mimic fragment coverage). Change-Id: Ib81a3e62d663acdf1b46b401ac4aa7ee9855cc7e --- libs/hwui/Caches.h | 3 + libs/hwui/OpenGLRenderer.cpp | 289 +++++++++++++++------ libs/hwui/OpenGLRenderer.h | 16 ++ libs/hwui/ProgramCache.cpp | 38 ++- libs/hwui/ProgramCache.h | 6 + libs/hwui/Vertex.h | 28 ++ tests/HwAccelerationTest/AndroidManifest.xml | 9 + .../src/com/android/test/hwui/Lines2Activity.java | 149 +++++++++++ .../src/com/android/test/hwui/LinesActivity.java | 2 +- 9 files changed, 453 insertions(+), 87 deletions(-) create mode 100644 tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 7d02cf8..faecadd 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -60,7 +60,10 @@ static const TextureVertex gMeshVertices[] = { FV(1.0f, 1.0f, 1.0f, 1.0f) }; static const GLsizei gMeshStride = sizeof(TextureVertex); +static const GLsizei gVertexStride = sizeof(Vertex); +static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex); static const GLsizei gMeshTextureOffset = 2 * sizeof(float); +static const GLsizei gVertexAlphaOffset = 2 * sizeof(float); static const GLsizei gMeshCount = 4; /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 6fabbef..7f28959 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -882,6 +882,10 @@ void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { mDescription.hasAlpha8Texture = isAlpha8; } +void OpenGLRenderer::setupDrawAALine() { + mDescription.hasWidth = true; +} + void OpenGLRenderer::setupDrawPoint(float pointSize) { mDescription.isPoint = true; mDescription.pointSize = pointSize; @@ -893,6 +897,7 @@ void OpenGLRenderer::setupDrawColor(int color) { void OpenGLRenderer::setupDrawColor(int color, int alpha) { mColorA = alpha / 255.0f; + // BUG on this next line? a is alpha divided by 255 *twice* const float a = mColorA / 255.0f; mColorR = a * ((color >> 16) & 0xFF); mColorG = a * ((color >> 8) & 0xFF); @@ -1060,6 +1065,37 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v } } +void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) { + mCaches.unbindMeshBuffer(); + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gVertexStride, vertices); +} + +/** + * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an + * outer boundary that fades out to 0. The variables set in the shader define the width of the + * core line primitive ("width") and the width of the fading boundary ("boundaryWidth"). The + * "vtxDistance" attribute (one per vertex) is a value from zero to one that tells the fragment + * shader where the fragment is in relation to the line width overall; this value is then used + * to compute the proper color, based on whether the fragment lies in the fading AA region of + * the line. + */ +void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth) { + mCaches.unbindMeshBuffer(); + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gAlphaVertexStride, vertices); + int distanceSlot = mCaches.currentProgram->getAttrib("vtxDistance"); + glEnableVertexAttribArray(distanceSlot); + glVertexAttribPointer(distanceSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, distanceCoords); + int widthSlot = mCaches.currentProgram->getUniform("width"); + int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth"); + int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth"); + float boundaryWidth = (1 - strokeWidth) / 2; + glUniform1f(widthSlot, strokeWidth); + glUniform1f(boundaryWidthSlot, boundaryWidth); + glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidth)); +} + void OpenGLRenderer::finishDrawTexture() { glDisableVertexAttribArray(mTexCoordsSlot); } @@ -1350,114 +1386,199 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int } } +void OpenGLRenderer::drawLinesAsQuads(float *points, int count, bool isAA, bool isHairline, + float strokeWidth) { + int verticesCount = count; + if (count > 4) { + // Polyline: account for extra vertices needed for continous tri-strip + verticesCount += (count -4); + } + if (isAA) { + // Expand boundary to enable AA calculations on the quad border + strokeWidth += .5f; + } + Vertex lines[verticesCount]; + Vertex* vertices = &lines[0]; + AlphaVertex wLines[verticesCount]; + AlphaVertex* aaVertices = &wLines[0]; + if (!isAA) { + setupDrawVertices(vertices); + } else { + void *alphaCoords = ((void*) aaVertices) + gVertexAlphaOffset; + // innerProportion is the ratio of the inner (non-AA) port of the line to the total + // AA stroke width (the base stroke width expanded by a half pixel on either side). + // This value is used in the fragment shader to determine how to fill fragments. + float innerProportion = fmax(strokeWidth - 1.0f, 0) / (strokeWidth + .5f); + setupDrawAALine((void*) aaVertices, (void*) alphaCoords, innerProportion); + } + + int generatedVerticesCount = 0; + AlphaVertex *prevAAVertex = NULL; + Vertex *prevVertex = NULL; + float inverseScaleX, inverseScaleY; + if (isHairline) { + // The quad that we use for AA hairlines needs to account for scaling because the line + // should always be one pixel wide regardless of scale. + inverseScaleX = 1.0f; + inverseScaleY = 1.0f; + if (!mSnapshot->transform->isPureTranslate()) { + Matrix4 *mat = mSnapshot->transform; + float m00 = mat->data[Matrix4::kScaleX]; + float m01 = mat->data[Matrix4::kSkewY]; + float m02 = mat->data[2]; + float m10 = mat->data[Matrix4::kSkewX]; + float m11 = mat->data[Matrix4::kScaleX]; + float m12 = mat->data[6]; + 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; + } + } + for (int i = 0; i < count; i += 4) { + // a = start point, b = end point + vec2 a(points[i], points[i + 1]); + vec2 b(points[i + 2], points[i + 3]); + + // Bias to snap to the same pixels as Skia + a += 0.375; + b += 0.375; + + // Find the normal to the line + vec2 n = (b - a).copyNormalized() * strokeWidth; + if (isHairline) { + float wideningFactor; + if (fabs(n.x) >= fabs(n.y)) { + wideningFactor = fabs(1.0f / n.x); + } else { + wideningFactor = fabs(1.0f / n.y); + } + n.x *= inverseScaleX; + n.y *= inverseScaleY; + n *= wideningFactor; + } + float x = n.x; + n.x = -n.y; + n.y = x; + + // Four corners of the rectangle defining a thick line + vec2 p1 = a - n; + vec2 p2 = a + n; + vec2 p3 = b + n; + vec2 p4 = b - n; + + const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x))); + const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x))); + const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y))); + const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y))); + + if (!quickReject(left, top, right, bottom)) { + // Draw the line as 2 triangles, could be optimized + // by using only 4 vertices and the correct indices + // Also we should probably used non textured vertices + // when line AA is disabled to save on bandwidth + if (!isAA) { + if (prevVertex != NULL) { + // Issue two repeat vertices to create degenerate triangles to bridge + // between the previous line and the new one. This is necessary because + // we are creating a single triangle_strip which will contain + // potentially discontinuous line segments. + Vertex::set(vertices++, prevVertex->position[0], prevVertex->position[1]); + Vertex::set(vertices++, p1.x, p1.y); + generatedVerticesCount += 2; + } + Vertex::set(vertices++, p1.x, p1.y); + Vertex::set(vertices++, p2.x, p2.y); + Vertex::set(vertices++, p4.x, p4.y); + Vertex::set(vertices++, p3.x, p3.y); + prevVertex = vertices - 1; + generatedVerticesCount += 4; + } else { + if (prevAAVertex != NULL) { + // Issue two repeat vertices to create degenerate triangles to bridge + // between the previous line and the new one. This is necessary because + // we are creating a single triangle_strip which will contain + // potentially discontinuous line segments. + AlphaVertex::set(aaVertices++,prevAAVertex->position[0], + prevAAVertex->position[1], prevAAVertex->alpha); + AlphaVertex::set(aaVertices++, p4.x, p4.y, 1); + generatedVerticesCount += 2; + } + AlphaVertex::set(aaVertices++, p4.x, p4.y, 1); + AlphaVertex::set(aaVertices++, p1.x, p1.y, 1); + AlphaVertex::set(aaVertices++, p3.x, p3.y, 0); + AlphaVertex::set(aaVertices++, p2.x, p2.y, 0); + prevAAVertex = aaVertices - 1; + generatedVerticesCount += 4; + } + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + } + } + if (generatedVerticesCount > 0) { + glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount); + } +} + void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { if (mSnapshot->isIgnored()) return; const bool isAA = paint->isAntiAlias(); const float strokeWidth = paint->getStrokeWidth() * 0.5f; - // A stroke width of 0 has a special meaningin Skia: - // it draws an unscaled 1px wide line - const bool isHairLine = paint->getStrokeWidth() == 0.0f; + // A stroke width of 0 has a special meaning in Skia: + // it draws a line 1 px wide regardless of current transform + bool isHairLine = paint->getStrokeWidth() == 0.0f; int alpha; SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - - int verticesCount = count >> 2; int generatedVerticesCount = 0; - if (!isHairLine) { - // TODO: AA needs more vertices - verticesCount *= 6; - } else { - // TODO: AA will be different - verticesCount *= 2; - } - - TextureVertex lines[verticesCount]; - TextureVertex* vertex = &lines[0]; setupDraw(); + if (isAA) { + setupDrawAALine(); + } setupDrawColor(paint->getColor(), alpha); setupDrawColorFilter(); setupDrawShader(); - setupDrawBlending(mode); + if (isAA) { + setupDrawBlending(true, mode); + } else { + setupDrawBlending(mode); + } setupDrawProgram(); setupDrawModelViewIdentity(); setupDrawColorUniforms(); setupDrawColorFilterUniforms(); setupDrawShaderIdentityUniforms(); - setupDrawMesh(vertex); if (!isHairLine) { - // TODO: Handle the AA case - for (int i = 0; i < count; i += 4) { - // a = start point, b = end point - vec2 a(points[i], points[i + 1]); - vec2 b(points[i + 2], points[i + 3]); - - // Bias to snap to the same pixels as Skia - a += 0.375; - b += 0.375; - - // Find the normal to the line - vec2 n = (b - a).copyNormalized() * strokeWidth; - float x = n.x; - n.x = -n.y; - n.y = x; - - // Four corners of the rectangle defining a thick line - vec2 p1 = a - n; - vec2 p2 = a + n; - vec2 p3 = b + n; - vec2 p4 = b - n; - - const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x))); - const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x))); - const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y))); - const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y))); - - if (!quickReject(left, top, right, bottom)) { - // Draw the line as 2 triangles, could be optimized - // by using only 4 vertices and the correct indices - // Also we should probably used non textured vertices - // when line AA is disabled to save on bandwidth - TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p2.x, p2.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p4.x, p4.y, 0.0f, 0.0f); - - generatedVerticesCount += 6; - - dirtyLayer(left, top, right, bottom, *mSnapshot->transform); - } - } - - if (generatedVerticesCount > 0) { - // GL_LINE does not give the result we want to match Skia - glDrawArrays(GL_TRIANGLES, 0, generatedVerticesCount); - } + drawLinesAsQuads(points, count, isAA, isHairLine, strokeWidth); } else { - // TODO: Handle the AA case - for (int i = 0; i < count; i += 4) { - TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f); - TextureVertex::set(vertex++, points[i + 2], points[i + 3], 0.0f, 0.0f); - - generatedVerticesCount += 2; - - const float left = fmin(points[i], points[i + 2]); - const float right = fmax(points[i], points[i + 2]); - const float top = fmin(points[i + 1], points[i + 3]); - const float bottom = fmax(points[i + 1], points[i + 3]); + if (isAA) { + drawLinesAsQuads(points, count, isAA, isHairLine, .5f); + } else { + int verticesCount = count >> 1; + Vertex lines[verticesCount]; + Vertex* vertices = &lines[0]; + setupDrawVertices(vertices); + for (int i = 0; i < count; i += 4) { + + const float left = fmin(points[i], points[i + 2]); + const float right = fmax(points[i], points[i + 2]); + const float top = fmin(points[i + 1], points[i + 3]); + const float bottom = fmax(points[i + 1], points[i + 3]); + + Vertex::set(vertices++, points[i], points[i + 1]); + Vertex::set(vertices++, points[i + 2], points[i + 3]); + generatedVerticesCount += 2; + dirtyLayer(left, top, + right == left ? left + 1 : right, bottom == top ? top + 1 : bottom, + *mSnapshot->transform); + } - dirtyLayer(left, top, - right == left ? left + 1 : right, bottom == top ? top + 1 : bottom, - *mSnapshot->transform); + glLineWidth(1.0f); + glDrawArrays(GL_LINES, 0, generatedVerticesCount); } - - glLineWidth(1.0f); - glDrawArrays(GL_LINES, 0, generatedVerticesCount); } } @@ -1467,7 +1588,7 @@ void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) { // TODO: The paint's cap style defines whether the points are square or circular // TODO: Handle AA for round points - // A stroke width of 0 has a special meaningin Skia: + // A stroke width of 0 has a special meaning in Skia: // it draws an unscaled 1px point const bool isHairLine = paint->getStrokeWidth() == 0.0f; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 4b93b80..0276095 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -283,6 +283,19 @@ private: void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint); /** + * Draws a line as a quad. Called by drawLines() for all cases except hairline without AA. + * + * @param points The vertices of the lines. Every four entries specifies the x/y points + * of a single line segment. + * @param count The number of entries in the points array. + * @param isAA Whether the line is anti-aliased + * @param isHairline Whether the line has strokeWidth==0, which results in the line being + * one pixel wide on the display regardless of scale. + */ + void drawLinesAsQuads(float *points, int count, bool isAA, bool isHairline, + float strokeWidth); + + /** * Draws a textured rectangle with the specified texture. The specified coordinates * are transformed by the current snapshot's transform matrix. * @@ -425,6 +438,7 @@ private: * Various methods to setup OpenGL rendering. */ void setupDrawWithTexture(bool isAlpha8 = false); + void setupDrawAALine(); void setupDrawPoint(float pointSize); void setupDrawColor(int color); void setupDrawColor(int color, int alpha); @@ -453,6 +467,8 @@ private: void setupDrawSimpleMesh(); void setupDrawTexture(GLuint texture); void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0); + void setupDrawVertices(GLvoid* vertices); + void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth); void finishDrawTexture(); void drawRegionRects(const Region& region); diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index f0bc36b..b873bb8 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -39,6 +39,8 @@ const char* gVS_Header_Attributes = "attribute vec4 position;\n"; const char* gVS_Header_Attributes_TexCoords = "attribute vec2 texCoords;\n"; +const char* gVS_Header_Attributes_Distance = + "attribute float vtxDistance;\n"; const char* gVS_Header_Uniforms = "uniform mat4 transform;\n"; const char* gVS_Header_Uniforms_IsPoint = @@ -56,6 +58,8 @@ 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_HasWidth = + "varying float distance;\n"; const char* gVS_Header_Varyings_HasBitmap = "varying vec2 outBitmapTexCoords;\n"; const char* gVS_Header_Varyings_PointHasBitmap = @@ -88,6 +92,8 @@ const char* gVS_Main_Position = " gl_Position = transform * position;\n"; const char* gVS_Main_PointSize = " gl_PointSize = pointSize;\n"; +const char* gVS_Main_Width = + " distance = vtxDistance;\n"; const char* gVS_Footer = "}\n\n"; @@ -101,6 +107,10 @@ const char* gFS_Header = "precision mediump float;\n\n"; const char* gFS_Uniforms_Color = "uniform vec4 color;\n"; +const char* gFS_Uniforms_Width = + "uniform float width;\n" + "uniform float boundaryWidth;\n" + "uniform float inverseBoundaryWidth;\n"; const char* gFS_Header_Uniforms_PointHasBitmap = "uniform vec2 textureDimension;\n" "uniform float pointSize;\n"; @@ -169,6 +179,12 @@ const char* gFS_Fast_SingleModulateGradient = // General case const char* gFS_Main_FetchColor = " fragColor = color;\n"; +const char* gFS_Main_AccountForWidth = + " if (distance < boundaryWidth) {\n" + " fragColor *= (distance * inverseBoundaryWidth);\n" + " } else if (distance > (1.0 - boundaryWidth)) {\n" + " fragColor *= ((1.0 - distance) * inverseBoundaryWidth);\n" + " }\n"; const char* gFS_Main_FetchTexture[2] = { // Don't modulate " fragColor = texture2D(sampler, outTexCoords);\n", @@ -354,6 +370,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasTexture) { shader.append(gVS_Header_Attributes_TexCoords); } + if (description.hasWidth) { + shader.append(gVS_Header_Attributes_Distance); + } // Uniforms shader.append(gVS_Header_Uniforms); if (description.hasGradient) { @@ -369,6 +388,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasTexture) { shader.append(gVS_Header_Varyings_HasTexture); } + if (description.hasWidth) { + shader.append(gVS_Header_Varyings_HasWidth); + } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); } @@ -383,6 +405,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasTexture) { shader.append(gVS_Main_OutTexCoords); } + if (description.hasWidth) { + shader.append(gVS_Main_Width); + } if (description.hasGradient) { shader.append(gVS_Main_OutGradient[description.gradientType]); } @@ -420,6 +445,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasTexture) { shader.append(gVS_Header_Varyings_HasTexture); } + if (description.hasWidth) { + shader.append(gVS_Header_Varyings_HasWidth); + } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); } @@ -441,6 +469,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasTexture) { shader.append(gFS_Uniforms_TextureSampler); } + if (description.hasWidth) { + shader.append(gFS_Uniforms_Width); + } if (description.hasGradient) { shader.append(gFS_Uniforms_GradientSampler[description.gradientType]); } @@ -449,8 +480,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } // Optimization for common cases - if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone && - !description.isPoint) { + if (!description.hasWidth && !blendFramebuffer && + description.colorOp == ProgramDescription::kColorNone && !description.isPoint) { bool fast = false; const bool noShader = !description.hasGradient && !description.hasBitmap; @@ -534,6 +565,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Main_FetchColor); } } + if (description.hasWidth) { + shader.append(gFS_Main_AccountForWidth); + } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[description.gradientType]); } diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 737d91b..cead75b 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -57,6 +57,7 @@ namespace uirenderer { #define PROGRAM_KEY_COLOR_BLEND 0x80 #define PROGRAM_KEY_BITMAP_NPOT 0x100 #define PROGRAM_KEY_SWAP_SRC_DST 0x2000 +#define PROGRAM_KEY_VERTEX_WIDTH 0x4000 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 @@ -119,6 +120,8 @@ struct ProgramDescription { bool hasBitmap; bool isBitmapNpot; + bool hasWidth; + bool hasGradient; Gradient gradientType; @@ -148,6 +151,8 @@ struct ProgramDescription { hasTexture = false; hasAlpha8Texture = false; + hasWidth = false; + modulate = false; hasBitmap = false; @@ -200,6 +205,7 @@ struct ProgramDescription { programid key() const { programid key = 0; if (hasTexture) key |= PROGRAM_KEY_TEXTURE; + if (hasWidth) key |= PROGRAM_KEY_VERTEX_WIDTH; if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE; if (hasBitmap) { key |= PROGRAM_KEY_BITMAP; diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index bbf4d4a..c120428 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -23,6 +23,18 @@ namespace uirenderer { /** * Simple structure to describe a vertex with a position and a texture. */ +struct Vertex { + float position[2]; + + static inline void set(Vertex* vertex, float x, float y) { + vertex[0].position[0] = x; + vertex[0].position[1] = y; + } +}; // struct Vertex + +/** + * Simple structure to describe a vertex with a position and a texture. + */ struct TextureVertex { float position[2]; float texture[2]; @@ -40,6 +52,22 @@ struct TextureVertex { } }; // struct TextureVertex +/** + * Simple structure to describe a vertex with a position and an alpha value. + */ +struct AlphaVertex : Vertex { + float alpha; + + static inline void set(AlphaVertex* vertex, float x, float y, float alpha) { + Vertex::set(vertex, x, y); + vertex[0].alpha = alpha; + } + + static inline void setColor(AlphaVertex* vertex, float alpha) { + vertex[0].alpha = alpha; + } +}; // struct AlphaVertex + }; // namespace uirenderer }; // namespace android diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 2afc935..c763b1d 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -388,6 +388,15 @@ + + + + + + + diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java new file mode 100644 index 0000000..ccf0631 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.view.View; +import android.widget.FrameLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class Lines2Activity extends Activity { + private ObjectAnimator mAnimator; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000)); + FrameLayout frame = new FrameLayout(this); + final LinesView gpuView = new LinesView(this, 0, Color.GREEN); + frame.addView(gpuView); + final LinesView swView = new LinesView(this, 400, Color.RED); + swView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + frame.addView(swView); + final LinesView hwBothView = new LinesView(this, 850, Color.GREEN); + // BUG: some lines not drawn or drawn with alpha when enabling hw layers +// hwBothView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + frame.addView(hwBothView); + final LinesView swBothView = new LinesView(this, 854, Color.RED); + swBothView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + frame.addView(swBothView); + setContentView(frame); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + public static class LinesView extends View { + + private float mOffset; + private int mColor; + private float[] basePoints = { + 120, 0, 120, 20, 120, 20, 125, 0, 130, 0, 132, 10 + }; + private float[] copyPoints = new float[12]; + + public LinesView(Context c, float offset, int color) { + super(c); + mOffset = offset; + mColor = color; + } + + private void drawLines(Canvas canvas, Paint p, float xOffset, float yOffset) { + canvas.drawLine(10 + xOffset, yOffset, 10 + xOffset, 40 + yOffset, p); + canvas.drawLine(30 + xOffset, yOffset, 40 + xOffset, 40 + yOffset, p); + canvas.drawLine(40 + xOffset, yOffset, 75 + xOffset, 35 + yOffset, p); + canvas.drawLine(50 + xOffset, 5+ yOffset, 100 + xOffset, 15 + yOffset, p); + canvas.drawLine(60 + xOffset, yOffset, 110 + xOffset, 2 + yOffset, p); + canvas.drawLine(60 + xOffset, 40 + yOffset, 110 + xOffset, 40 + yOffset, p); + for (int i = 0; i < 12; i += 2) { + copyPoints[i] = basePoints[i] + xOffset; + copyPoints[i+1] = basePoints[i+1] + yOffset; + } + canvas.drawLines(copyPoints, 0, 12, p); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + Paint p = new Paint(); + p.setColor(mColor); + float yOffset = 10; + + canvas.save(); + drawLines(canvas, p, mOffset, yOffset); + canvas.scale(2, 2); + canvas.translate(60, 0); + drawLines(canvas, p, mOffset/2, yOffset/2); + canvas.restore(); + + yOffset +=100; + canvas.save(); + p.setStrokeWidth(1); + drawLines(canvas, p, mOffset, yOffset); + canvas.scale(2, 2); + canvas.translate(60, 0); + drawLines(canvas, p, mOffset/2, yOffset/2); + canvas.restore(); + + yOffset += 100; + canvas.save(); + p.setStrokeWidth(2); + drawLines(canvas, p, mOffset, yOffset); + canvas.scale(2, 2); + canvas.translate(60, 0); + drawLines(canvas, p, mOffset/2, yOffset/2); + canvas.restore(); + + p.setAntiAlias(true); + p.setStrokeWidth(0); + yOffset += 100; + canvas.save(); + drawLines(canvas, p, mOffset, yOffset); + canvas.scale(2, 2); + canvas.translate(60, 0); + drawLines(canvas, p, mOffset/2, yOffset/2); + canvas.restore(); + + yOffset += 100; + canvas.save(); + p.setStrokeWidth(1); + drawLines(canvas, p, mOffset, yOffset); + canvas.scale(2, 2); + canvas.translate(60, 0); + drawLines(canvas, p, mOffset/2, yOffset/2); + canvas.restore(); + + yOffset += 100; + canvas.save(); + p.setStrokeWidth(2); + drawLines(canvas, p, mOffset, yOffset); + canvas.scale(2, 2); + canvas.translate(60, 0); + drawLines(canvas, p, mOffset/2, yOffset/2); + canvas.restore(); + } + } +} diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java index 4233367..c3a91ce 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java @@ -53,7 +53,7 @@ public class LinesActivity extends Activity { } public static class LinesView extends View { - private static final boolean LINE_AA = false; + private static final boolean LINE_AA = true; private final Bitmap mBitmap1; private final Paint mSmallPaint; -- cgit v1.1