summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChet Haase <chet@google.com>2011-04-21 11:52:59 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-04-21 11:52:59 -0700
commitbf96c356c02c430153c281fb81dc1a00f7ad0b43 (patch)
treeae1ac4bbd2c2c28b1b498dc87d47564806155c3a
parent52c03d2cb9a782366e804d1910a7c8f072b05353 (diff)
parent5b0200bd47e8a9a4dc8d2e6c3a110d522b30bf82 (diff)
downloadframeworks_base-bf96c356c02c430153c281fb81dc1a00f7ad0b43.zip
frameworks_base-bf96c356c02c430153c281fb81dc1a00f7ad0b43.tar.gz
frameworks_base-bf96c356c02c430153c281fb81dc1a00f7ad0b43.tar.bz2
Merge "Enable anti-aliasing for hw-accelerated lines"
-rw-r--r--libs/hwui/Caches.h3
-rw-r--r--libs/hwui/OpenGLRenderer.cpp289
-rw-r--r--libs/hwui/OpenGLRenderer.h16
-rw-r--r--libs/hwui/ProgramCache.cpp38
-rw-r--r--libs/hwui/ProgramCache.h6
-rw-r--r--libs/hwui/Vertex.h28
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml9
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java149
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java2
9 files changed, 453 insertions, 87 deletions
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 @@
</activity>
<activity
+ android:name="Lines2Activity"
+ android:label="_Lines2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="PathsActivity"
android:label="_Paths">
<intent-filter>
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;