Fix various hw-accelerated line/point bugs
All accelerated lines are now rendered as quads. Hairlines used to
be rendered as GL_LINES, but these lines don't render the same as our
non-accelerated lines, so we're using quads for everything. Also, fixed
a bug in the way that we were offsetting quads (and not offseting points)
to ensure that our lines/points actuall start on the same pixels as
Skia's.
Change-Id: I51b923cc08a9858444c430ba07bc8aa0c83cbe6a
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index dd0cca22..e1d7f6b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -979,8 +979,8 @@
}
}
-void OpenGLRenderer::setupDrawModelViewIdentity() {
- mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform);
+void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
+ mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform, offset);
}
void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
@@ -1389,13 +1389,46 @@
}
}
-void OpenGLRenderer::drawLinesAsQuads(float *points, int count, bool isAA, bool isHairline,
- float strokeWidth) {
+void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
+ if (mSnapshot->isIgnored()) return;
+
+ const bool isAA = paint->isAntiAlias();
+ float strokeWidth = paint->getStrokeWidth() * 0.5f;
+ // 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;
+ int generatedVerticesCount = 0;
int verticesCount = count;
if (count > 4) {
// Polyline: account for extra vertices needed for continous tri-strip
verticesCount += (count -4);
}
+
+ getAlphaAndMode(paint, &alpha, &mode);
+ setupDraw();
+ if (isAA) {
+ setupDrawAALine();
+ }
+ setupDrawColor(paint->getColor(), alpha);
+ setupDrawColorFilter();
+ setupDrawShader();
+ if (isAA) {
+ setupDrawBlending(true, mode);
+ } else {
+ setupDrawBlending(mode);
+ }
+ setupDrawProgram();
+ setupDrawModelViewIdentity(true);
+ setupDrawColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderIdentityUniforms();
+
+ if (isHairLine) {
+ // Set a real stroke width to be used in quad construction
+ strokeWidth = .5;
+ }
if (isAA) {
// Expand boundary to enable AA calculations on the quad border
strokeWidth += .5f;
@@ -1407,25 +1440,22 @@
if (!isAA) {
setupDrawVertices(vertices);
} else {
- AlphaVertex* alphaCoords = aaVertices + gVertexAlphaOffset;
+ void* alphaCoords = ((GLbyte*) 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);
+ setupDrawAALine((void*) aaVertices, alphaCoords, innerProportion);
}
- int generatedVerticesCount = 0;
AlphaVertex *prevAAVertex = NULL;
Vertex *prevVertex = NULL;
float inverseScaleX = 1.0f;
float inverseScaleY = 1.0f;
- if (isHairline) {
+ 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];
@@ -1446,22 +1476,19 @@
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);
+ if (isHairLine) {
+ n *= inverseScaleX;
+ if (isAA) {
+ float wideningFactor;
+ if (fabs(n.x) >= fabs(n.y)) {
+ wideningFactor = fabs(1.0f / n.x);
+ } else {
+ wideningFactor = fabs(1.0f / n.y);
+ }
+ n *= wideningFactor;
}
- n.x *= inverseScaleX;
- n.y *= inverseScaleY;
- n *= wideningFactor;
}
float x = n.x;
n.x = -n.y;
@@ -1525,69 +1552,6 @@
}
}
-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 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 generatedVerticesCount = 0;
-
- setupDraw();
- if (isAA) {
- setupDrawAALine();
- }
- setupDrawColor(paint->getColor(), alpha);
- setupDrawColorFilter();
- setupDrawShader();
- if (isAA) {
- setupDrawBlending(true, mode);
- } else {
- setupDrawBlending(mode);
- }
- setupDrawProgram();
- setupDrawModelViewIdentity();
- setupDrawColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderIdentityUniforms();
-
- if (!isHairLine) {
- drawLinesAsQuads(points, count, isAA, isHairLine, strokeWidth);
- } else {
- 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);
- }
-
- glLineWidth(1.0f);
- glDrawArrays(GL_LINES, 0, generatedVerticesCount);
- }
- }
-}
-
void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
if (mSnapshot->isIgnored()) return;
@@ -1596,8 +1560,13 @@
// A stroke width of 0 has a special meaning in Skia:
// it draws an unscaled 1px point
+ float strokeWidth = paint->getStrokeWidth();
const bool isHairLine = paint->getStrokeWidth() == 0.0f;
-
+ if (isHairLine) {
+ // Now that we know it's hairline, we can set the effective width, to be used later
+ strokeWidth = 1.0f;
+ }
+ const float halfWidth = strokeWidth / 2;
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
@@ -1609,13 +1578,13 @@
TextureVertex* vertex = &pointsData[0];
setupDraw();
- setupDrawPoint(isHairLine ? 1.0f : paint->getStrokeWidth());
+ setupDrawPoint(strokeWidth);
setupDrawColor(paint->getColor(), alpha);
setupDrawColorFilter();
setupDrawShader();
setupDrawBlending(mode);
setupDrawProgram();
- setupDrawModelViewIdentity();
+ setupDrawModelViewIdentity(true);
setupDrawColorUniforms();
setupDrawColorFilterUniforms();
setupDrawPointUniforms();
@@ -1625,6 +1594,11 @@
for (int i = 0; i < count; i += 2) {
TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
generatedVerticesCount++;
+ float left = points[i] - halfWidth;
+ float right = points[i] + halfWidth;
+ float top = points[i + 1] - halfWidth;
+ float bottom = points [i + 1] + halfWidth;
+ dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
}
glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0276095..918e1fb 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -283,19 +283,6 @@
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.
*
@@ -453,7 +440,7 @@
bool swapSrcDst = false);
void setupDrawProgram();
void setupDrawDirtyRegionsDisabled();
- void setupDrawModelViewIdentity();
+ void setupDrawModelViewIdentity(bool offset = false);
void setupDrawModelView(float left, float top, float right, float bottom,
bool ignoreTransform = false, bool ignoreModelView = false);
void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 2187f24..972dd87 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -124,8 +124,15 @@
}
void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
- const mat4& transformMatrix) {
+ const mat4& transformMatrix, bool offset) {
mat4 t(projectionMatrix);
+ if (offset) {
+ // offset screenspace xy by an amount that compensates for typical precision issues
+ // in GPU hardware that tends to paint hor/vert lines in pixels shifted up and to the left.
+ // This offset value is based on an assumption that some hardware may use as little
+ // as 12.4 precision, so we offset by slightly more than 1/16.
+ t.translate(.375, .375, 0);
+ }
t.multiply(transformMatrix);
t.multiply(modelViewMatrix);
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index afc6f3d..764cb05 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -81,7 +81,7 @@
* transform matrices.
*/
void set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
- const mat4& transformMatrix);
+ const mat4& transformMatrix, bool offset = false);
/**
* Sets the color associated with this shader.