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