1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "GrOvalOpFactory.h"
9
10 #include "GrDrawOpTest.h"
11 #include "GrGeometryProcessor.h"
12 #include "GrOpFlushState.h"
13 #include "GrProcessor.h"
14 #include "GrResourceProvider.h"
15 #include "GrShaderCaps.h"
16 #include "GrStyle.h"
17 #include "SkRRect.h"
18 #include "SkStrokeRec.h"
19 #include "glsl/GrGLSLFragmentShaderBuilder.h"
20 #include "glsl/GrGLSLGeometryProcessor.h"
21 #include "glsl/GrGLSLProgramDataManager.h"
22 #include "glsl/GrGLSLUniformHandler.h"
23 #include "glsl/GrGLSLUtil.h"
24 #include "glsl/GrGLSLVarying.h"
25 #include "glsl/GrGLSLVertexShaderBuilder.h"
26 #include "ops/GrMeshDrawOp.h"
27
28 // TODO(joshualitt) - Break this file up during GrOp post implementation cleanup
29
30 namespace {
31
32 struct EllipseVertex {
33 SkPoint fPos;
34 GrColor fColor;
35 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
38 };
39
40 struct DIEllipseVertex {
41 SkPoint fPos;
42 GrColor fColor;
43 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
45 };
46
circle_stays_circle(const SkMatrix & m)47 static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
48 }
49
50 ///////////////////////////////////////////////////////////////////////////////
51
52 /**
53 * The output of this effect is a modulation of the input color and coverage for a circle. It
54 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
55 * with origin at the circle center. Three vertex attributes are used:
56 * vec2f : position in device space of the bounding geometry vertices
57 * vec4ub: color
58 * vec4f : (p.xy, outerRad, innerRad)
59 * p is the position in the normalized space.
60 * outerRad is the outerRadius in device space.
61 * innerRad is the innerRadius in normalized space (ignored if not stroking).
62 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
63 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
64 * vec4f : (v.xy, outerDistance, innerDistance)
65 * v is a normalized vector pointing to the outer edge
66 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
67 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
68 * Additional clip planes are supported for rendering circular arcs. The additional planes are
69 * either intersected or unioned together. Up to three planes are supported (an initial plane,
70 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
71 * are useful for any given arc, but having all three in one instance allows combining different
72 * types of arcs.
73 */
74
75 class CircleGeometryProcessor : public GrGeometryProcessor {
76 public:
CircleGeometryProcessor(bool stroke,bool clipPlane,bool isectPlane,bool unionPlane,const SkMatrix & localMatrix)77 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
78 const SkMatrix& localMatrix)
79 : fLocalMatrix(localMatrix) {
80 this->initClassID<CircleGeometryProcessor>();
81 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
82 kHigh_GrSLPrecision);
83 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
84 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType,
85 kHigh_GrSLPrecision);
86 if (clipPlane) {
87 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
88 } else {
89 fInClipPlane = nullptr;
90 }
91 if (isectPlane) {
92 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
93 } else {
94 fInIsectPlane = nullptr;
95 }
96 if (unionPlane) {
97 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
98 } else {
99 fInUnionPlane = nullptr;
100 }
101 fStroke = stroke;
102 }
103
implementsDistanceVector() const104 bool implementsDistanceVector() const override { return !fInClipPlane; }
105
~CircleGeometryProcessor()106 ~CircleGeometryProcessor() override {}
107
name() const108 const char* name() const override { return "CircleEdge"; }
109
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const110 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
111 GLSLProcessor::GenKey(*this, caps, b);
112 }
113
createGLSLInstance(const GrShaderCaps &) const114 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
115 return new GLSLProcessor();
116 }
117
118 private:
119 class GLSLProcessor : public GrGLSLGeometryProcessor {
120 public:
GLSLProcessor()121 GLSLProcessor() {}
122
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)123 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
124 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
125 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
126 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
127 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
128 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
129
130 // emit attributes
131 varyingHandler->emitAttributes(cgp);
132 fragBuilder->codeAppend("highp vec4 circleEdge;");
133 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge",
134 kHigh_GrSLPrecision);
135 if (cgp.fInClipPlane) {
136 fragBuilder->codeAppend("vec3 clipPlane;");
137 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
138 }
139 if (cgp.fInIsectPlane) {
140 SkASSERT(cgp.fInClipPlane);
141 fragBuilder->codeAppend("vec3 isectPlane;");
142 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
143 }
144 if (cgp.fInUnionPlane) {
145 SkASSERT(cgp.fInClipPlane);
146 fragBuilder->codeAppend("vec3 unionPlane;");
147 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
148 }
149
150 // setup pass through color
151 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
152
153 // Setup position
154 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
155
156 // emit transforms
157 this->emitTransforms(vertBuilder,
158 varyingHandler,
159 uniformHandler,
160 gpArgs->fPositionVar,
161 cgp.fInPosition->fName,
162 cgp.fLocalMatrix,
163 args.fFPCoordTransformHandler);
164
165 fragBuilder->codeAppend("highp float d = length(circleEdge.xy);");
166 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
167 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
168 if (cgp.fStroke) {
169 fragBuilder->codeAppend(
170 "float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
171 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
172 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
173 }
174
175 if (args.fDistanceVectorName) {
176 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
177 fragBuilder->codeAppendf(
178 "if (d == 0.0) {" // if on the center of the circle
179 " %s = vec4(1.0, 0.0, distanceToOuterEdge, "
180 " %s);", // no normalize
181 args.fDistanceVectorName,
182 innerEdgeDistance);
183 fragBuilder->codeAppendf(
184 "} else {"
185 " %s = vec4(normalize(circleEdge.xy),"
186 " distanceToOuterEdge, %s);"
187 "}",
188 args.fDistanceVectorName, innerEdgeDistance);
189 }
190 if (cgp.fInClipPlane) {
191 fragBuilder->codeAppend(
192 "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
193 "clipPlane.z, 0.0, 1.0);");
194 if (cgp.fInIsectPlane) {
195 fragBuilder->codeAppend(
196 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
197 "isectPlane.z, 0.0, 1.0);");
198 }
199 if (cgp.fInUnionPlane) {
200 fragBuilder->codeAppend(
201 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
202 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
203 }
204 fragBuilder->codeAppend("edgeAlpha *= clip;");
205 }
206 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
207 }
208
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)209 static void GenKey(const GrGeometryProcessor& gp,
210 const GrShaderCaps&,
211 GrProcessorKeyBuilder* b) {
212 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
213 uint16_t key;
214 key = cgp.fStroke ? 0x01 : 0x0;
215 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
216 key |= cgp.fInClipPlane ? 0x04 : 0x0;
217 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
218 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
219 b->add32(key);
220 }
221
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & primProc,FPCoordTransformIter && transformIter)222 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
223 FPCoordTransformIter&& transformIter) override {
224 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
225 pdman, &transformIter);
226 }
227
228 private:
229 typedef GrGLSLGeometryProcessor INHERITED;
230 };
231
232 SkMatrix fLocalMatrix;
233 const Attribute* fInPosition;
234 const Attribute* fInColor;
235 const Attribute* fInCircleEdge;
236 const Attribute* fInClipPlane;
237 const Attribute* fInIsectPlane;
238 const Attribute* fInUnionPlane;
239 bool fStroke;
240
241 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
242
243 typedef GrGeometryProcessor INHERITED;
244 };
245
246 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
247
248 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)249 sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
250 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
251 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
252 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
253 }
254 #endif
255
256 ///////////////////////////////////////////////////////////////////////////////
257
258 /**
259 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
260 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
261 * in both x and y directions.
262 *
263 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
264 */
265
266 class EllipseGeometryProcessor : public GrGeometryProcessor {
267 public:
EllipseGeometryProcessor(bool stroke,const SkMatrix & localMatrix)268 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
269 this->initClassID<EllipseGeometryProcessor>();
270 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
271 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
272 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
273 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
274 fStroke = stroke;
275 }
276
~EllipseGeometryProcessor()277 ~EllipseGeometryProcessor() override {}
278
name() const279 const char* name() const override { return "EllipseEdge"; }
280
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const281 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
282 GLSLProcessor::GenKey(*this, caps, b);
283 }
284
createGLSLInstance(const GrShaderCaps &) const285 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
286 return new GLSLProcessor();
287 }
288
289 private:
290 class GLSLProcessor : public GrGLSLGeometryProcessor {
291 public:
GLSLProcessor()292 GLSLProcessor() {}
293
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)294 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
295 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
296 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
297 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
298 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
299
300 // emit attributes
301 varyingHandler->emitAttributes(egp);
302
303 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
304 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
305 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
306 egp.fInEllipseOffset->fName);
307
308 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
309 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
310 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
311
312 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
313 // setup pass through color
314 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
315
316 // Setup position
317 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
318
319 // emit transforms
320 this->emitTransforms(vertBuilder,
321 varyingHandler,
322 uniformHandler,
323 gpArgs->fPositionVar,
324 egp.fInPosition->fName,
325 egp.fLocalMatrix,
326 args.fFPCoordTransformHandler);
327
328 // for outer curve
329 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
330 ellipseRadii.fsIn());
331 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
332 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
333 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
334
335 // avoid calling inversesqrt on zero.
336 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
337 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
338 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
339
340 // for inner curve
341 if (egp.fStroke) {
342 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
343 ellipseRadii.fsIn());
344 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
345 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
346 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
347 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
348 }
349
350 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
351 }
352
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)353 static void GenKey(const GrGeometryProcessor& gp,
354 const GrShaderCaps&,
355 GrProcessorKeyBuilder* b) {
356 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
357 uint16_t key = egp.fStroke ? 0x1 : 0x0;
358 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
359 b->add32(key);
360 }
361
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & primProc,FPCoordTransformIter && transformIter)362 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
363 FPCoordTransformIter&& transformIter) override {
364 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
365 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
366 }
367
368 private:
369 typedef GrGLSLGeometryProcessor INHERITED;
370 };
371
372 const Attribute* fInPosition;
373 const Attribute* fInColor;
374 const Attribute* fInEllipseOffset;
375 const Attribute* fInEllipseRadii;
376 SkMatrix fLocalMatrix;
377 bool fStroke;
378
379 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
380
381 typedef GrGeometryProcessor INHERITED;
382 };
383
384 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
385
386 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)387 sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
388 return sk_sp<GrGeometryProcessor>(
389 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
390 }
391 #endif
392
393 ///////////////////////////////////////////////////////////////////////////////
394
395 /**
396 * The output of this effect is a modulation of the input color and coverage for an ellipse,
397 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
398 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
399 * using differentials.
400 *
401 * The result is device-independent and can be used with any affine matrix.
402 */
403
404 enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
405
406 class DIEllipseGeometryProcessor : public GrGeometryProcessor {
407 public:
DIEllipseGeometryProcessor(const SkMatrix & viewMatrix,DIEllipseStyle style)408 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
409 : fViewMatrix(viewMatrix) {
410 this->initClassID<DIEllipseGeometryProcessor>();
411 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
412 kHigh_GrSLPrecision);
413 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
414 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
415 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
416 fStyle = style;
417 }
418
~DIEllipseGeometryProcessor()419 ~DIEllipseGeometryProcessor() override {}
420
name() const421 const char* name() const override { return "DIEllipseEdge"; }
422
getGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const423 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
424 GLSLProcessor::GenKey(*this, caps, b);
425 }
426
createGLSLInstance(const GrShaderCaps &) const427 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
428 return new GLSLProcessor();
429 }
430
431 private:
432 class GLSLProcessor : public GrGLSLGeometryProcessor {
433 public:
GLSLProcessor()434 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
435
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)436 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
437 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
438 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
439 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
440 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
441
442 // emit attributes
443 varyingHandler->emitAttributes(diegp);
444
445 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
446 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
447 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
448
449 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
450 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
451 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
452
453 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
454 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
455
456 // Setup position
457 this->setupPosition(vertBuilder,
458 uniformHandler,
459 gpArgs,
460 diegp.fInPosition->fName,
461 diegp.fViewMatrix,
462 &fViewMatrixUniform);
463
464 // emit transforms
465 this->emitTransforms(vertBuilder,
466 varyingHandler,
467 uniformHandler,
468 gpArgs->fPositionVar,
469 diegp.fInPosition->fName,
470 args.fFPCoordTransformHandler);
471
472 // for outer curve
473 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
474 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
475 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
476 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
477 fragBuilder->codeAppendf(
478 "vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
479 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
480 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
481
482 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
483 // avoid calling inversesqrt on zero.
484 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
485 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
486 if (DIEllipseStyle::kHairline == diegp.fStyle) {
487 // can probably do this with one step
488 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
489 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
490 } else {
491 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
492 }
493
494 // for inner curve
495 if (DIEllipseStyle::kStroke == diegp.fStyle) {
496 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
497 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
498 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
499 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
500 fragBuilder->codeAppendf(
501 "grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
502 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
503 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
504 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
505 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
506 }
507
508 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
509 }
510
GenKey(const GrGeometryProcessor & gp,const GrShaderCaps &,GrProcessorKeyBuilder * b)511 static void GenKey(const GrGeometryProcessor& gp,
512 const GrShaderCaps&,
513 GrProcessorKeyBuilder* b) {
514 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
515 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
516 key |= ComputePosKey(diegp.fViewMatrix) << 10;
517 b->add32(key);
518 }
519
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp,FPCoordTransformIter && transformIter)520 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
521 FPCoordTransformIter&& transformIter) override {
522 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
523
524 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
525 fViewMatrix = diegp.fViewMatrix;
526 float viewMatrix[3 * 3];
527 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
528 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
529 }
530 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
531 }
532
533 private:
534 SkMatrix fViewMatrix;
535 UniformHandle fViewMatrixUniform;
536
537 typedef GrGLSLGeometryProcessor INHERITED;
538 };
539
540 const Attribute* fInPosition;
541 const Attribute* fInColor;
542 const Attribute* fInEllipseOffsets0;
543 const Attribute* fInEllipseOffsets1;
544 SkMatrix fViewMatrix;
545 DIEllipseStyle fStyle;
546
547 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
548
549 typedef GrGeometryProcessor INHERITED;
550 };
551
552 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
553
554 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)555 sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
556 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
557 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
558 }
559 #endif
560
561 ///////////////////////////////////////////////////////////////////////////////
562
563 // We have two possible cases for geometry for a circle:
564
565 // In the case of a normal fill, we draw geometry for the circle as an octagon.
566 static const uint16_t gFillCircleIndices[] = {
567 // enter the octagon
568 // clang-format off
569 0, 1, 8, 1, 2, 8,
570 2, 3, 8, 3, 4, 8,
571 4, 5, 8, 5, 6, 8,
572 6, 7, 8, 7, 0, 8
573 // clang-format on
574 };
575
576 // For stroked circles, we use two nested octagons.
577 static const uint16_t gStrokeCircleIndices[] = {
578 // enter the octagon
579 // clang-format off
580 0, 1, 9, 0, 9, 8,
581 1, 2, 10, 1, 10, 9,
582 2, 3, 11, 2, 11, 10,
583 3, 4, 12, 3, 12, 11,
584 4, 5, 13, 4, 13, 12,
585 5, 6, 14, 5, 14, 13,
586 6, 7, 15, 6, 15, 14,
587 7, 0, 8, 7, 8, 15,
588 // clang-format on
589 };
590
591
592 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
593 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
594 static const int kVertsPerStrokeCircle = 16;
595 static const int kVertsPerFillCircle = 9;
596
circle_type_to_vert_count(bool stroked)597 static int circle_type_to_vert_count(bool stroked) {
598 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
599 }
600
circle_type_to_index_count(bool stroked)601 static int circle_type_to_index_count(bool stroked) {
602 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
603 }
604
circle_type_to_indices(bool stroked)605 static const uint16_t* circle_type_to_indices(bool stroked) {
606 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
607 }
608
609 ///////////////////////////////////////////////////////////////////////////////
610
611 class CircleOp final : public GrMeshDrawOp {
612 public:
613 DEFINE_OP_CLASS_ID
614
615 /** Optional extra params to render a partial arc rather than a full circle. */
616 struct ArcParams {
617 SkScalar fStartAngleRadians;
618 SkScalar fSweepAngleRadians;
619 bool fUseCenter;
620 };
Make(GrColor color,const SkMatrix & viewMatrix,SkPoint center,SkScalar radius,const GrStyle & style,const ArcParams * arcParams=nullptr)621 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
622 SkPoint center, SkScalar radius, const GrStyle& style,
623 const ArcParams* arcParams = nullptr) {
624 SkASSERT(circle_stays_circle(viewMatrix));
625 const SkStrokeRec& stroke = style.strokeRec();
626 if (style.hasPathEffect()) {
627 return nullptr;
628 }
629 SkStrokeRec::Style recStyle = stroke.getStyle();
630 if (arcParams) {
631 // Arc support depends on the style.
632 switch (recStyle) {
633 case SkStrokeRec::kStrokeAndFill_Style:
634 // This produces a strange result that this op doesn't implement.
635 return nullptr;
636 case SkStrokeRec::kFill_Style:
637 // This supports all fills.
638 break;
639 case SkStrokeRec::kStroke_Style: // fall through
640 case SkStrokeRec::kHairline_Style:
641 // Strokes that don't use the center point are supported with butt cap.
642 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
643 return nullptr;
644 }
645 break;
646 }
647 }
648
649 viewMatrix.mapPoints(¢er, 1);
650 radius = viewMatrix.mapRadius(radius);
651 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
652
653 bool isStrokeOnly =
654 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
655 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
656
657 SkScalar innerRadius = -SK_ScalarHalf;
658 SkScalar outerRadius = radius;
659 SkScalar halfWidth = 0;
660 if (hasStroke) {
661 if (SkScalarNearlyZero(strokeWidth)) {
662 halfWidth = SK_ScalarHalf;
663 } else {
664 halfWidth = SkScalarHalf(strokeWidth);
665 }
666
667 outerRadius += halfWidth;
668 if (isStrokeOnly) {
669 innerRadius = radius - halfWidth;
670 }
671 }
672
673 // The radii are outset for two reasons. First, it allows the shader to simply perform
674 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
675 // Second, the outer radius is used to compute the verts of the bounding box that is
676 // rendered and the outset ensures the box will cover all partially covered by the circle.
677 outerRadius += SK_ScalarHalf;
678 innerRadius -= SK_ScalarHalf;
679 bool stroked = isStrokeOnly && innerRadius > 0.0f;
680 std::unique_ptr<CircleOp> op(new CircleOp());
681 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
682
683 // This makes every point fully inside the intersection plane.
684 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
685 // This makes every point fully outside the union plane.
686 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
687 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
688 center.fX + outerRadius, center.fY + outerRadius);
689 if (arcParams) {
690 // The shader operates in a space where the circle is translated to be centered at the
691 // origin. Here we compute points on the unit circle at the starting and ending angles.
692 SkPoint startPoint, stopPoint;
693 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
694 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
695 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
696 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
697 // radial lines. However, in both cases we have to be careful about the half-circle.
698 // case. In that case the two radial lines are equal and so that edge gets clipped
699 // twice. Since the shared edge goes through the center we fall back on the useCenter
700 // case.
701 bool useCenter =
702 (arcParams->fUseCenter || isStrokeOnly) &&
703 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
704 if (useCenter) {
705 SkVector norm0 = {startPoint.fY, -startPoint.fX};
706 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
707 if (arcParams->fSweepAngleRadians > 0) {
708 norm0.negate();
709 } else {
710 norm1.negate();
711 }
712 op->fClipPlane = true;
713 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
714 op->fGeoData.emplace_back(Geometry{
715 color,
716 innerRadius,
717 outerRadius,
718 {norm0.fX, norm0.fY, 0.5f},
719 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
720 {norm1.fX, norm1.fY, 0.5f},
721 devBounds,
722 stroked});
723 op->fClipPlaneIsect = false;
724 op->fClipPlaneUnion = true;
725 } else {
726 op->fGeoData.emplace_back(Geometry{
727 color,
728 innerRadius,
729 outerRadius,
730 {norm0.fX, norm0.fY, 0.5f},
731 {norm1.fX, norm1.fY, 0.5f},
732 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
733 devBounds,
734 stroked});
735 op->fClipPlaneIsect = true;
736 op->fClipPlaneUnion = false;
737 }
738 } else {
739 // We clip to a secant of the original circle.
740 startPoint.scale(radius);
741 stopPoint.scale(radius);
742 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
743 norm.normalize();
744 if (arcParams->fSweepAngleRadians > 0) {
745 norm.negate();
746 }
747 SkScalar d = -norm.dot(startPoint) + 0.5f;
748
749 op->fGeoData.emplace_back(
750 Geometry{color,
751 innerRadius,
752 outerRadius,
753 {norm.fX, norm.fY, d},
754 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
755 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
756 devBounds,
757 stroked});
758 op->fClipPlane = true;
759 op->fClipPlaneIsect = false;
760 op->fClipPlaneUnion = false;
761 }
762 } else {
763 op->fGeoData.emplace_back(
764 Geometry{color,
765 innerRadius,
766 outerRadius,
767 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
768 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
769 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
770 devBounds,
771 stroked});
772 op->fClipPlane = false;
773 op->fClipPlaneIsect = false;
774 op->fClipPlaneUnion = false;
775 }
776 // Use the original radius and stroke radius for the bounds so that it does not include the
777 // AA bloat.
778 radius += halfWidth;
779 op->setBounds(
780 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
781 HasAABloat::kYes, IsZeroArea::kNo);
782 op->fVertCount = circle_type_to_vert_count(stroked);
783 op->fIndexCount = circle_type_to_index_count(stroked);
784 op->fAllFill = !stroked;
785 return std::move(op);
786 }
787
name() const788 const char* name() const override { return "CircleOp"; }
789
dumpInfo() const790 SkString dumpInfo() const override {
791 SkString string;
792 for (int i = 0; i < fGeoData.count(); ++i) {
793 string.appendf(
794 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
795 "InnerRad: %.2f, OuterRad: %.2f\n",
796 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
797 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
798 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
799 }
800 string.append(DumpPipelineInfo(*this->pipeline()));
801 string.append(INHERITED::dumpInfo());
802 return string;
803 }
804
805 private:
CircleOp()806 CircleOp() : INHERITED(ClassID()) {}
807
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const808 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
809 GrPipelineAnalysisCoverage* coverage) const override {
810 color->setToConstant(fGeoData[0].fColor);
811 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
812 }
813
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)814 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
815 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
816 if (!optimizations.readsLocalCoords()) {
817 fViewMatrixIfUsingLocalCoords.reset();
818 }
819 }
820
onPrepareDraws(Target * target) const821 void onPrepareDraws(Target* target) const override {
822 SkMatrix localMatrix;
823 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
824 return;
825 }
826
827 // Setup geometry processor
828 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
829 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
830
831 struct CircleVertex {
832 SkPoint fPos;
833 GrColor fColor;
834 SkPoint fOffset;
835 SkScalar fOuterRadius;
836 SkScalar fInnerRadius;
837 // These planes may or may not be present in the vertex buffer.
838 SkScalar fHalfPlanes[3][3];
839 };
840
841 int instanceCount = fGeoData.count();
842 size_t vertexStride = gp->getVertexStride();
843 SkASSERT(vertexStride ==
844 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
845 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
846 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
847
848 const GrBuffer* vertexBuffer;
849 int firstVertex;
850 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
851 &firstVertex);
852 if (!vertices) {
853 SkDebugf("Could not allocate vertices\n");
854 return;
855 }
856
857 const GrBuffer* indexBuffer = nullptr;
858 int firstIndex = 0;
859 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
860 if (!indices) {
861 SkDebugf("Could not allocate indices\n");
862 return;
863 }
864
865 int currStartVertex = 0;
866 for (int i = 0; i < instanceCount; i++) {
867 const Geometry& geom = fGeoData[i];
868
869 GrColor color = geom.fColor;
870 SkScalar innerRadius = geom.fInnerRadius;
871 SkScalar outerRadius = geom.fOuterRadius;
872
873 const SkRect& bounds = geom.fDevBounds;
874 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
875 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
876 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
877 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
878 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
879 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
880 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
881 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
882
883 // The inner radius in the vertex data must be specified in normalized space.
884 innerRadius = innerRadius / outerRadius;
885
886 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
887 SkScalar halfWidth = 0.5f * bounds.width();
888 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
889
890 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
891 v0->fColor = color;
892 v0->fOffset = SkPoint::Make(-octOffset, -1);
893 v0->fOuterRadius = outerRadius;
894 v0->fInnerRadius = innerRadius;
895
896 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
897 v1->fColor = color;
898 v1->fOffset = SkPoint::Make(octOffset, -1);
899 v1->fOuterRadius = outerRadius;
900 v1->fInnerRadius = innerRadius;
901
902 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
903 v2->fColor = color;
904 v2->fOffset = SkPoint::Make(1, -octOffset);
905 v2->fOuterRadius = outerRadius;
906 v2->fInnerRadius = innerRadius;
907
908 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
909 v3->fColor = color;
910 v3->fOffset = SkPoint::Make(1, octOffset);
911 v3->fOuterRadius = outerRadius;
912 v3->fInnerRadius = innerRadius;
913
914 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
915 v4->fColor = color;
916 v4->fOffset = SkPoint::Make(octOffset, 1);
917 v4->fOuterRadius = outerRadius;
918 v4->fInnerRadius = innerRadius;
919
920 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
921 v5->fColor = color;
922 v5->fOffset = SkPoint::Make(-octOffset, 1);
923 v5->fOuterRadius = outerRadius;
924 v5->fInnerRadius = innerRadius;
925
926 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
927 v6->fColor = color;
928 v6->fOffset = SkPoint::Make(-1, octOffset);
929 v6->fOuterRadius = outerRadius;
930 v6->fInnerRadius = innerRadius;
931
932 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
933 v7->fColor = color;
934 v7->fOffset = SkPoint::Make(-1, -octOffset);
935 v7->fOuterRadius = outerRadius;
936 v7->fInnerRadius = innerRadius;
937
938 if (fClipPlane) {
939 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
940 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
941 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
942 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
943 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
944 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
945 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
946 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
947 }
948 int unionIdx = 1;
949 if (fClipPlaneIsect) {
950 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
951 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
952 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
953 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
954 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
955 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
956 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
957 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
958 unionIdx = 2;
959 }
960 if (fClipPlaneUnion) {
961 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
962 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
963 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
964 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
965 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
966 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
967 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
968 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
969 }
970
971 if (geom.fStroked) {
972 // compute the inner ring
973 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
974 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
975 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
976 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
977 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
978 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
979 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
980 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
981
982 // cosine and sine of pi/8
983 SkScalar c = 0.923579533f;
984 SkScalar s = 0.382683432f;
985 SkScalar r = geom.fInnerRadius;
986
987 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
988 v0->fColor = color;
989 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
990 v0->fOuterRadius = outerRadius;
991 v0->fInnerRadius = innerRadius;
992
993 v1->fPos = center + SkPoint::Make(s * r, -c * r);
994 v1->fColor = color;
995 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
996 v1->fOuterRadius = outerRadius;
997 v1->fInnerRadius = innerRadius;
998
999 v2->fPos = center + SkPoint::Make(c * r, -s * r);
1000 v2->fColor = color;
1001 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
1002 v2->fOuterRadius = outerRadius;
1003 v2->fInnerRadius = innerRadius;
1004
1005 v3->fPos = center + SkPoint::Make(c * r, s * r);
1006 v3->fColor = color;
1007 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
1008 v3->fOuterRadius = outerRadius;
1009 v3->fInnerRadius = innerRadius;
1010
1011 v4->fPos = center + SkPoint::Make(s * r, c * r);
1012 v4->fColor = color;
1013 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
1014 v4->fOuterRadius = outerRadius;
1015 v4->fInnerRadius = innerRadius;
1016
1017 v5->fPos = center + SkPoint::Make(-s * r, c * r);
1018 v5->fColor = color;
1019 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
1020 v5->fOuterRadius = outerRadius;
1021 v5->fInnerRadius = innerRadius;
1022
1023 v6->fPos = center + SkPoint::Make(-c * r, s * r);
1024 v6->fColor = color;
1025 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
1026 v6->fOuterRadius = outerRadius;
1027 v6->fInnerRadius = innerRadius;
1028
1029 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
1030 v7->fColor = color;
1031 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
1032 v7->fOuterRadius = outerRadius;
1033 v7->fInnerRadius = innerRadius;
1034
1035 if (fClipPlane) {
1036 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1037 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1039 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1040 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1041 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1042 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1043 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1044 }
1045 int unionIdx = 1;
1046 if (fClipPlaneIsect) {
1047 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1048 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1050 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1051 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1052 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1053 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1054 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1055 unionIdx = 2;
1056 }
1057 if (fClipPlaneUnion) {
1058 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1059 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1061 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1062 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1063 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1064 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1065 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1066 }
1067 } else {
1068 // filled
1069 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1070 v8->fPos = center;
1071 v8->fColor = color;
1072 v8->fOffset = SkPoint::Make(0, 0);
1073 v8->fOuterRadius = outerRadius;
1074 v8->fInnerRadius = innerRadius;
1075 if (fClipPlane) {
1076 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1077 }
1078 int unionIdx = 1;
1079 if (fClipPlaneIsect) {
1080 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1081 unionIdx = 2;
1082 }
1083 if (fClipPlaneUnion) {
1084 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1085 }
1086 }
1087
1088 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1089 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1090 for (int i = 0; i < primIndexCount; ++i) {
1091 *indices++ = primIndices[i] + currStartVertex;
1092 }
1093
1094 currStartVertex += circle_type_to_vert_count(geom.fStroked);
1095 vertices += circle_type_to_vert_count(geom.fStroked) * vertexStride;
1096 }
1097
1098 GrMesh mesh;
1099 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1100 firstIndex, fVertCount, fIndexCount);
1101 target->draw(gp.get(), mesh);
1102 }
1103
onCombineIfPossible(GrOp * t,const GrCaps & caps)1104 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1105 CircleOp* that = t->cast<CircleOp>();
1106
1107 // can only represent 65535 unique vertices with 16-bit indices
1108 if (fVertCount + that->fVertCount > 65536) {
1109 return false;
1110 }
1111
1112 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1113 that->bounds(), caps)) {
1114 return false;
1115 }
1116
1117 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1118 return false;
1119 }
1120
1121 // Because we've set up the ops that don't use the planes with noop values
1122 // we can just accumulate used planes by later ops.
1123 fClipPlane |= that->fClipPlane;
1124 fClipPlaneIsect |= that->fClipPlaneIsect;
1125 fClipPlaneUnion |= that->fClipPlaneUnion;
1126
1127 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
1128 this->joinBounds(*that);
1129 fVertCount += that->fVertCount;
1130 fIndexCount += that->fIndexCount;
1131 fAllFill = fAllFill && that->fAllFill;
1132 return true;
1133 }
1134
1135 struct Geometry {
1136 GrColor fColor;
1137 SkScalar fInnerRadius;
1138 SkScalar fOuterRadius;
1139 SkScalar fClipPlane[3];
1140 SkScalar fIsectPlane[3];
1141 SkScalar fUnionPlane[3];
1142 SkRect fDevBounds;
1143 bool fStroked;
1144 };
1145
1146 SkSTArray<1, Geometry, true> fGeoData;
1147 SkMatrix fViewMatrixIfUsingLocalCoords;
1148 int fVertCount;
1149 int fIndexCount;
1150 bool fAllFill;
1151 bool fClipPlane;
1152 bool fClipPlaneIsect;
1153 bool fClipPlaneUnion;
1154
1155 typedef GrMeshDrawOp INHERITED;
1156 };
1157
1158 ///////////////////////////////////////////////////////////////////////////////
1159
1160 class EllipseOp : public GrMeshDrawOp {
1161 public:
1162 DEFINE_OP_CLASS_ID
Make(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1163 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
1164 const SkRect& ellipse, const SkStrokeRec& stroke) {
1165 SkASSERT(viewMatrix.rectStaysRect());
1166
1167 // do any matrix crunching before we reset the draw state for device coords
1168 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1169 viewMatrix.mapPoints(¢er, 1);
1170 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1171 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1172 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1173 viewMatrix[SkMatrix::kMSkewY] * ellipseYRadius);
1174 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * ellipseXRadius +
1175 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
1176
1177 // do (potentially) anisotropic mapping of stroke
1178 SkVector scaledStroke;
1179 SkScalar strokeWidth = stroke.getWidth();
1180 scaledStroke.fX = SkScalarAbs(
1181 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1182 scaledStroke.fY = SkScalarAbs(
1183 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
1184
1185 SkStrokeRec::Style style = stroke.getStyle();
1186 bool isStrokeOnly =
1187 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
1188 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1189
1190 SkScalar innerXRadius = 0;
1191 SkScalar innerYRadius = 0;
1192 if (hasStroke) {
1193 if (SkScalarNearlyZero(scaledStroke.length())) {
1194 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1195 } else {
1196 scaledStroke.scale(SK_ScalarHalf);
1197 }
1198
1199 // we only handle thick strokes for near-circular ellipses
1200 if (scaledStroke.length() > SK_ScalarHalf &&
1201 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
1202 return nullptr;
1203 }
1204
1205 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1206 if (scaledStroke.fX * (yRadius * yRadius) <
1207 (scaledStroke.fY * scaledStroke.fY) * xRadius ||
1208 scaledStroke.fY * (xRadius * xRadius) <
1209 (scaledStroke.fX * scaledStroke.fX) * yRadius) {
1210 return nullptr;
1211 }
1212
1213 // this is legit only if scale & translation (which should be the case at the moment)
1214 if (isStrokeOnly) {
1215 innerXRadius = xRadius - scaledStroke.fX;
1216 innerYRadius = yRadius - scaledStroke.fY;
1217 }
1218
1219 xRadius += scaledStroke.fX;
1220 yRadius += scaledStroke.fY;
1221 }
1222
1223 std::unique_ptr<EllipseOp> op(new EllipseOp());
1224 op->fGeoData.emplace_back(
1225 Geometry{color, xRadius, yRadius, innerXRadius, innerYRadius,
1226 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1227 center.fX + xRadius, center.fY + yRadius)});
1228
1229 op->setBounds(op->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
1230
1231 // Outset bounds to include half-pixel width antialiasing.
1232 op->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1233
1234 op->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1235 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
1236 return std::move(op);
1237 }
1238
name() const1239 const char* name() const override { return "EllipseOp"; }
1240
dumpInfo() const1241 SkString dumpInfo() const override {
1242 SkString string;
1243 string.appendf("Stroked: %d\n", fStroked);
1244 for (const auto& geo : fGeoData) {
1245 string.appendf(
1246 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1247 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1248 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1249 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1250 geo.fInnerYRadius);
1251 }
1252 string.append(DumpPipelineInfo(*this->pipeline()));
1253 string.append(INHERITED::dumpInfo());
1254 return string;
1255 }
1256
1257 private:
EllipseOp()1258 EllipseOp() : INHERITED(ClassID()) {}
1259
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const1260 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
1261 GrPipelineAnalysisCoverage* coverage) const override {
1262 color->setToConstant(fGeoData[0].fColor);
1263 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
1264 }
1265
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)1266 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1267 if (!optimizations.readsLocalCoords()) {
1268 fViewMatrixIfUsingLocalCoords.reset();
1269 }
1270 }
1271
onPrepareDraws(Target * target) const1272 void onPrepareDraws(Target* target) const override {
1273 SkMatrix localMatrix;
1274 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1275 return;
1276 }
1277
1278 // Setup geometry processor
1279 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
1280
1281 int instanceCount = fGeoData.count();
1282 QuadHelper helper;
1283 size_t vertexStride = gp->getVertexStride();
1284 SkASSERT(vertexStride == sizeof(EllipseVertex));
1285 EllipseVertex* verts =
1286 reinterpret_cast<EllipseVertex*>(helper.init(target, vertexStride, instanceCount));
1287 if (!verts) {
1288 return;
1289 }
1290
1291 for (int i = 0; i < instanceCount; i++) {
1292 const Geometry& geom = fGeoData[i];
1293
1294 GrColor color = geom.fColor;
1295 SkScalar xRadius = geom.fXRadius;
1296 SkScalar yRadius = geom.fYRadius;
1297
1298 // Compute the reciprocals of the radii here to save time in the shader
1299 SkScalar xRadRecip = SkScalarInvert(xRadius);
1300 SkScalar yRadRecip = SkScalarInvert(yRadius);
1301 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1302 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
1303
1304 const SkRect& bounds = geom.fDevBounds;
1305
1306 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1307 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1308 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1309
1310 // The inner radius in the vertex data must be specified in normalized space.
1311 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1312 verts[0].fColor = color;
1313 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
1314 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1315 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1316
1317 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1318 verts[1].fColor = color;
1319 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
1320 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1321 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1322
1323 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1324 verts[2].fColor = color;
1325 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
1326 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1327 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1328
1329 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1330 verts[3].fColor = color;
1331 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
1332 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1333 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1334
1335 verts += kVerticesPerQuad;
1336 }
1337 helper.recordDraw(target, gp.get());
1338 }
1339
onCombineIfPossible(GrOp * t,const GrCaps & caps)1340 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1341 EllipseOp* that = t->cast<EllipseOp>();
1342
1343 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1344 that->bounds(), caps)) {
1345 return false;
1346 }
1347
1348 if (fStroked != that->fStroked) {
1349 return false;
1350 }
1351
1352 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1353 return false;
1354 }
1355
1356 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
1357 this->joinBounds(*that);
1358 return true;
1359 }
1360
1361 struct Geometry {
1362 GrColor fColor;
1363 SkScalar fXRadius;
1364 SkScalar fYRadius;
1365 SkScalar fInnerXRadius;
1366 SkScalar fInnerYRadius;
1367 SkRect fDevBounds;
1368 };
1369
1370 bool fStroked;
1371 SkMatrix fViewMatrixIfUsingLocalCoords;
1372 SkSTArray<1, Geometry, true> fGeoData;
1373
1374 typedef GrMeshDrawOp INHERITED;
1375 };
1376
1377 /////////////////////////////////////////////////////////////////////////////////////////////////
1378
1379 class DIEllipseOp : public GrMeshDrawOp {
1380 public:
1381 DEFINE_OP_CLASS_ID
1382
Make(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1383 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
1384 const SkMatrix& viewMatrix,
1385 const SkRect& ellipse,
1386 const SkStrokeRec& stroke) {
1387 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1388 SkScalar xRadius = SkScalarHalf(ellipse.width());
1389 SkScalar yRadius = SkScalarHalf(ellipse.height());
1390
1391 SkStrokeRec::Style style = stroke.getStyle();
1392 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style)
1393 ? DIEllipseStyle::kStroke
1394 : (SkStrokeRec::kHairline_Style == style)
1395 ? DIEllipseStyle::kHairline
1396 : DIEllipseStyle::kFill;
1397
1398 SkScalar innerXRadius = 0;
1399 SkScalar innerYRadius = 0;
1400 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1401 SkScalar strokeWidth = stroke.getWidth();
1402
1403 if (SkScalarNearlyZero(strokeWidth)) {
1404 strokeWidth = SK_ScalarHalf;
1405 } else {
1406 strokeWidth *= SK_ScalarHalf;
1407 }
1408
1409 // we only handle thick strokes for near-circular ellipses
1410 if (strokeWidth > SK_ScalarHalf &&
1411 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
1412 return nullptr;
1413 }
1414
1415 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1416 if (strokeWidth * (yRadius * yRadius) < (strokeWidth * strokeWidth) * xRadius ||
1417 strokeWidth * (xRadius * xRadius) < (strokeWidth * strokeWidth) * yRadius) {
1418 return nullptr;
1419 }
1420
1421 // set inner radius (if needed)
1422 if (SkStrokeRec::kStroke_Style == style) {
1423 innerXRadius = xRadius - strokeWidth;
1424 innerYRadius = yRadius - strokeWidth;
1425 }
1426
1427 xRadius += strokeWidth;
1428 yRadius += strokeWidth;
1429 }
1430 if (DIEllipseStyle::kStroke == dieStyle) {
1431 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle::kStroke
1432 : DIEllipseStyle::kFill;
1433 }
1434
1435 // This expands the outer rect so that after CTM we end up with a half-pixel border
1436 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1437 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1438 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1439 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1440 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1441 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
1442
1443 std::unique_ptr<DIEllipseOp> op(new DIEllipseOp());
1444 op->fGeoData.emplace_back(Geometry{
1445 viewMatrix, color, xRadius, yRadius, innerXRadius, innerYRadius, geoDx, geoDy,
1446 dieStyle,
1447 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1448 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)});
1449 op->setTransformedBounds(op->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1450 IsZeroArea::kNo);
1451 return std::move(op);
1452 }
1453
name() const1454 const char* name() const override { return "DIEllipseOp"; }
1455
dumpInfo() const1456 SkString dumpInfo() const override {
1457 SkString string;
1458 for (const auto& geo : fGeoData) {
1459 string.appendf(
1460 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1461 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1462 "GeoDY: %.2f\n",
1463 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1464 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1465 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
1466 }
1467 string.append(DumpPipelineInfo(*this->pipeline()));
1468 string.append(INHERITED::dumpInfo());
1469 return string;
1470 }
1471
1472 private:
DIEllipseOp()1473 DIEllipseOp() : INHERITED(ClassID()) {}
1474
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const1475 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
1476 GrPipelineAnalysisCoverage* coverage) const override {
1477 color->setToConstant(fGeoData[0].fColor);
1478 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
1479 }
1480
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)1481 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1482 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1483 fUsesLocalCoords = optimizations.readsLocalCoords();
1484 }
1485
onPrepareDraws(Target * target) const1486 void onPrepareDraws(Target* target) const override {
1487 // Setup geometry processor
1488 sk_sp<GrGeometryProcessor> gp(
1489 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
1490
1491 int instanceCount = fGeoData.count();
1492 size_t vertexStride = gp->getVertexStride();
1493 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1494 QuadHelper helper;
1495 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1496 helper.init(target, vertexStride, instanceCount));
1497 if (!verts) {
1498 return;
1499 }
1500
1501 for (int i = 0; i < instanceCount; i++) {
1502 const Geometry& geom = fGeoData[i];
1503
1504 GrColor color = geom.fColor;
1505 SkScalar xRadius = geom.fXRadius;
1506 SkScalar yRadius = geom.fYRadius;
1507
1508 const SkRect& bounds = geom.fBounds;
1509
1510 // This adjusts the "radius" to include the half-pixel border
1511 SkScalar offsetDx = geom.fGeoDx / xRadius;
1512 SkScalar offsetDy = geom.fGeoDy / yRadius;
1513
1514 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1515 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
1516
1517 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1518 verts[0].fColor = color;
1519 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1520 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1521
1522 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1523 verts[1].fColor = color;
1524 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1525 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1526
1527 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1528 verts[2].fColor = color;
1529 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1530 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1531
1532 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1533 verts[3].fColor = color;
1534 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1535 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1536
1537 verts += kVerticesPerQuad;
1538 }
1539 helper.recordDraw(target, gp.get());
1540 }
1541
onCombineIfPossible(GrOp * t,const GrCaps & caps)1542 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1543 DIEllipseOp* that = t->cast<DIEllipseOp>();
1544 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1545 that->bounds(), caps)) {
1546 return false;
1547 }
1548
1549 if (this->style() != that->style()) {
1550 return false;
1551 }
1552
1553 // TODO rewrite to allow positioning on CPU
1554 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1555 return false;
1556 }
1557
1558 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
1559 this->joinBounds(*that);
1560 return true;
1561 }
1562
viewMatrix() const1563 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
style() const1564 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
1565
1566 struct Geometry {
1567 SkMatrix fViewMatrix;
1568 GrColor fColor;
1569 SkScalar fXRadius;
1570 SkScalar fYRadius;
1571 SkScalar fInnerXRadius;
1572 SkScalar fInnerYRadius;
1573 SkScalar fGeoDx;
1574 SkScalar fGeoDy;
1575 DIEllipseStyle fStyle;
1576 SkRect fBounds;
1577 };
1578
1579 bool fUsesLocalCoords;
1580 SkSTArray<1, Geometry, true> fGeoData;
1581
1582 typedef GrMeshDrawOp INHERITED;
1583 };
1584
1585 ///////////////////////////////////////////////////////////////////////////////
1586
1587 // We have three possible cases for geometry for a roundrect.
1588 //
1589 // In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1590 // ____________
1591 // |_|________|_|
1592 // | | | |
1593 // | | | |
1594 // | | | |
1595 // |_|________|_|
1596 // |_|________|_|
1597 //
1598 // For strokes, we don't draw the center quad.
1599 //
1600 // For circular roundrects, in the case where the stroke width is greater than twice
1601 // the corner radius (overstroke), we add additional geometry to mark out the rectangle
1602 // in the center. The shared vertices are duplicated so we can set a different outer radius
1603 // for the fill calculation.
1604 // ____________
1605 // |_|________|_|
1606 // | |\ ____ /| |
1607 // | | | | | |
1608 // | | |____| | |
1609 // |_|/______\|_|
1610 // |_|________|_|
1611 //
1612 // We don't draw the center quad from the fill rect in this case.
1613 //
1614 // For filled rrects that need to provide a distance vector we resuse the overstroke
1615 // geometry but make the inner rect degenerate (either a point or a horizontal or
1616 // vertical line).
1617
1618 static const uint16_t gOverstrokeRRectIndices[] = {
1619 // clang-format off
1620 // overstroke quads
1621 // we place this at the beginning so that we can skip these indices when rendering normally
1622 16, 17, 19, 16, 19, 18,
1623 19, 17, 23, 19, 23, 21,
1624 21, 23, 22, 21, 22, 20,
1625 22, 16, 18, 22, 18, 20,
1626
1627 // corners
1628 0, 1, 5, 0, 5, 4,
1629 2, 3, 7, 2, 7, 6,
1630 8, 9, 13, 8, 13, 12,
1631 10, 11, 15, 10, 15, 14,
1632
1633 // edges
1634 1, 2, 6, 1, 6, 5,
1635 4, 5, 9, 4, 9, 8,
1636 6, 7, 11, 6, 11, 10,
1637 9, 10, 14, 9, 14, 13,
1638
1639 // center
1640 // we place this at the end so that we can ignore these indices when not rendering as filled
1641 5, 6, 10, 5, 10, 9,
1642 // clang-format on
1643 };
1644
1645 // fill and standard stroke indices skip the overstroke "ring"
1646 static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
1647
1648 // overstroke count is arraysize minus the center indices
1649 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1650 // fill count skips overstroke indices and includes center
1651 static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
1652 // stroke count is fill count minus center indices
1653 static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1654 static const int kVertsPerStandardRRect = 16;
1655 static const int kVertsPerOverstrokeRRect = 24;
1656
1657 enum RRectType {
1658 kFill_RRectType,
1659 kStroke_RRectType,
1660 kOverstroke_RRectType,
1661 kFillWithDist_RRectType
1662 };
1663
rrect_type_to_vert_count(RRectType type)1664 static int rrect_type_to_vert_count(RRectType type) {
1665 switch (type) {
1666 case kFill_RRectType:
1667 case kStroke_RRectType:
1668 return kVertsPerStandardRRect;
1669 case kOverstroke_RRectType:
1670 case kFillWithDist_RRectType:
1671 return kVertsPerOverstrokeRRect;
1672 }
1673 SkFAIL("Invalid type");
1674 return 0;
1675 }
1676
rrect_type_to_index_count(RRectType type)1677 static int rrect_type_to_index_count(RRectType type) {
1678 switch (type) {
1679 case kFill_RRectType:
1680 return kIndicesPerFillRRect;
1681 case kStroke_RRectType:
1682 return kIndicesPerStrokeRRect;
1683 case kOverstroke_RRectType:
1684 case kFillWithDist_RRectType:
1685 return kIndicesPerOverstrokeRRect;
1686 }
1687 SkFAIL("Invalid type");
1688 return 0;
1689 }
1690
rrect_type_to_indices(RRectType type)1691 static const uint16_t* rrect_type_to_indices(RRectType type) {
1692 switch (type) {
1693 case kFill_RRectType:
1694 case kStroke_RRectType:
1695 return gStandardRRectIndices;
1696 case kOverstroke_RRectType:
1697 case kFillWithDist_RRectType:
1698 return gOverstrokeRRectIndices;
1699 }
1700 SkFAIL("Invalid type");
1701 return 0;
1702 }
1703
1704 ///////////////////////////////////////////////////////////////////////////////////////////////////
1705
1706 // For distance computations in the interior of filled rrects we:
1707 //
1708 // add a interior degenerate (point or line) rect
1709 // each vertex of that rect gets -outerRad as its radius
1710 // this makes the computation of the distance to the outer edge be negative
1711 // negative values are caught and then handled differently in the GP's onEmitCode
1712 // each vertex is also given the normalized x & y distance from the interior rect's edge
1713 // the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1714
1715 class CircularRRectOp : public GrMeshDrawOp {
1716 public:
1717 DEFINE_OP_CLASS_ID
1718
1719 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1720 // whether the rrect is only stroked or stroked and filled.
CircularRRectOp(GrColor color,bool needsDistance,const SkMatrix & viewMatrix,const SkRect & devRect,float devRadius,float devStrokeWidth,bool strokeOnly)1721 CircularRRectOp(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1722 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
1723 : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
1724 SkRect bounds = devRect;
1725 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1726 SkScalar innerRadius = 0.0f;
1727 SkScalar outerRadius = devRadius;
1728 SkScalar halfWidth = 0;
1729 RRectType type = kFill_RRectType;
1730 if (devStrokeWidth > 0) {
1731 if (SkScalarNearlyZero(devStrokeWidth)) {
1732 halfWidth = SK_ScalarHalf;
1733 } else {
1734 halfWidth = SkScalarHalf(devStrokeWidth);
1735 }
1736
1737 if (strokeOnly) {
1738 // Outset stroke by 1/4 pixel
1739 devStrokeWidth += 0.25f;
1740 // If stroke is greater than width or height, this is still a fill
1741 // Otherwise we compute stroke params
1742 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
1743 innerRadius = devRadius - halfWidth;
1744 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
1745 }
1746 }
1747 outerRadius += halfWidth;
1748 bounds.outset(halfWidth, halfWidth);
1749 }
1750 if (kFill_RRectType == type && needsDistance) {
1751 type = kFillWithDist_RRectType;
1752 }
1753
1754 // The radii are outset for two reasons. First, it allows the shader to simply perform
1755 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1756 // Second, the outer radius is used to compute the verts of the bounding box that is
1757 // rendered and the outset ensures the box will cover all partially covered by the rrect
1758 // corners.
1759 outerRadius += SK_ScalarHalf;
1760 innerRadius -= SK_ScalarHalf;
1761
1762 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1763
1764 // Expand the rect for aa to generate correct vertices.
1765 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1766
1767 fGeoData.emplace_back(Geometry{color, innerRadius, outerRadius, bounds, type});
1768 fVertCount = rrect_type_to_vert_count(type);
1769 fIndexCount = rrect_type_to_index_count(type);
1770 fAllFill = (kFill_RRectType == type);
1771 }
1772
name() const1773 const char* name() const override { return "CircularRRectOp"; }
1774
dumpInfo() const1775 SkString dumpInfo() const override {
1776 SkString string;
1777 for (int i = 0; i < fGeoData.count(); ++i) {
1778 string.appendf(
1779 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1780 "InnerRad: %.2f, OuterRad: %.2f\n",
1781 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1782 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1783 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
1784 }
1785 string.append(DumpPipelineInfo(*this->pipeline()));
1786 string.append(INHERITED::dumpInfo());
1787 return string;
1788 }
1789
1790 private:
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const1791 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
1792 GrPipelineAnalysisCoverage* coverage) const override {
1793 color->setToConstant(fGeoData[0].fColor);
1794 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
1795 }
1796
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)1797 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1798 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1799 if (!optimizations.readsLocalCoords()) {
1800 fViewMatrixIfUsingLocalCoords.reset();
1801 }
1802 }
1803
1804 struct CircleVertex {
1805 SkPoint fPos;
1806 GrColor fColor;
1807 SkPoint fOffset;
1808 SkScalar fOuterRadius;
1809 SkScalar fInnerRadius;
1810 // No half plane, we don't use it here.
1811 };
1812
FillInOverstrokeVerts(CircleVertex ** verts,const SkRect & bounds,SkScalar smInset,SkScalar bigInset,SkScalar xOffset,SkScalar outerRadius,SkScalar innerRadius,GrColor color)1813 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1814 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1815 SkScalar innerRadius, GrColor color) {
1816 SkASSERT(smInset < bigInset);
1817
1818 // TL
1819 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1820 (*verts)->fColor = color;
1821 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1822 (*verts)->fOuterRadius = outerRadius;
1823 (*verts)->fInnerRadius = innerRadius;
1824 (*verts)++;
1825
1826 // TR
1827 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
1828 (*verts)->fColor = color;
1829 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1830 (*verts)->fOuterRadius = outerRadius;
1831 (*verts)->fInnerRadius = innerRadius;
1832 (*verts)++;
1833
1834 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
1835 (*verts)->fColor = color;
1836 (*verts)->fOffset = SkPoint::Make(0, 0);
1837 (*verts)->fOuterRadius = outerRadius;
1838 (*verts)->fInnerRadius = innerRadius;
1839 (*verts)++;
1840
1841 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
1842 (*verts)->fColor = color;
1843 (*verts)->fOffset = SkPoint::Make(0, 0);
1844 (*verts)->fOuterRadius = outerRadius;
1845 (*verts)->fInnerRadius = innerRadius;
1846 (*verts)++;
1847
1848 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1849 (*verts)->fColor = color;
1850 (*verts)->fOffset = SkPoint::Make(0, 0);
1851 (*verts)->fOuterRadius = outerRadius;
1852 (*verts)->fInnerRadius = innerRadius;
1853 (*verts)++;
1854
1855 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1856 (*verts)->fColor = color;
1857 (*verts)->fOffset = SkPoint::Make(0, 0);
1858 (*verts)->fOuterRadius = outerRadius;
1859 (*verts)->fInnerRadius = innerRadius;
1860 (*verts)++;
1861
1862 // BL
1863 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1864 (*verts)->fColor = color;
1865 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1866 (*verts)->fOuterRadius = outerRadius;
1867 (*verts)->fInnerRadius = innerRadius;
1868 (*verts)++;
1869
1870 // BR
1871 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1872 (*verts)->fColor = color;
1873 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1874 (*verts)->fOuterRadius = outerRadius;
1875 (*verts)->fInnerRadius = innerRadius;
1876 (*verts)++;
1877 }
1878
onPrepareDraws(Target * target) const1879 void onPrepareDraws(Target* target) const override {
1880 // Invert the view matrix as a local matrix (if any other processors require coords).
1881 SkMatrix localMatrix;
1882 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1883 return;
1884 }
1885
1886 // Setup geometry processor
1887 sk_sp<GrGeometryProcessor> gp(
1888 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
1889
1890 int instanceCount = fGeoData.count();
1891 size_t vertexStride = gp->getVertexStride();
1892 SkASSERT(sizeof(CircleVertex) == vertexStride);
1893
1894 const GrBuffer* vertexBuffer;
1895 int firstVertex;
1896
1897 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1898 &vertexBuffer, &firstVertex);
1899 if (!verts) {
1900 SkDebugf("Could not allocate vertices\n");
1901 return;
1902 }
1903
1904 const GrBuffer* indexBuffer = nullptr;
1905 int firstIndex = 0;
1906 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1907 if (!indices) {
1908 SkDebugf("Could not allocate indices\n");
1909 return;
1910 }
1911
1912 int currStartVertex = 0;
1913 for (int i = 0; i < instanceCount; i++) {
1914 const Geometry& args = fGeoData[i];
1915
1916 GrColor color = args.fColor;
1917 SkScalar outerRadius = args.fOuterRadius;
1918
1919 const SkRect& bounds = args.fDevBounds;
1920
1921 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1922 bounds.fBottom - outerRadius, bounds.fBottom};
1923
1924 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
1925 // The inner radius in the vertex data must be specified in normalized space.
1926 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
1927 SkScalar innerRadius =
1928 args.fType != kFill_RRectType && args.fType != kFillWithDist_RRectType
1929 ? args.fInnerRadius / args.fOuterRadius
1930 : -1.0f / args.fOuterRadius;
1931 for (int i = 0; i < 4; ++i) {
1932 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1933 verts->fColor = color;
1934 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1935 verts->fOuterRadius = outerRadius;
1936 verts->fInnerRadius = innerRadius;
1937 verts++;
1938
1939 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1940 verts->fColor = color;
1941 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1942 verts->fOuterRadius = outerRadius;
1943 verts->fInnerRadius = innerRadius;
1944 verts++;
1945
1946 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1947 verts->fColor = color;
1948 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1949 verts->fOuterRadius = outerRadius;
1950 verts->fInnerRadius = innerRadius;
1951 verts++;
1952
1953 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1954 verts->fColor = color;
1955 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1956 verts->fOuterRadius = outerRadius;
1957 verts->fInnerRadius = innerRadius;
1958 verts++;
1959 }
1960 // Add the additional vertices for overstroked rrects.
1961 // Effectively this is an additional stroked rrect, with its
1962 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1963 // This will give us correct AA in the center and the correct
1964 // distance to the outer edge.
1965 //
1966 // Also, the outer offset is a constant vector pointing to the right, which
1967 // guarantees that the distance value along the outer rectangle is constant.
1968 if (kOverstroke_RRectType == args.fType) {
1969 SkASSERT(args.fInnerRadius <= 0.0f);
1970
1971 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1972 // this is the normalized distance from the outer rectangle of this
1973 // geometry to the outer edge
1974 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
1975
1976 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
1977 overstrokeOuterRadius, 0.0f, color);
1978 }
1979
1980 if (kFillWithDist_RRectType == args.fType) {
1981 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
1982
1983 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
1984
1985 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
1986 -1.0f, color);
1987 }
1988
1989 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1990 const int primIndexCount = rrect_type_to_index_count(args.fType);
1991 for (int i = 0; i < primIndexCount; ++i) {
1992 *indices++ = primIndices[i] + currStartVertex;
1993 }
1994
1995 currStartVertex += rrect_type_to_vert_count(args.fType);
1996 }
1997
1998 GrMesh mesh;
1999 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
2000 firstIndex, fVertCount, fIndexCount);
2001 target->draw(gp.get(), mesh);
2002 }
2003
onCombineIfPossible(GrOp * t,const GrCaps & caps)2004 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
2005 CircularRRectOp* that = t->cast<CircularRRectOp>();
2006
2007 // can only represent 65535 unique vertices with 16-bit indices
2008 if (fVertCount + that->fVertCount > 65536) {
2009 return false;
2010 }
2011
2012 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2013 that->bounds(), caps)) {
2014 return false;
2015 }
2016
2017 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
2018 return false;
2019 }
2020
2021 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
2022 this->joinBounds(*that);
2023 fVertCount += that->fVertCount;
2024 fIndexCount += that->fIndexCount;
2025 fAllFill = fAllFill && that->fAllFill;
2026 return true;
2027 }
2028
2029 struct Geometry {
2030 GrColor fColor;
2031 SkScalar fInnerRadius;
2032 SkScalar fOuterRadius;
2033 SkRect fDevBounds;
2034 RRectType fType;
2035 };
2036
2037 SkSTArray<1, Geometry, true> fGeoData;
2038 SkMatrix fViewMatrixIfUsingLocalCoords;
2039 int fVertCount;
2040 int fIndexCount;
2041 bool fAllFill;
2042
2043 typedef GrMeshDrawOp INHERITED;
2044 };
2045
2046 static const int kNumRRectsInIndexBuffer = 256;
2047
2048 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2049 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
ref_rrect_index_buffer(RRectType type,GrResourceProvider * resourceProvider)2050 static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2051 GrResourceProvider* resourceProvider) {
2052 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2053 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2054 switch (type) {
2055 case kFill_RRectType:
2056 return resourceProvider->findOrCreateInstancedIndexBuffer(
2057 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2058 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
2059 case kStroke_RRectType:
2060 return resourceProvider->findOrCreateInstancedIndexBuffer(
2061 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2062 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
2063 default:
2064 SkASSERT(false);
2065 return nullptr;
2066 };
2067 }
2068
2069 class EllipticalRRectOp : public GrMeshDrawOp {
2070 public:
2071 DEFINE_OP_CLASS_ID
2072
2073 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2074 // whether the rrect is only stroked or stroked and filled.
Make(GrColor color,const SkMatrix & viewMatrix,const SkRect & devRect,float devXRadius,float devYRadius,SkVector devStrokeWidths,bool strokeOnly)2075 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
2076 const SkRect& devRect, float devXRadius,
2077 float devYRadius, SkVector devStrokeWidths,
2078 bool strokeOnly) {
2079 SkASSERT(devXRadius > 0.5);
2080 SkASSERT(devYRadius > 0.5);
2081 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2082 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2083 SkScalar innerXRadius = 0.0f;
2084 SkScalar innerYRadius = 0.0f;
2085 SkRect bounds = devRect;
2086 bool stroked = false;
2087 if (devStrokeWidths.fX > 0) {
2088 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2089 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2090 } else {
2091 devStrokeWidths.scale(SK_ScalarHalf);
2092 }
2093
2094 // we only handle thick strokes for near-circular ellipses
2095 if (devStrokeWidths.length() > SK_ScalarHalf &&
2096 (SK_ScalarHalf * devXRadius > devYRadius ||
2097 SK_ScalarHalf * devYRadius > devXRadius)) {
2098 return nullptr;
2099 }
2100
2101 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2102 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2103 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
2104 return nullptr;
2105 }
2106 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2107 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
2108 return nullptr;
2109 }
2110
2111 // this is legit only if scale & translation (which should be the case at the moment)
2112 if (strokeOnly) {
2113 innerXRadius = devXRadius - devStrokeWidths.fX;
2114 innerYRadius = devYRadius - devStrokeWidths.fY;
2115 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2116 }
2117
2118 devXRadius += devStrokeWidths.fX;
2119 devYRadius += devStrokeWidths.fY;
2120 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2121 }
2122
2123 std::unique_ptr<EllipticalRRectOp> op(new EllipticalRRectOp());
2124 op->fStroked = stroked;
2125 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
2126 op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2127 // Expand the rect for aa in order to generate the correct vertices.
2128 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2129 op->fGeoData.emplace_back(
2130 Geometry{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2131 return std::move(op);
2132 }
2133
name() const2134 const char* name() const override { return "EllipticalRRectOp"; }
2135
dumpInfo() const2136 SkString dumpInfo() const override {
2137 SkString string;
2138 string.appendf("Stroked: %d\n", fStroked);
2139 for (const auto& geo : fGeoData) {
2140 string.appendf(
2141 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2142 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2143 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2144 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2145 geo.fInnerYRadius);
2146 }
2147 string.append(DumpPipelineInfo(*this->pipeline()));
2148 string.append(INHERITED::dumpInfo());
2149 return string;
2150 }
2151
2152 private:
EllipticalRRectOp()2153 EllipticalRRectOp() : INHERITED(ClassID()) {}
2154
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const2155 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
2156 GrPipelineAnalysisCoverage* coverage) const override {
2157 color->setToConstant(fGeoData[0].fColor);
2158 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
2159 }
2160
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)2161 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
2162 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
2163 if (!optimizations.readsLocalCoords()) {
2164 fViewMatrixIfUsingLocalCoords.reset();
2165 }
2166 }
2167
onPrepareDraws(Target * target) const2168 void onPrepareDraws(Target* target) const override {
2169 SkMatrix localMatrix;
2170 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
2171 return;
2172 }
2173
2174 // Setup geometry processor
2175 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
2176
2177 int instanceCount = fGeoData.count();
2178 size_t vertexStride = gp->getVertexStride();
2179 SkASSERT(vertexStride == sizeof(EllipseVertex));
2180
2181 // drop out the middle quad if we're stroked
2182 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
2183 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2184 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
2185
2186 InstancedHelper helper;
2187 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
2188 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
2189 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
2190 if (!verts || !indexBuffer) {
2191 SkDebugf("Could not allocate vertices\n");
2192 return;
2193 }
2194
2195 for (int i = 0; i < instanceCount; i++) {
2196 const Geometry& args = fGeoData[i];
2197
2198 GrColor color = args.fColor;
2199
2200 // Compute the reciprocals of the radii here to save time in the shader
2201 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2202 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2203 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2204 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2205
2206 // Extend the radii out half a pixel to antialias.
2207 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2208 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2209
2210 const SkRect& bounds = args.fDevBounds;
2211
2212 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2213 bounds.fBottom - yOuterRadius, bounds.fBottom};
2214 SkScalar yOuterOffsets[4] = {yOuterRadius,
2215 SK_ScalarNearlyZero, // we're using inversesqrt() in
2216 // shader, so can't be exactly 0
2217 SK_ScalarNearlyZero, yOuterRadius};
2218
2219 for (int i = 0; i < 4; ++i) {
2220 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
2221 verts->fColor = color;
2222 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2223 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2224 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2225 verts++;
2226
2227 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
2228 verts->fColor = color;
2229 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2230 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2231 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2232 verts++;
2233
2234 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
2235 verts->fColor = color;
2236 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2237 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2238 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2239 verts++;
2240
2241 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
2242 verts->fColor = color;
2243 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2244 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2245 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2246 verts++;
2247 }
2248 }
2249 helper.recordDraw(target, gp.get());
2250 }
2251
onCombineIfPossible(GrOp * t,const GrCaps & caps)2252 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
2253 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
2254
2255 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2256 that->bounds(), caps)) {
2257 return false;
2258 }
2259
2260 if (fStroked != that->fStroked) {
2261 return false;
2262 }
2263
2264 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
2265 return false;
2266 }
2267
2268 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
2269 this->joinBounds(*that);
2270 return true;
2271 }
2272
2273 struct Geometry {
2274 GrColor fColor;
2275 SkScalar fXRadius;
2276 SkScalar fYRadius;
2277 SkScalar fInnerXRadius;
2278 SkScalar fInnerYRadius;
2279 SkRect fDevBounds;
2280 };
2281
2282 bool fStroked;
2283 SkMatrix fViewMatrixIfUsingLocalCoords;
2284 SkSTArray<1, Geometry, true> fGeoData;
2285
2286 typedef GrMeshDrawOp INHERITED;
2287 };
2288
make_rrect_op(GrColor color,bool needsDistance,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkStrokeRec & stroke)2289 static std::unique_ptr<GrMeshDrawOp> make_rrect_op(GrColor color,
2290 bool needsDistance,
2291 const SkMatrix& viewMatrix,
2292 const SkRRect& rrect,
2293 const SkStrokeRec& stroke) {
2294 SkASSERT(viewMatrix.rectStaysRect());
2295 SkASSERT(rrect.isSimple());
2296 SkASSERT(!rrect.isOval());
2297
2298 // RRect ops only handle simple, but not too simple, rrects.
2299 // Do any matrix crunching before we reset the draw state for device coords.
2300 const SkRect& rrectBounds = rrect.getBounds();
2301 SkRect bounds;
2302 viewMatrix.mapRect(&bounds, rrectBounds);
2303
2304 SkVector radii = rrect.getSimpleRadii();
2305 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2306 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2307 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2308 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
2309
2310 SkStrokeRec::Style style = stroke.getStyle();
2311
2312 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2313 SkVector scaledStroke = {-1, -1};
2314 SkScalar strokeWidth = stroke.getWidth();
2315
2316 bool isStrokeOnly =
2317 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
2318 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2319
2320 bool isCircular = (xRadius == yRadius);
2321 if (hasStroke) {
2322 if (SkStrokeRec::kHairline_Style == style) {
2323 scaledStroke.set(1, 1);
2324 } else {
2325 scaledStroke.fX = SkScalarAbs(
2326 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2327 scaledStroke.fY = SkScalarAbs(
2328 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
2329 }
2330
2331 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2332 // for non-circular rrects, if half of strokewidth is greater than radius,
2333 // we don't handle that right now
2334 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2335 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
2336 return nullptr;
2337 }
2338 }
2339
2340 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2341 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2342 // patch will have fractional coverage. This only matters when the interior is actually filled.
2343 // We could consider falling back to rect rendering here, since a tiny radius is
2344 // indistinguishable from a square corner.
2345 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
2346 return nullptr;
2347 }
2348
2349 // if the corners are circles, use the circle renderer
2350 if (isCircular) {
2351 return std::unique_ptr<GrMeshDrawOp>(new CircularRRectOp(
2352 color, needsDistance, viewMatrix, bounds, xRadius, scaledStroke.fX, isStrokeOnly));
2353 // otherwise we use the ellipse renderer
2354 } else {
2355 return EllipticalRRectOp::Make(color, viewMatrix, bounds, xRadius, yRadius, scaledStroke,
2356 isStrokeOnly);
2357 }
2358 }
2359
MakeRRectOp(GrColor color,bool needsDistance,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkStrokeRec & stroke,const GrShaderCaps * shaderCaps)2360 std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeRRectOp(GrColor color,
2361 bool needsDistance,
2362 const SkMatrix& viewMatrix,
2363 const SkRRect& rrect,
2364 const SkStrokeRec& stroke,
2365 const GrShaderCaps* shaderCaps) {
2366 if (rrect.isOval()) {
2367 return MakeOvalOp(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
2368 }
2369
2370 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2371 return nullptr;
2372 }
2373
2374 return make_rrect_op(color, needsDistance, viewMatrix, rrect, stroke);
2375 }
2376
2377 ///////////////////////////////////////////////////////////////////////////////
2378
MakeOvalOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & oval,const SkStrokeRec & stroke,const GrShaderCaps * shaderCaps)2379 std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeOvalOp(GrColor color,
2380 const SkMatrix& viewMatrix,
2381 const SkRect& oval,
2382 const SkStrokeRec& stroke,
2383 const GrShaderCaps* shaderCaps) {
2384 // we can draw circles
2385 SkScalar width = oval.width();
2386 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2387 SkPoint center = {oval.centerX(), oval.centerY()};
2388 return CircleOp::Make(color, viewMatrix, center, width / 2.f, GrStyle(stroke, nullptr));
2389 }
2390
2391 // prefer the device space ellipse op for batchability
2392 if (viewMatrix.rectStaysRect()) {
2393 return EllipseOp::Make(color, viewMatrix, oval, stroke);
2394 }
2395
2396 // Otherwise, if we have shader derivative support, render as device-independent
2397 if (shaderCaps->shaderDerivativeSupport()) {
2398 return DIEllipseOp::Make(color, viewMatrix, oval, stroke);
2399 }
2400
2401 return nullptr;
2402 }
2403
2404 ///////////////////////////////////////////////////////////////////////////////
2405
MakeArcOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,const GrStyle & style,const GrShaderCaps * shaderCaps)2406 std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeArcOp(GrColor color, const SkMatrix& viewMatrix,
2407 const SkRect& oval, SkScalar startAngle,
2408 SkScalar sweepAngle, bool useCenter,
2409 const GrStyle& style,
2410 const GrShaderCaps* shaderCaps) {
2411 SkASSERT(!oval.isEmpty());
2412 SkASSERT(sweepAngle);
2413 SkScalar width = oval.width();
2414 if (SkScalarAbs(sweepAngle) >= 360.f) {
2415 return nullptr;
2416 }
2417 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2418 return nullptr;
2419 }
2420 SkPoint center = {oval.centerX(), oval.centerY()};
2421 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2422 useCenter};
2423 return CircleOp::Make(color, viewMatrix, center, width / 2.f, style, &arcParams);
2424 }
2425
2426 ///////////////////////////////////////////////////////////////////////////////
2427
2428 #if GR_TEST_UTILS
2429
DRAW_OP_TEST_DEFINE(CircleOp)2430 DRAW_OP_TEST_DEFINE(CircleOp) {
2431 do {
2432 SkScalar rotate = random->nextSScalar1() * 360.f;
2433 SkScalar translateX = random->nextSScalar1() * 1000.f;
2434 SkScalar translateY = random->nextSScalar1() * 1000.f;
2435 SkScalar scale = random->nextSScalar1() * 100.f;
2436 SkMatrix viewMatrix;
2437 viewMatrix.setRotate(rotate);
2438 viewMatrix.postTranslate(translateX, translateY);
2439 viewMatrix.postScale(scale, scale);
2440 GrColor color = GrRandomColor(random);
2441 SkRect circle = GrTest::TestSquare(random);
2442 SkPoint center = {circle.centerX(), circle.centerY()};
2443 SkScalar radius = circle.width() / 2.f;
2444 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2445 CircleOp::ArcParams arcParamsTmp;
2446 const CircleOp::ArcParams* arcParams = nullptr;
2447 if (random->nextBool()) {
2448 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
2449 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2450 arcParamsTmp.fUseCenter = random->nextBool();
2451 arcParams = &arcParamsTmp;
2452 }
2453 std::unique_ptr<GrMeshDrawOp> op = CircleOp::Make(color, viewMatrix, center, radius,
2454 GrStyle(stroke, nullptr), arcParams);
2455 if (op) {
2456 return op;
2457 }
2458 } while (true);
2459 }
2460
DRAW_OP_TEST_DEFINE(EllipseOp)2461 DRAW_OP_TEST_DEFINE(EllipseOp) {
2462 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2463 GrColor color = GrRandomColor(random);
2464 SkRect ellipse = GrTest::TestSquare(random);
2465 return EllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
2466 }
2467
DRAW_OP_TEST_DEFINE(DIEllipseOp)2468 DRAW_OP_TEST_DEFINE(DIEllipseOp) {
2469 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2470 GrColor color = GrRandomColor(random);
2471 SkRect ellipse = GrTest::TestSquare(random);
2472 return DIEllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
2473 }
2474
DRAW_OP_TEST_DEFINE(RRectOp)2475 DRAW_OP_TEST_DEFINE(RRectOp) {
2476 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2477 GrColor color = GrRandomColor(random);
2478 const SkRRect& rrect = GrTest::TestRRectSimple(random);
2479 bool needsDistance = random->nextBool();
2480 return make_rrect_op(color, needsDistance, viewMatrix, rrect, GrTest::TestStrokeRec(random));
2481 }
2482
2483 #endif
2484