Skip to content

Commit 16b897c

Browse files
committed
Fix rectangle AA offset calculation
bug:4419017 Fixes compiler warning Handle bezier thresholds with large stroke widths better Fix sub-hairlines (for scaleX == scaleY) Change-Id: Ida387483348ee61424b7fba729abca2a88bd68b3
1 parent 9a4a037 commit 16b897c

File tree

2 files changed

+54
-29
lines changed

2 files changed

+54
-29
lines changed

libs/hwui/Caches.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ class ANDROID_API Caches: public Singleton<Caches> {
294294
GLuint mCurrentBuffer;
295295
GLuint mCurrentIndicesBuffer;
296296
void* mCurrentPositionPointer;
297-
GLuint mCurrentPositionStride;
297+
GLsizei mCurrentPositionStride;
298298
void* mCurrentTexCoordsPointer;
299299

300300
bool mTexCoordsArrayEnabled;

libs/hwui/PathRenderer.cpp

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,34 @@ void computeInverseScales(const mat4 *transform, float &inverseScaleX, float& in
5757
float m11 = transform->data[Matrix4::kScaleY];
5858
float scaleX = sqrt(m00 * m00 + m01 * m01);
5959
float scaleY = sqrt(m10 * m10 + m11 * m11);
60-
inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 0;
61-
inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 0;
60+
inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
61+
inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
6262
} else {
6363
inverseScaleX = 1.0f;
6464
inverseScaleY = 1.0f;
6565
}
6666
}
6767

68-
inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr)
69-
{
68+
inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
7069
Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
7170
}
7271

73-
inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr)
74-
{
72+
inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
7573
AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
7674
}
7775

76+
/**
77+
* Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
78+
* from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
79+
* will be offset by 1.0
80+
*
81+
* Note that we can't add and normalize the two vectors, that would result in a rectangle having an
82+
* offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
83+
*/
84+
inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
85+
return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
86+
}
87+
7888
void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
7989
Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
8090

@@ -108,9 +118,7 @@ void getStrokeVerticesFromPerimeter(const Vector<Vertex>& perimeter, float halfS
108118
current->position[0] - next->position[0]);
109119
nextNormal.normalize();
110120

111-
// offset each point by its normal, out and in, by appropriate stroke offset
112-
vec2 totalOffset = (lastNormal + nextNormal);
113-
totalOffset.normalize();
121+
vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
114122
if (halfStrokeWidth == 0.0f) {
115123
// hairline - compensate for scale
116124
totalOffset.x *= 0.5f * inverseScaleX;
@@ -155,12 +163,11 @@ void getFillVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, VertexBuffe
155163
current->position[0] - next->position[0]);
156164
nextNormal.normalize();
157165

158-
// AA point offset from original point is that point's normal, such that
159-
// each side is offset by .5 pixels
160-
vec2 totalOffset = (lastNormal + nextNormal);
161-
totalOffset.normalize();
162-
totalOffset.x *= inverseScaleX * 0.5f;
163-
totalOffset.y *= inverseScaleY * 0.5f;
166+
// AA point offset from original point is that point's normal, such that each side is offset
167+
// by .5 pixels
168+
vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
169+
totalOffset.x *= 0.5f * inverseScaleX;
170+
totalOffset.y *= 0.5f * inverseScaleY;
164171

165172
AlphaVertex::set(&buffer[currentIndex++],
166173
current->position[0] + totalOffset.x,
@@ -204,6 +211,15 @@ void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float hal
204211
VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
205212
AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
206213

214+
// avoid lines smaller than hairline since they break triangle based sampling. instead reducing
215+
// alpha value (TODO: support different X/Y scale)
216+
float maxAlpha = 1.0f;
217+
if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
218+
halfStrokeWidth * inverseScaleX < 1.0f) {
219+
maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
220+
halfStrokeWidth = 0.0f;
221+
}
222+
207223
int offset = 2 * perimeter.size() + 3;
208224
int currentAAOuterIndex = 0;
209225
int currentStrokeIndex = offset;
@@ -220,13 +236,12 @@ void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float hal
220236
current->position[0] - next->position[0]);
221237
nextNormal.normalize();
222238

223-
vec2 pointNormal = (lastNormal + nextNormal);
224-
pointNormal.normalize();
225-
vec2 AAOffset = pointNormal * 0.5f;
226-
AAOffset.x *= inverseScaleX;
227-
AAOffset.y *= inverseScaleY;
239+
vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
240+
vec2 AAOffset = totalOffset;
241+
AAOffset.x *= 0.5f * inverseScaleX;
242+
AAOffset.y *= 0.5f * inverseScaleY;
228243

229-
vec2 innerOffset = pointNormal;
244+
vec2 innerOffset = totalOffset;
230245
if (halfStrokeWidth == 0.0f) {
231246
// hairline! - compensate for scale
232247
innerOffset.x *= 0.5f * inverseScaleX;
@@ -244,27 +259,26 @@ void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float hal
244259
AlphaVertex::set(&buffer[currentAAOuterIndex++],
245260
current->position[0] + innerOffset.x,
246261
current->position[1] + innerOffset.y,
247-
1.0f);
262+
maxAlpha);
248263

249264
AlphaVertex::set(&buffer[currentStrokeIndex++],
250265
current->position[0] + innerOffset.x,
251266
current->position[1] + innerOffset.y,
252-
1.0f);
267+
maxAlpha);
253268
AlphaVertex::set(&buffer[currentStrokeIndex++],
254269
current->position[0] - innerOffset.x,
255270
current->position[1] - innerOffset.y,
256-
1.0f);
271+
maxAlpha);
257272

258273
AlphaVertex::set(&buffer[currentAAInnerIndex++],
259274
current->position[0] - innerOffset.x,
260275
current->position[1] - innerOffset.y,
261-
1.0f);
276+
maxAlpha);
262277
AlphaVertex::set(&buffer[currentAAInnerIndex++],
263278
current->position[0] - outerOffset.x,
264279
current->position[1] - outerOffset.y,
265280
0.0f);
266281

267-
// TODO: current = next, copy last normal instead of recalculate
268282
last = current;
269283
current = next;
270284
lastNormal = nextNormal;
@@ -295,8 +309,19 @@ void PathRenderer::convexPathVertices(const SkPath &path, const SkPaint* paint,
295309
computeInverseScales(transform, inverseScaleX, inverseScaleY);
296310

297311
Vector<Vertex> tempVertices;
298-
convexPathPerimeterVertices(path, inverseScaleX * inverseScaleX, inverseScaleY * inverseScaleY,
299-
tempVertices);
312+
float threshInvScaleX = inverseScaleX;
313+
float threshInvScaleY = inverseScaleY;
314+
if (style == SkPaint::kStroke_Style) {
315+
// alter the bezier recursion threshold values we calculate in order to compensate for
316+
// expansion done after the path vertices are found
317+
SkRect bounds = path.getBounds();
318+
if (!bounds.isEmpty()) {
319+
threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
320+
threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
321+
}
322+
}
323+
convexPathPerimeterVertices(path, threshInvScaleX * threshInvScaleX,
324+
threshInvScaleY * threshInvScaleY, tempVertices);
300325

301326
#if VERTEX_DEBUG
302327
for (unsigned int i = 0; i < tempVertices.size(); i++) {

0 commit comments

Comments
 (0)