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