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 "GrOvalRenderer.h"
9
10 #include "GrBatchFlushState.h"
11 #include "GrBatchTest.h"
12 #include "GrGeometryProcessor.h"
13 #include "GrInvariantOutput.h"
14 #include "GrProcessor.h"
15 #include "GrResourceProvider.h"
16 #include "SkRRect.h"
17 #include "SkStrokeRec.h"
18 #include "batches/GrVertexBatch.h"
19 #include "glsl/GrGLSLFragmentShaderBuilder.h"
20 #include "glsl/GrGLSLGeometryProcessor.h"
21 #include "glsl/GrGLSLProgramDataManager.h"
22 #include "glsl/GrGLSLVarying.h"
23 #include "glsl/GrGLSLVertexShaderBuilder.h"
24 #include "glsl/GrGLSLUniformHandler.h"
25 #include "glsl/GrGLSLUtil.h"
26
27 // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
28
29 namespace {
30
31 struct CircleVertex {
32 SkPoint fPos;
33 GrColor fColor;
34 SkPoint fOffset;
35 SkScalar fOuterRadius;
36 SkScalar fInnerRadius;
37 };
38
39 struct EllipseVertex {
40 SkPoint fPos;
41 GrColor fColor;
42 SkPoint fOffset;
43 SkPoint fOuterRadii;
44 SkPoint fInnerRadii;
45 };
46
47 struct DIEllipseVertex {
48 SkPoint fPos;
49 GrColor fColor;
50 SkPoint fOuterOffset;
51 SkPoint fInnerOffset;
52 };
53
circle_stays_circle(const SkMatrix & m)54 inline bool circle_stays_circle(const SkMatrix& m) {
55 return m.isSimilarity();
56 }
57
58 }
59
60 ///////////////////////////////////////////////////////////////////////////////
61
62 /**
63 * The output of this effect is a modulation of the input color and coverage for a circle. It
64 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
65 * with origin at the circle center. Two vertex attributes are used:
66 * vec2f : position in device space of the bounding geometry vertices
67 * vec4f : (p.xy, outerRad, innerRad)
68 * p is the position in the normalized space.
69 * outerRad is the outerRadius in device space.
70 * innerRad is the innerRadius in normalized space (ignored if not stroking).
71 */
72
73 class CircleEdgeEffect : public GrGeometryProcessor {
74 public:
Create(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)75 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
76 bool usesLocalCoords) {
77 return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
78 }
79
inPosition() const80 const Attribute* inPosition() const { return fInPosition; }
inColor() const81 const Attribute* inColor() const { return fInColor; }
inCircleEdge() const82 const Attribute* inCircleEdge() const { return fInCircleEdge; }
color() const83 GrColor color() const { return fColor; }
colorIgnored() const84 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
localMatrix() const85 const SkMatrix& localMatrix() const { return fLocalMatrix; }
usesLocalCoords() const86 bool usesLocalCoords() const { return fUsesLocalCoords; }
~CircleEdgeEffect()87 virtual ~CircleEdgeEffect() {}
88
name() const89 const char* name() const override { return "CircleEdge"; }
90
isStroked() const91 inline bool isStroked() const { return fStroke; }
92
93 class GLSLProcessor : public GrGLSLGeometryProcessor {
94 public:
GLSLProcessor()95 GLSLProcessor() {}
96
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)97 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
98 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
99 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
100 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
101 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
102
103 // emit attributes
104 varyingHandler->emitAttributes(ce);
105
106 GrGLSLVertToFrag v(kVec4f_GrSLType);
107 varyingHandler->addVarying("CircleEdge", &v);
108 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
109
110 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
111 // setup pass through color
112 if (!ce.colorIgnored()) {
113 varyingHandler->addPassThroughAttribute(ce.inColor(), args.fOutputColor);
114 }
115
116 // Setup position
117 this->setupPosition(vertBuilder, gpArgs, ce.inPosition()->fName);
118
119 // emit transforms
120 this->emitTransforms(vertBuilder,
121 varyingHandler,
122 uniformHandler,
123 gpArgs->fPositionVar,
124 ce.inPosition()->fName,
125 ce.localMatrix(),
126 args.fTransformsIn,
127 args.fTransformsOut);
128
129 fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
130 fragBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);",
131 v.fsIn());
132 if (ce.isStroked()) {
133 fragBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
134 v.fsIn(), v.fsIn());
135 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
136 }
137
138 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
139 }
140
GenKey(const GrGeometryProcessor & gp,const GrGLSLCaps &,GrProcessorKeyBuilder * b)141 static void GenKey(const GrGeometryProcessor& gp,
142 const GrGLSLCaps&,
143 GrProcessorKeyBuilder* b) {
144 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
145 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
146 key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
147 key |= ce.colorIgnored() ? 0x4 : 0x0;
148 b->add32(key);
149 }
150
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp)151 void setData(const GrGLSLProgramDataManager& pdman,
152 const GrPrimitiveProcessor& gp) override {
153 }
154
setTransformData(const GrPrimitiveProcessor & primProc,const GrGLSLProgramDataManager & pdman,int index,const SkTArray<const GrCoordTransform *,true> & transforms)155 void setTransformData(const GrPrimitiveProcessor& primProc,
156 const GrGLSLProgramDataManager& pdman,
157 int index,
158 const SkTArray<const GrCoordTransform*, true>& transforms) override {
159 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
160 }
161
162 private:
163 typedef GrGLSLGeometryProcessor INHERITED;
164 };
165
getGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const166 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
167 GLSLProcessor::GenKey(*this, caps, b);
168 }
169
createGLSLInstance(const GrGLSLCaps &) const170 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
171 return new GLSLProcessor();
172 }
173
174 private:
CircleEdgeEffect(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)175 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
176 : fColor(color)
177 , fLocalMatrix(localMatrix)
178 , fUsesLocalCoords(usesLocalCoords) {
179 this->initClassID<CircleEdgeEffect>();
180 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
181 kHigh_GrSLPrecision));
182 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
183 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
184 kVec4f_GrVertexAttribType));
185 fStroke = stroke;
186 }
187
188 GrColor fColor;
189 SkMatrix fLocalMatrix;
190 const Attribute* fInPosition;
191 const Attribute* fInColor;
192 const Attribute* fInCircleEdge;
193 bool fStroke;
194 bool fUsesLocalCoords;
195
196 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
197
198 typedef GrGeometryProcessor INHERITED;
199 };
200
201 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
202
TestCreate(GrProcessorTestData * d)203 const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
204 return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
205 d->fRandom->nextBool(),
206 GrTest::TestMatrix(d->fRandom),
207 d->fRandom->nextBool());
208 }
209
210 ///////////////////////////////////////////////////////////////////////////////
211
212 /**
213 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
214 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
215 * in both x and y directions.
216 *
217 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
218 */
219
220 class EllipseEdgeEffect : public GrGeometryProcessor {
221 public:
Create(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)222 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
223 bool usesLocalCoords) {
224 return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
225 }
226
~EllipseEdgeEffect()227 virtual ~EllipseEdgeEffect() {}
228
name() const229 const char* name() const override { return "EllipseEdge"; }
230
inPosition() const231 const Attribute* inPosition() const { return fInPosition; }
inColor() const232 const Attribute* inColor() const { return fInColor; }
inEllipseOffset() const233 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
inEllipseRadii() const234 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
color() const235 GrColor color() const { return fColor; }
colorIgnored() const236 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
localMatrix() const237 const SkMatrix& localMatrix() const { return fLocalMatrix; }
usesLocalCoords() const238 bool usesLocalCoords() const { return fUsesLocalCoords; }
239
isStroked() const240 inline bool isStroked() const { return fStroke; }
241
242 class GLSLProcessor : public GrGLSLGeometryProcessor {
243 public:
GLSLProcessor()244 GLSLProcessor() {}
245
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)246 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
247 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
248 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
249 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
250 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
251
252 // emit attributes
253 varyingHandler->emitAttributes(ee);
254
255 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
256 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
257 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
258 ee.inEllipseOffset()->fName);
259
260 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
261 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
262 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
263 ee.inEllipseRadii()->fName);
264
265 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
266 // setup pass through color
267 if (!ee.colorIgnored()) {
268 varyingHandler->addPassThroughAttribute(ee.inColor(), args.fOutputColor);
269 }
270
271 // Setup position
272 this->setupPosition(vertBuilder, gpArgs, ee.inPosition()->fName);
273
274 // emit transforms
275 this->emitTransforms(vertBuilder,
276 varyingHandler,
277 uniformHandler,
278 gpArgs->fPositionVar,
279 ee.inPosition()->fName,
280 ee.localMatrix(),
281 args.fTransformsIn,
282 args.fTransformsOut);
283
284 // for outer curve
285 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
286 ellipseRadii.fsIn());
287 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
288 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
289 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
290
291 // avoid calling inversesqrt on zero.
292 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
293 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
294 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
295
296 // for inner curve
297 if (ee.isStroked()) {
298 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
299 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
300 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
301 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
302 ellipseRadii.fsIn());
303 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
304 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
305 }
306
307 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
308 }
309
GenKey(const GrGeometryProcessor & gp,const GrGLSLCaps &,GrProcessorKeyBuilder * b)310 static void GenKey(const GrGeometryProcessor& gp,
311 const GrGLSLCaps&,
312 GrProcessorKeyBuilder* b) {
313 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
314 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
315 key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
316 key |= ee.colorIgnored() ? 0x4 : 0x0;
317 b->add32(key);
318 }
319
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp)320 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
321 }
322
setTransformData(const GrPrimitiveProcessor & primProc,const GrGLSLProgramDataManager & pdman,int index,const SkTArray<const GrCoordTransform *,true> & transforms)323 void setTransformData(const GrPrimitiveProcessor& primProc,
324 const GrGLSLProgramDataManager& pdman,
325 int index,
326 const SkTArray<const GrCoordTransform*, true>& transforms) override {
327 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
328 }
329
330 private:
331 typedef GrGLSLGeometryProcessor INHERITED;
332 };
333
getGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const334 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
335 GLSLProcessor::GenKey(*this, caps, b);
336 }
337
createGLSLInstance(const GrGLSLCaps &) const338 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
339 return new GLSLProcessor();
340 }
341
342 private:
EllipseEdgeEffect(GrColor color,bool stroke,const SkMatrix & localMatrix,bool usesLocalCoords)343 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
344 bool usesLocalCoords)
345 : fColor(color)
346 , fLocalMatrix(localMatrix)
347 , fUsesLocalCoords(usesLocalCoords) {
348 this->initClassID<EllipseEdgeEffect>();
349 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
350 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
351 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
352 kVec2f_GrVertexAttribType));
353 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
354 kVec4f_GrVertexAttribType));
355 fStroke = stroke;
356 }
357
358 const Attribute* fInPosition;
359 const Attribute* fInColor;
360 const Attribute* fInEllipseOffset;
361 const Attribute* fInEllipseRadii;
362 GrColor fColor;
363 SkMatrix fLocalMatrix;
364 bool fStroke;
365 bool fUsesLocalCoords;
366
367 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
368
369 typedef GrGeometryProcessor INHERITED;
370 };
371
372 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
373
TestCreate(GrProcessorTestData * d)374 const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
375 return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
376 d->fRandom->nextBool(),
377 GrTest::TestMatrix(d->fRandom),
378 d->fRandom->nextBool());
379 }
380
381 ///////////////////////////////////////////////////////////////////////////////
382
383 /**
384 * The output of this effect is a modulation of the input color and coverage for an ellipse,
385 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
386 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
387 * using differentials.
388 *
389 * The result is device-independent and can be used with any affine matrix.
390 */
391
392 class DIEllipseEdgeEffect : public GrGeometryProcessor {
393 public:
394 enum Mode { kStroke = 0, kHairline, kFill };
395
Create(GrColor color,const SkMatrix & viewMatrix,Mode mode,bool usesLocalCoords)396 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
397 bool usesLocalCoords) {
398 return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
399 }
400
~DIEllipseEdgeEffect()401 virtual ~DIEllipseEdgeEffect() {}
402
name() const403 const char* name() const override { return "DIEllipseEdge"; }
404
inPosition() const405 const Attribute* inPosition() const { return fInPosition; }
inColor() const406 const Attribute* inColor() const { return fInColor; }
inEllipseOffsets0() const407 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
inEllipseOffsets1() const408 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
color() const409 GrColor color() const { return fColor; }
colorIgnored() const410 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
viewMatrix() const411 const SkMatrix& viewMatrix() const { return fViewMatrix; }
usesLocalCoords() const412 bool usesLocalCoords() const { return fUsesLocalCoords; }
413
getMode() const414 inline Mode getMode() const { return fMode; }
415
416 class GLSLProcessor : public GrGLSLGeometryProcessor {
417 public:
GLSLProcessor()418 GLSLProcessor()
419 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
420
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)421 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
422 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
423 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
424 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
425 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
426
427 // emit attributes
428 varyingHandler->emitAttributes(ee);
429
430 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
431 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
432 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
433 ee.inEllipseOffsets0()->fName);
434
435 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
436 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
437 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
438 ee.inEllipseOffsets1()->fName);
439
440 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
441 // setup pass through color
442 if (!ee.colorIgnored()) {
443 varyingHandler->addPassThroughAttribute(ee.inColor(), args.fOutputColor);
444 }
445
446 // Setup position
447 this->setupPosition(vertBuilder,
448 uniformHandler,
449 gpArgs,
450 ee.inPosition()->fName,
451 ee.viewMatrix(),
452 &fViewMatrixUniform);
453
454 // emit transforms
455 this->emitTransforms(vertBuilder,
456 varyingHandler,
457 uniformHandler,
458 gpArgs->fPositionVar,
459 ee.inPosition()->fName,
460 args.fTransformsIn,
461 args.fTransformsOut);
462
463 SkAssertResult(fragBuilder->enableFeature(
464 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
465 // for outer curve
466 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
467 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
468 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
469 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
470 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
471 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
472 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
473
474 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
475 // avoid calling inversesqrt on zero.
476 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
477 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
478 if (kHairline == ee.getMode()) {
479 // can probably do this with one step
480 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
481 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
482 } else {
483 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
484 }
485
486 // for inner curve
487 if (kStroke == ee.getMode()) {
488 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
489 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
490 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
491 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
492 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
493 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
494 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
495 offsets1.fsIn());
496 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
497 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
498 }
499
500 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
501 }
502
GenKey(const GrGeometryProcessor & gp,const GrGLSLCaps &,GrProcessorKeyBuilder * b)503 static void GenKey(const GrGeometryProcessor& gp,
504 const GrGLSLCaps&,
505 GrProcessorKeyBuilder* b) {
506 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
507 uint16_t key = ellipseEffect.getMode();
508 key |= ellipseEffect.colorIgnored() << 9;
509 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
510 b->add32(key);
511 }
512
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & gp)513 void setData(const GrGLSLProgramDataManager& pdman,
514 const GrPrimitiveProcessor& gp) override {
515 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
516
517 if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
518 fViewMatrix = dee.viewMatrix();
519 float viewMatrix[3 * 3];
520 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
521 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
522 }
523 }
524
525 private:
526 SkMatrix fViewMatrix;
527 UniformHandle fViewMatrixUniform;
528
529 typedef GrGLSLGeometryProcessor INHERITED;
530 };
531
getGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const532 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
533 GLSLProcessor::GenKey(*this, caps, b);
534 }
535
createGLSLInstance(const GrGLSLCaps &) const536 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
537 return new GLSLProcessor();
538 }
539
540 private:
DIEllipseEdgeEffect(GrColor color,const SkMatrix & viewMatrix,Mode mode,bool usesLocalCoords)541 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
542 bool usesLocalCoords)
543 : fColor(color)
544 , fViewMatrix(viewMatrix)
545 , fUsesLocalCoords(usesLocalCoords) {
546 this->initClassID<DIEllipseEdgeEffect>();
547 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
548 kHigh_GrSLPrecision));
549 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
550 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
551 kVec2f_GrVertexAttribType));
552 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
553 kVec2f_GrVertexAttribType));
554 fMode = mode;
555 }
556
557 const Attribute* fInPosition;
558 const Attribute* fInColor;
559 const Attribute* fInEllipseOffsets0;
560 const Attribute* fInEllipseOffsets1;
561 GrColor fColor;
562 SkMatrix fViewMatrix;
563 Mode fMode;
564 bool fUsesLocalCoords;
565
566 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
567
568 typedef GrGeometryProcessor INHERITED;
569 };
570
571 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
572
TestCreate(GrProcessorTestData * d)573 const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
574 return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
575 GrTest::TestMatrix(d->fRandom),
576 (Mode)(d->fRandom->nextRangeU(0,2)),
577 d->fRandom->nextBool());
578 }
579
580 ///////////////////////////////////////////////////////////////////////////////
581
CreateOvalBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & oval,const SkStrokeRec & stroke,GrShaderCaps * shaderCaps)582 GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
583 const SkMatrix& viewMatrix,
584 const SkRect& oval,
585 const SkStrokeRec& stroke,
586 GrShaderCaps* shaderCaps) {
587 // we can draw circles
588 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
589 return CreateCircleBatch(color, viewMatrix, oval, stroke);
590 }
591
592 // if we have shader derivative support, render as device-independent
593 if (shaderCaps->shaderDerivativeSupport()) {
594 return CreateDIEllipseBatch(color, viewMatrix, oval, stroke);
595 }
596
597 // otherwise axis-aligned ellipses only
598 if (viewMatrix.rectStaysRect()) {
599 return CreateEllipseBatch(color, viewMatrix, oval, stroke);
600 }
601
602 return nullptr;
603 }
604
605 ///////////////////////////////////////////////////////////////////////////////
606
607 class CircleBatch : public GrVertexBatch {
608 public:
609 DEFINE_BATCH_CLASS_ID
610
611 struct Geometry {
612 SkMatrix fViewMatrix;
613 SkRect fDevBounds;
614 SkScalar fInnerRadius;
615 SkScalar fOuterRadius;
616 GrColor fColor;
617 bool fStroke;
618 };
619
Create(const Geometry & geometry)620 static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
621
name() const622 const char* name() const override { return "CircleBatch"; }
623
dumpInfo() const624 SkString dumpInfo() const override {
625 SkString string;
626 for (int i = 0; i < fGeoData.count(); ++i) {
627 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
628 "InnerRad: %.2f, OuterRad: %.2f\n",
629 fGeoData[i].fColor,
630 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
631 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
632 fGeoData[i].fInnerRadius,
633 fGeoData[i].fOuterRadius);
634 }
635 string.append(INHERITED::dumpInfo());
636 return string;
637 }
638
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const639 void computePipelineOptimizations(GrInitInvariantOutput* color,
640 GrInitInvariantOutput* coverage,
641 GrBatchToXPOverrides* overrides) const override {
642 // When this is called on a batch, there is only one geometry bundle
643 color->setKnownFourComponents(fGeoData[0].fColor);
644 coverage->setUnknownSingleComponent();
645 }
646
647 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)648 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
649 // Handle any color overrides
650 if (!overrides.readsColor()) {
651 fGeoData[0].fColor = GrColor_ILLEGAL;
652 }
653 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
654
655 // setup batch properties
656 fBatch.fColorIgnored = !overrides.readsColor();
657 fBatch.fColor = fGeoData[0].fColor;
658 fBatch.fStroke = fGeoData[0].fStroke;
659 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
660 fBatch.fCoverageIgnored = !overrides.readsCoverage();
661 }
662
onPrepareDraws(Target * target) const663 void onPrepareDraws(Target* target) const override {
664 SkMatrix invert;
665 if (!this->viewMatrix().invert(&invert)) {
666 return;
667 }
668
669 // Setup geometry processor
670 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
671 this->stroke(),
672 invert,
673 this->usesLocalCoords()));
674
675 target->initDraw(gp, this->pipeline());
676
677 int instanceCount = fGeoData.count();
678 size_t vertexStride = gp->getVertexStride();
679 SkASSERT(vertexStride == sizeof(CircleVertex));
680 QuadHelper helper;
681 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
682 instanceCount));
683 if (!verts) {
684 return;
685 }
686
687 for (int i = 0; i < instanceCount; i++) {
688 const Geometry& geom = fGeoData[i];
689
690 GrColor color = geom.fColor;
691 SkScalar innerRadius = geom.fInnerRadius;
692 SkScalar outerRadius = geom.fOuterRadius;
693
694 const SkRect& bounds = geom.fDevBounds;
695
696 // The inner radius in the vertex data must be specified in normalized space.
697 innerRadius = innerRadius / outerRadius;
698 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
699 verts[0].fColor = color;
700 verts[0].fOffset = SkPoint::Make(-1, -1);
701 verts[0].fOuterRadius = outerRadius;
702 verts[0].fInnerRadius = innerRadius;
703
704 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
705 verts[1].fColor = color;
706 verts[1].fOffset = SkPoint::Make(-1, 1);
707 verts[1].fOuterRadius = outerRadius;
708 verts[1].fInnerRadius = innerRadius;
709
710 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
711 verts[2].fColor = color;
712 verts[2].fOffset = SkPoint::Make(1, 1);
713 verts[2].fOuterRadius = outerRadius;
714 verts[2].fInnerRadius = innerRadius;
715
716 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
717 verts[3].fColor = color;
718 verts[3].fOffset = SkPoint::Make(1, -1);
719 verts[3].fOuterRadius = outerRadius;
720 verts[3].fInnerRadius = innerRadius;
721
722 verts += kVerticesPerQuad;
723 }
724 helper.recordDraw(target);
725 }
726
geoData()727 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
728
CircleBatch(const Geometry & geometry)729 CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
730 fGeoData.push_back(geometry);
731
732 this->setBounds(geometry.fDevBounds);
733 }
734
onCombineIfPossible(GrBatch * t,const GrCaps & caps)735 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
736 CircleBatch* that = t->cast<CircleBatch>();
737 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
738 that->bounds(), caps)) {
739 return false;
740 }
741
742 if (this->stroke() != that->stroke()) {
743 return false;
744 }
745
746 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
747 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
748 return false;
749 }
750
751 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
752 this->joinBounds(that->bounds());
753 return true;
754 }
755
color() const756 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const757 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const758 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const759 bool stroke() const { return fBatch.fStroke; }
760
761 struct BatchTracker {
762 GrColor fColor;
763 bool fStroke;
764 bool fUsesLocalCoords;
765 bool fColorIgnored;
766 bool fCoverageIgnored;
767 };
768
769 BatchTracker fBatch;
770 SkSTArray<1, Geometry, true> fGeoData;
771
772 typedef GrVertexBatch INHERITED;
773 };
774
create_circle_batch(GrColor color,const SkMatrix & viewMatrix,const SkRect & circle,const SkStrokeRec & stroke)775 static GrDrawBatch* create_circle_batch(GrColor color,
776 const SkMatrix& viewMatrix,
777 const SkRect& circle,
778 const SkStrokeRec& stroke) {
779 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
780 viewMatrix.mapPoints(¢er, 1);
781 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
782 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
783
784 SkStrokeRec::Style style = stroke.getStyle();
785 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
786 SkStrokeRec::kHairline_Style == style;
787 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
788
789 SkScalar innerRadius = 0.0f;
790 SkScalar outerRadius = radius;
791 SkScalar halfWidth = 0;
792 if (hasStroke) {
793 if (SkScalarNearlyZero(strokeWidth)) {
794 halfWidth = SK_ScalarHalf;
795 } else {
796 halfWidth = SkScalarHalf(strokeWidth);
797 }
798
799 outerRadius += halfWidth;
800 if (isStrokeOnly) {
801 innerRadius = radius - halfWidth;
802 }
803 }
804
805 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
806 // computation because the computed alpha is zero, rather than 50%, at the radius.
807 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
808 // and the outset ensures the box will cover all partially covered by the circle.
809 outerRadius += SK_ScalarHalf;
810 innerRadius -= SK_ScalarHalf;
811
812 CircleBatch::Geometry geometry;
813 geometry.fViewMatrix = viewMatrix;
814 geometry.fColor = color;
815 geometry.fInnerRadius = innerRadius;
816 geometry.fOuterRadius = outerRadius;
817 geometry.fStroke = isStrokeOnly && innerRadius > 0;
818 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
819 center.fX + outerRadius, center.fY + outerRadius);
820
821 return CircleBatch::Create(geometry);
822 }
823
CreateCircleBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & circle,const SkStrokeRec & stroke)824 GrDrawBatch* GrOvalRenderer::CreateCircleBatch(GrColor color,
825 const SkMatrix& viewMatrix,
826 const SkRect& circle,
827 const SkStrokeRec& stroke) {
828 return create_circle_batch(color, viewMatrix, circle, stroke);
829 }
830
831 ///////////////////////////////////////////////////////////////////////////////
832
833 class EllipseBatch : public GrVertexBatch {
834 public:
835 DEFINE_BATCH_CLASS_ID
836
837 struct Geometry {
838 SkMatrix fViewMatrix;
839 SkRect fDevBounds;
840 SkScalar fXRadius;
841 SkScalar fYRadius;
842 SkScalar fInnerXRadius;
843 SkScalar fInnerYRadius;
844 GrColor fColor;
845 bool fStroke;
846 };
847
Create(const Geometry & geometry)848 static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
849
name() const850 const char* name() const override { return "EllipseBatch"; }
851
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const852 void computePipelineOptimizations(GrInitInvariantOutput* color,
853 GrInitInvariantOutput* coverage,
854 GrBatchToXPOverrides* overrides) const override {
855 // When this is called on a batch, there is only one geometry bundle
856 color->setKnownFourComponents(fGeoData[0].fColor);
857 coverage->setUnknownSingleComponent();
858 }
859
860 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)861 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
862 // Handle any color overrides
863 if (!overrides.readsCoverage()) {
864 fGeoData[0].fColor = GrColor_ILLEGAL;
865 }
866 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
867
868 // setup batch properties
869 fBatch.fColorIgnored = !overrides.readsColor();
870 fBatch.fColor = fGeoData[0].fColor;
871 fBatch.fStroke = fGeoData[0].fStroke;
872 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
873 fBatch.fCoverageIgnored = !overrides.readsCoverage();
874 }
875
onPrepareDraws(Target * target) const876 void onPrepareDraws(Target* target) const override {
877 SkMatrix invert;
878 if (!this->viewMatrix().invert(&invert)) {
879 return;
880 }
881
882 // Setup geometry processor
883 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
884 this->stroke(),
885 invert,
886 this->usesLocalCoords()));
887
888 target->initDraw(gp, this->pipeline());
889
890 int instanceCount = fGeoData.count();
891 QuadHelper helper;
892 size_t vertexStride = gp->getVertexStride();
893 SkASSERT(vertexStride == sizeof(EllipseVertex));
894 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
895 helper.init(target, vertexStride, instanceCount));
896 if (!verts) {
897 return;
898 }
899
900 for (int i = 0; i < instanceCount; i++) {
901 const Geometry& geom = fGeoData[i];
902
903 GrColor color = geom.fColor;
904 SkScalar xRadius = geom.fXRadius;
905 SkScalar yRadius = geom.fYRadius;
906
907 // Compute the reciprocals of the radii here to save time in the shader
908 SkScalar xRadRecip = SkScalarInvert(xRadius);
909 SkScalar yRadRecip = SkScalarInvert(yRadius);
910 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
911 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
912
913 const SkRect& bounds = geom.fDevBounds;
914
915 // The inner radius in the vertex data must be specified in normalized space.
916 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
917 verts[0].fColor = color;
918 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
919 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
920 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
921
922 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
923 verts[1].fColor = color;
924 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
925 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
926 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
927
928 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
929 verts[2].fColor = color;
930 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
931 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
932 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
933
934 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
935 verts[3].fColor = color;
936 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
937 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
938 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
939
940 verts += kVerticesPerQuad;
941 }
942 helper.recordDraw(target);
943 }
944
geoData()945 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
946
EllipseBatch(const Geometry & geometry)947 EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
948 fGeoData.push_back(geometry);
949
950 this->setBounds(geometry.fDevBounds);
951 }
952
onCombineIfPossible(GrBatch * t,const GrCaps & caps)953 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
954 EllipseBatch* that = t->cast<EllipseBatch>();
955
956 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
957 that->bounds(), caps)) {
958 return false;
959 }
960
961 if (this->stroke() != that->stroke()) {
962 return false;
963 }
964
965 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
966 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
967 return false;
968 }
969
970 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
971 this->joinBounds(that->bounds());
972 return true;
973 }
974
color() const975 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const976 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const977 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const978 bool stroke() const { return fBatch.fStroke; }
979
980 struct BatchTracker {
981 GrColor fColor;
982 bool fStroke;
983 bool fUsesLocalCoords;
984 bool fColorIgnored;
985 bool fCoverageIgnored;
986 };
987
988 BatchTracker fBatch;
989 SkSTArray<1, Geometry, true> fGeoData;
990
991 typedef GrVertexBatch INHERITED;
992 };
993
create_ellipse_batch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)994 static GrDrawBatch* create_ellipse_batch(GrColor color,
995 const SkMatrix& viewMatrix,
996 const SkRect& ellipse,
997 const SkStrokeRec& stroke) {
998 SkASSERT(viewMatrix.rectStaysRect());
999
1000 // do any matrix crunching before we reset the draw state for device coords
1001 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1002 viewMatrix.mapPoints(¢er, 1);
1003 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1004 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1005 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1006 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1007 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1008 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
1009
1010 // do (potentially) anisotropic mapping of stroke
1011 SkVector scaledStroke;
1012 SkScalar strokeWidth = stroke.getWidth();
1013 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1014 viewMatrix[SkMatrix::kMSkewY]));
1015 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1016 viewMatrix[SkMatrix::kMScaleY]));
1017
1018 SkStrokeRec::Style style = stroke.getStyle();
1019 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1020 SkStrokeRec::kHairline_Style == style;
1021 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1022
1023 SkScalar innerXRadius = 0;
1024 SkScalar innerYRadius = 0;
1025 if (hasStroke) {
1026 if (SkScalarNearlyZero(scaledStroke.length())) {
1027 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1028 } else {
1029 scaledStroke.scale(SK_ScalarHalf);
1030 }
1031
1032 // we only handle thick strokes for near-circular ellipses
1033 if (scaledStroke.length() > SK_ScalarHalf &&
1034 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1035 return nullptr;
1036 }
1037
1038 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1039 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1040 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1041 return nullptr;
1042 }
1043
1044 // this is legit only if scale & translation (which should be the case at the moment)
1045 if (isStrokeOnly) {
1046 innerXRadius = xRadius - scaledStroke.fX;
1047 innerYRadius = yRadius - scaledStroke.fY;
1048 }
1049
1050 xRadius += scaledStroke.fX;
1051 yRadius += scaledStroke.fY;
1052 }
1053
1054 // We've extended the outer x radius out half a pixel to antialias.
1055 // This will also expand the rect so all the pixels will be captured.
1056 // TODO: Consider if we should use sqrt(2)/2 instead
1057 xRadius += SK_ScalarHalf;
1058 yRadius += SK_ScalarHalf;
1059
1060 EllipseBatch::Geometry geometry;
1061 geometry.fViewMatrix = viewMatrix;
1062 geometry.fColor = color;
1063 geometry.fXRadius = xRadius;
1064 geometry.fYRadius = yRadius;
1065 geometry.fInnerXRadius = innerXRadius;
1066 geometry.fInnerYRadius = innerYRadius;
1067 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1068 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1069 center.fX + xRadius, center.fY + yRadius);
1070
1071 return EllipseBatch::Create(geometry);
1072 }
1073
CreateEllipseBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1074 GrDrawBatch* GrOvalRenderer::CreateEllipseBatch(GrColor color,
1075 const SkMatrix& viewMatrix,
1076 const SkRect& ellipse,
1077 const SkStrokeRec& stroke) {
1078 return create_ellipse_batch(color, viewMatrix, ellipse, stroke);
1079 }
1080
1081 /////////////////////////////////////////////////////////////////////////////////////////////////
1082
1083 class DIEllipseBatch : public GrVertexBatch {
1084 public:
1085 DEFINE_BATCH_CLASS_ID
1086
1087 struct Geometry {
1088 SkMatrix fViewMatrix;
1089 SkRect fBounds;
1090 SkScalar fXRadius;
1091 SkScalar fYRadius;
1092 SkScalar fInnerXRadius;
1093 SkScalar fInnerYRadius;
1094 SkScalar fGeoDx;
1095 SkScalar fGeoDy;
1096 GrColor fColor;
1097 DIEllipseEdgeEffect::Mode fMode;
1098 };
1099
Create(const Geometry & geometry,const SkRect & bounds)1100 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1101 return new DIEllipseBatch(geometry, bounds);
1102 }
1103
name() const1104 const char* name() const override { return "DIEllipseBatch"; }
1105
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const1106 void computePipelineOptimizations(GrInitInvariantOutput* color,
1107 GrInitInvariantOutput* coverage,
1108 GrBatchToXPOverrides* overrides) const override {
1109 // When this is called on a batch, there is only one geometry bundle
1110 color->setKnownFourComponents(fGeoData[0].fColor);
1111 coverage->setUnknownSingleComponent();
1112 }
1113
1114 private:
1115
initBatchTracker(const GrXPOverridesForBatch & overrides)1116 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1117 // Handle any color overrides
1118 if (!overrides.readsColor()) {
1119 fGeoData[0].fColor = GrColor_ILLEGAL;
1120 }
1121 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1122
1123 // setup batch properties
1124 fBatch.fColorIgnored = !overrides.readsColor();
1125 fBatch.fColor = fGeoData[0].fColor;
1126 fBatch.fMode = fGeoData[0].fMode;
1127 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1128 fBatch.fCoverageIgnored = !overrides.readsCoverage();
1129 }
1130
onPrepareDraws(Target * target) const1131 void onPrepareDraws(Target* target) const override {
1132 // Setup geometry processor
1133 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1134 this->viewMatrix(),
1135 this->mode(),
1136 this->usesLocalCoords()));
1137
1138 target->initDraw(gp, this->pipeline());
1139
1140 int instanceCount = fGeoData.count();
1141 size_t vertexStride = gp->getVertexStride();
1142 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1143 QuadHelper helper;
1144 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1145 helper.init(target, vertexStride, instanceCount));
1146 if (!verts) {
1147 return;
1148 }
1149
1150 for (int i = 0; i < instanceCount; i++) {
1151 const Geometry& geom = fGeoData[i];
1152
1153 GrColor color = geom.fColor;
1154 SkScalar xRadius = geom.fXRadius;
1155 SkScalar yRadius = geom.fYRadius;
1156
1157 const SkRect& bounds = geom.fBounds;
1158
1159 // This adjusts the "radius" to include the half-pixel border
1160 SkScalar offsetDx = geom.fGeoDx / xRadius;
1161 SkScalar offsetDy = geom.fGeoDy / yRadius;
1162
1163 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1164 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
1165
1166 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1167 verts[0].fColor = color;
1168 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1169 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1170
1171 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1172 verts[1].fColor = color;
1173 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1174 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1175
1176 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1177 verts[2].fColor = color;
1178 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1179 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1180
1181 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1182 verts[3].fColor = color;
1183 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1184 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1185
1186 verts += kVerticesPerQuad;
1187 }
1188 helper.recordDraw(target);
1189 }
1190
geoData()1191 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1192
DIEllipseBatch(const Geometry & geometry,const SkRect & bounds)1193 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
1194 fGeoData.push_back(geometry);
1195
1196 this->setBounds(bounds);
1197 }
1198
onCombineIfPossible(GrBatch * t,const GrCaps & caps)1199 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1200 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1201 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1202 that->bounds(), caps)) {
1203 return false;
1204 }
1205
1206 if (this->mode() != that->mode()) {
1207 return false;
1208 }
1209
1210 // TODO rewrite to allow positioning on CPU
1211 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1212 return false;
1213 }
1214
1215 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1216 this->joinBounds(that->bounds());
1217 return true;
1218 }
1219
color() const1220 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1221 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1222 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
mode() const1223 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1224
1225 struct BatchTracker {
1226 GrColor fColor;
1227 DIEllipseEdgeEffect::Mode fMode;
1228 bool fUsesLocalCoords;
1229 bool fColorIgnored;
1230 bool fCoverageIgnored;
1231 };
1232
1233 BatchTracker fBatch;
1234 SkSTArray<1, Geometry, true> fGeoData;
1235
1236 typedef GrVertexBatch INHERITED;
1237 };
1238
create_diellipse_batch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1239 static GrDrawBatch* create_diellipse_batch(GrColor color,
1240 const SkMatrix& viewMatrix,
1241 const SkRect& ellipse,
1242 const SkStrokeRec& stroke) {
1243 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1244 SkScalar xRadius = SkScalarHalf(ellipse.width());
1245 SkScalar yRadius = SkScalarHalf(ellipse.height());
1246
1247 SkStrokeRec::Style style = stroke.getStyle();
1248 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
1249 DIEllipseEdgeEffect::kStroke :
1250 (SkStrokeRec::kHairline_Style == style) ?
1251 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1252
1253 SkScalar innerXRadius = 0;
1254 SkScalar innerYRadius = 0;
1255 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1256 SkScalar strokeWidth = stroke.getWidth();
1257
1258 if (SkScalarNearlyZero(strokeWidth)) {
1259 strokeWidth = SK_ScalarHalf;
1260 } else {
1261 strokeWidth *= SK_ScalarHalf;
1262 }
1263
1264 // we only handle thick strokes for near-circular ellipses
1265 if (strokeWidth > SK_ScalarHalf &&
1266 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1267 return nullptr;
1268 }
1269
1270 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1271 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1272 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1273 return nullptr;
1274 }
1275
1276 // set inner radius (if needed)
1277 if (SkStrokeRec::kStroke_Style == style) {
1278 innerXRadius = xRadius - strokeWidth;
1279 innerYRadius = yRadius - strokeWidth;
1280 }
1281
1282 xRadius += strokeWidth;
1283 yRadius += strokeWidth;
1284 }
1285 if (DIEllipseEdgeEffect::kStroke == mode) {
1286 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1287 DIEllipseEdgeEffect::kFill;
1288 }
1289
1290 // This expands the outer rect so that after CTM we end up with a half-pixel border
1291 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1292 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1293 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1294 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1295 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1296 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1297
1298 DIEllipseBatch::Geometry geometry;
1299 geometry.fViewMatrix = viewMatrix;
1300 geometry.fColor = color;
1301 geometry.fXRadius = xRadius;
1302 geometry.fYRadius = yRadius;
1303 geometry.fInnerXRadius = innerXRadius;
1304 geometry.fInnerYRadius = innerYRadius;
1305 geometry.fGeoDx = geoDx;
1306 geometry.fGeoDy = geoDy;
1307 geometry.fMode = mode;
1308 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1309 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
1310
1311 SkRect devBounds = geometry.fBounds;
1312 viewMatrix.mapRect(&devBounds);
1313 return DIEllipseBatch::Create(geometry, devBounds);
1314 }
1315
CreateDIEllipseBatch(GrColor color,const SkMatrix & viewMatrix,const SkRect & ellipse,const SkStrokeRec & stroke)1316 GrDrawBatch* GrOvalRenderer::CreateDIEllipseBatch(GrColor color,
1317 const SkMatrix& viewMatrix,
1318 const SkRect& ellipse,
1319 const SkStrokeRec& stroke) {
1320 return create_diellipse_batch(color, viewMatrix, ellipse, stroke);
1321 }
1322
1323 ///////////////////////////////////////////////////////////////////////////////
1324
1325 static const uint16_t gRRectIndices[] = {
1326 // corners
1327 0, 1, 5, 0, 5, 4,
1328 2, 3, 7, 2, 7, 6,
1329 8, 9, 13, 8, 13, 12,
1330 10, 11, 15, 10, 15, 14,
1331
1332 // edges
1333 1, 2, 6, 1, 6, 5,
1334 4, 5, 9, 4, 9, 8,
1335 6, 7, 11, 6, 11, 10,
1336 9, 10, 14, 9, 14, 13,
1337
1338 // center
1339 // we place this at the end so that we can ignore these indices when rendering stroke-only
1340 5, 6, 10, 5, 10, 9
1341 };
1342
1343 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1344 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1345 static const int kVertsPerRRect = 16;
1346 static const int kNumRRectsInIndexBuffer = 256;
1347
1348 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1349 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
ref_rrect_index_buffer(bool strokeOnly,GrResourceProvider * resourceProvider)1350 static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1351 GrResourceProvider* resourceProvider) {
1352 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1353 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1354 if (strokeOnly) {
1355 return resourceProvider->findOrCreateInstancedIndexBuffer(
1356 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1357 gStrokeRRectOnlyIndexBufferKey);
1358 } else {
1359 return resourceProvider->findOrCreateInstancedIndexBuffer(
1360 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1361 gRRectOnlyIndexBufferKey);
1362
1363 }
1364 }
1365
1366 ///////////////////////////////////////////////////////////////////////////////////////////////////
1367
1368 class RRectCircleRendererBatch : public GrVertexBatch {
1369 public:
1370 DEFINE_BATCH_CLASS_ID
1371
1372 struct Geometry {
1373 SkMatrix fViewMatrix;
1374 SkRect fDevBounds;
1375 SkScalar fInnerRadius;
1376 SkScalar fOuterRadius;
1377 GrColor fColor;
1378 bool fStroke;
1379 };
1380
Create(const Geometry & geometry)1381 static GrDrawBatch* Create(const Geometry& geometry) {
1382 return new RRectCircleRendererBatch(geometry);
1383 }
1384
name() const1385 const char* name() const override { return "RRectCircleBatch"; }
1386
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const1387 void computePipelineOptimizations(GrInitInvariantOutput* color,
1388 GrInitInvariantOutput* coverage,
1389 GrBatchToXPOverrides* overrides) const override {
1390 // When this is called on a batch, there is only one geometry bundle
1391 color->setKnownFourComponents(fGeoData[0].fColor);
1392 coverage->setUnknownSingleComponent();
1393 }
1394
1395 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)1396 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1397 // Handle any color overrides
1398 if (!overrides.readsColor()) {
1399 fGeoData[0].fColor = GrColor_ILLEGAL;
1400 }
1401 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1402
1403 // setup batch properties
1404 fBatch.fColorIgnored = !overrides.readsColor();
1405 fBatch.fColor = fGeoData[0].fColor;
1406 fBatch.fStroke = fGeoData[0].fStroke;
1407 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1408 fBatch.fCoverageIgnored = !overrides.readsCoverage();
1409 }
1410
onPrepareDraws(Target * target) const1411 void onPrepareDraws(Target* target) const override {
1412 // reset to device coordinates
1413 SkMatrix invert;
1414 if (!this->viewMatrix().invert(&invert)) {
1415 SkDebugf("Failed to invert\n");
1416 return;
1417 }
1418
1419 // Setup geometry processor
1420 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1421 this->stroke(),
1422 invert,
1423 this->usesLocalCoords()));
1424
1425 target->initDraw(gp, this->pipeline());
1426
1427 int instanceCount = fGeoData.count();
1428 size_t vertexStride = gp->getVertexStride();
1429 SkASSERT(vertexStride == sizeof(CircleVertex));
1430
1431 // drop out the middle quad if we're stroked
1432 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1433 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1434 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
1435
1436 InstancedHelper helper;
1437 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
1438 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1439 indicesPerInstance, instanceCount));
1440 if (!verts || !indexBuffer) {
1441 SkDebugf("Could not allocate vertices\n");
1442 return;
1443 }
1444
1445 for (int i = 0; i < instanceCount; i++) {
1446 const Geometry& args = fGeoData[i];
1447
1448 GrColor color = args.fColor;
1449 SkScalar outerRadius = args.fOuterRadius;
1450
1451 const SkRect& bounds = args.fDevBounds;
1452
1453 SkScalar yCoords[4] = {
1454 bounds.fTop,
1455 bounds.fTop + outerRadius,
1456 bounds.fBottom - outerRadius,
1457 bounds.fBottom
1458 };
1459
1460 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1461 // The inner radius in the vertex data must be specified in normalized space.
1462 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1463 for (int i = 0; i < 4; ++i) {
1464 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1465 verts->fColor = color;
1466 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1467 verts->fOuterRadius = outerRadius;
1468 verts->fInnerRadius = innerRadius;
1469 verts++;
1470
1471 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1472 verts->fColor = color;
1473 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1474 verts->fOuterRadius = outerRadius;
1475 verts->fInnerRadius = innerRadius;
1476 verts++;
1477
1478 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1479 verts->fColor = color;
1480 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1481 verts->fOuterRadius = outerRadius;
1482 verts->fInnerRadius = innerRadius;
1483 verts++;
1484
1485 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1486 verts->fColor = color;
1487 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1488 verts->fOuterRadius = outerRadius;
1489 verts->fInnerRadius = innerRadius;
1490 verts++;
1491 }
1492 }
1493
1494 helper.recordDraw(target);
1495 }
1496
geoData()1497 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1498
RRectCircleRendererBatch(const Geometry & geometry)1499 RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
1500 fGeoData.push_back(geometry);
1501
1502 this->setBounds(geometry.fDevBounds);
1503 }
1504
onCombineIfPossible(GrBatch * t,const GrCaps & caps)1505 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1506 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1507 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1508 that->bounds(), caps)) {
1509 return false;
1510 }
1511
1512 if (this->stroke() != that->stroke()) {
1513 return false;
1514 }
1515
1516 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1517 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1518 return false;
1519 }
1520
1521 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1522 this->joinBounds(that->bounds());
1523 return true;
1524 }
1525
color() const1526 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1527 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1528 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const1529 bool stroke() const { return fBatch.fStroke; }
1530
1531 struct BatchTracker {
1532 GrColor fColor;
1533 bool fStroke;
1534 bool fUsesLocalCoords;
1535 bool fColorIgnored;
1536 bool fCoverageIgnored;
1537 };
1538
1539 BatchTracker fBatch;
1540 SkSTArray<1, Geometry, true> fGeoData;
1541
1542 typedef GrVertexBatch INHERITED;
1543 };
1544
1545 class RRectEllipseRendererBatch : public GrVertexBatch {
1546 public:
1547 DEFINE_BATCH_CLASS_ID
1548
1549 struct Geometry {
1550 SkMatrix fViewMatrix;
1551 SkRect fDevBounds;
1552 SkScalar fXRadius;
1553 SkScalar fYRadius;
1554 SkScalar fInnerXRadius;
1555 SkScalar fInnerYRadius;
1556 GrColor fColor;
1557 bool fStroke;
1558 };
1559
Create(const Geometry & geometry)1560 static GrDrawBatch* Create(const Geometry& geometry) {
1561 return new RRectEllipseRendererBatch(geometry);
1562 }
1563
name() const1564 const char* name() const override { return "RRectEllipseRendererBatch"; }
1565
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const1566 void computePipelineOptimizations(GrInitInvariantOutput* color,
1567 GrInitInvariantOutput* coverage,
1568 GrBatchToXPOverrides* overrides) const override {
1569 // When this is called on a batch, there is only one geometry bundle
1570 color->setKnownFourComponents(fGeoData[0].fColor);
1571 coverage->setUnknownSingleComponent();
1572 }
1573
1574 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)1575 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
1576 // Handle any color overrides
1577 if (!overrides.readsColor()) {
1578 fGeoData[0].fColor = GrColor_ILLEGAL;
1579 }
1580 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
1581
1582 // setup batch properties
1583 fBatch.fColorIgnored = !overrides.readsColor();
1584 fBatch.fColor = fGeoData[0].fColor;
1585 fBatch.fStroke = fGeoData[0].fStroke;
1586 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
1587 fBatch.fCoverageIgnored = !overrides.readsCoverage();
1588 }
1589
onPrepareDraws(Target * target) const1590 void onPrepareDraws(Target* target) const override {
1591 // reset to device coordinates
1592 SkMatrix invert;
1593 if (!this->viewMatrix().invert(&invert)) {
1594 SkDebugf("Failed to invert\n");
1595 return;
1596 }
1597
1598 // Setup geometry processor
1599 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1600 this->stroke(),
1601 invert,
1602 this->usesLocalCoords()));
1603
1604 target->initDraw(gp, this->pipeline());
1605
1606 int instanceCount = fGeoData.count();
1607 size_t vertexStride = gp->getVertexStride();
1608 SkASSERT(vertexStride == sizeof(EllipseVertex));
1609
1610 // drop out the middle quad if we're stroked
1611 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1612 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1613 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
1614
1615 InstancedHelper helper;
1616 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1617 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
1618 kVertsPerRRect, indicesPerInstance, instanceCount));
1619 if (!verts || !indexBuffer) {
1620 SkDebugf("Could not allocate vertices\n");
1621 return;
1622 }
1623
1624 for (int i = 0; i < instanceCount; i++) {
1625 const Geometry& args = fGeoData[i];
1626
1627 GrColor color = args.fColor;
1628
1629 // Compute the reciprocals of the radii here to save time in the shader
1630 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1631 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1632 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1633 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1634
1635 // Extend the radii out half a pixel to antialias.
1636 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1637 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1638
1639 const SkRect& bounds = args.fDevBounds;
1640
1641 SkScalar yCoords[4] = {
1642 bounds.fTop,
1643 bounds.fTop + yOuterRadius,
1644 bounds.fBottom - yOuterRadius,
1645 bounds.fBottom
1646 };
1647 SkScalar yOuterOffsets[4] = {
1648 yOuterRadius,
1649 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1650 SK_ScalarNearlyZero,
1651 yOuterRadius
1652 };
1653
1654 for (int i = 0; i < 4; ++i) {
1655 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1656 verts->fColor = color;
1657 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1658 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1659 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1660 verts++;
1661
1662 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1663 verts->fColor = color;
1664 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1665 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1666 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1667 verts++;
1668
1669 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1670 verts->fColor = color;
1671 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1672 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1673 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1674 verts++;
1675
1676 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1677 verts->fColor = color;
1678 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1679 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1680 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1681 verts++;
1682 }
1683 }
1684 helper.recordDraw(target);
1685 }
1686
geoData()1687 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1688
RRectEllipseRendererBatch(const Geometry & geometry)1689 RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
1690 fGeoData.push_back(geometry);
1691
1692 this->setBounds(geometry.fDevBounds);
1693 }
1694
onCombineIfPossible(GrBatch * t,const GrCaps & caps)1695 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
1696 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1697
1698 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1699 that->bounds(), caps)) {
1700 return false;
1701 }
1702
1703 if (this->stroke() != that->stroke()) {
1704 return false;
1705 }
1706
1707 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1708 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1709 return false;
1710 }
1711
1712 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1713 this->joinBounds(that->bounds());
1714 return true;
1715 }
1716
color() const1717 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1718 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1719 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const1720 bool stroke() const { return fBatch.fStroke; }
1721
1722 struct BatchTracker {
1723 GrColor fColor;
1724 bool fStroke;
1725 bool fUsesLocalCoords;
1726 bool fColorIgnored;
1727 bool fCoverageIgnored;
1728 };
1729
1730 BatchTracker fBatch;
1731 SkSTArray<1, Geometry, true> fGeoData;
1732
1733 typedef GrVertexBatch INHERITED;
1734 };
1735
create_rrect_batch(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkStrokeRec & stroke)1736 static GrDrawBatch* create_rrect_batch(GrColor color,
1737 const SkMatrix& viewMatrix,
1738 const SkRRect& rrect,
1739 const SkStrokeRec& stroke) {
1740 SkASSERT(viewMatrix.rectStaysRect());
1741 SkASSERT(rrect.isSimple());
1742 SkASSERT(!rrect.isOval());
1743
1744 // RRect batchs only handle simple, but not too simple, rrects
1745 // do any matrix crunching before we reset the draw state for device coords
1746 const SkRect& rrectBounds = rrect.getBounds();
1747 SkRect bounds;
1748 viewMatrix.mapRect(&bounds, rrectBounds);
1749
1750 SkVector radii = rrect.getSimpleRadii();
1751 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1752 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1753 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1754 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
1755
1756 SkStrokeRec::Style style = stroke.getStyle();
1757
1758 // do (potentially) anisotropic mapping of stroke
1759 SkVector scaledStroke;
1760 SkScalar strokeWidth = stroke.getWidth();
1761
1762 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1763 SkStrokeRec::kHairline_Style == style;
1764 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1765
1766 if (hasStroke) {
1767 if (SkStrokeRec::kHairline_Style == style) {
1768 scaledStroke.set(1, 1);
1769 } else {
1770 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1771 viewMatrix[SkMatrix::kMSkewY]));
1772 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1773 viewMatrix[SkMatrix::kMScaleY]));
1774 }
1775
1776 // if half of strokewidth is greater than radius, we don't handle that right now
1777 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
1778 return nullptr;
1779 }
1780 }
1781
1782 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1783 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1784 // patch will have fractional coverage. This only matters when the interior is actually filled.
1785 // We could consider falling back to rect rendering here, since a tiny radius is
1786 // indistinguishable from a square corner.
1787 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
1788 return nullptr;
1789 }
1790
1791 // if the corners are circles, use the circle renderer
1792 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
1793 SkScalar innerRadius = 0.0f;
1794 SkScalar outerRadius = xRadius;
1795 SkScalar halfWidth = 0;
1796 if (hasStroke) {
1797 if (SkScalarNearlyZero(scaledStroke.fX)) {
1798 halfWidth = SK_ScalarHalf;
1799 } else {
1800 halfWidth = SkScalarHalf(scaledStroke.fX);
1801 }
1802
1803 if (isStrokeOnly) {
1804 innerRadius = xRadius - halfWidth;
1805 }
1806 outerRadius += halfWidth;
1807 bounds.outset(halfWidth, halfWidth);
1808 }
1809
1810 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
1811
1812 // The radii are outset for two reasons. First, it allows the shader to simply perform
1813 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1814 // Second, the outer radius is used to compute the verts of the bounding box that is
1815 // rendered and the outset ensures the box will cover all partially covered by the rrect
1816 // corners.
1817 outerRadius += SK_ScalarHalf;
1818 innerRadius -= SK_ScalarHalf;
1819
1820 // Expand the rect so all the pixels will be captured.
1821 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1822
1823 RRectCircleRendererBatch::Geometry geometry;
1824 geometry.fViewMatrix = viewMatrix;
1825 geometry.fColor = color;
1826 geometry.fInnerRadius = innerRadius;
1827 geometry.fOuterRadius = outerRadius;
1828 geometry.fStroke = isStrokeOnly;
1829 geometry.fDevBounds = bounds;
1830
1831 return RRectCircleRendererBatch::Create(geometry);
1832 // otherwise we use the ellipse renderer
1833 } else {
1834 SkScalar innerXRadius = 0.0f;
1835 SkScalar innerYRadius = 0.0f;
1836 if (hasStroke) {
1837 if (SkScalarNearlyZero(scaledStroke.length())) {
1838 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1839 } else {
1840 scaledStroke.scale(SK_ScalarHalf);
1841 }
1842
1843 // we only handle thick strokes for near-circular ellipses
1844 if (scaledStroke.length() > SK_ScalarHalf &&
1845 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1846 return nullptr;
1847 }
1848
1849 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1850 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1851 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1852 return nullptr;
1853 }
1854
1855 // this is legit only if scale & translation (which should be the case at the moment)
1856 if (isStrokeOnly) {
1857 innerXRadius = xRadius - scaledStroke.fX;
1858 innerYRadius = yRadius - scaledStroke.fY;
1859 }
1860
1861 xRadius += scaledStroke.fX;
1862 yRadius += scaledStroke.fY;
1863 bounds.outset(scaledStroke.fX, scaledStroke.fY);
1864 }
1865
1866 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
1867
1868 // Expand the rect so all the pixels will be captured.
1869 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1870
1871 RRectEllipseRendererBatch::Geometry geometry;
1872 geometry.fViewMatrix = viewMatrix;
1873 geometry.fColor = color;
1874 geometry.fXRadius = xRadius;
1875 geometry.fYRadius = yRadius;
1876 geometry.fInnerXRadius = innerXRadius;
1877 geometry.fInnerYRadius = innerYRadius;
1878 geometry.fStroke = isStrokeOnly;
1879 geometry.fDevBounds = bounds;
1880
1881 return RRectEllipseRendererBatch::Create(geometry);
1882 }
1883 }
1884
CreateRRectBatch(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkStrokeRec & stroke,GrShaderCaps * shaderCaps)1885 GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
1886 const SkMatrix& viewMatrix,
1887 const SkRRect& rrect,
1888 const SkStrokeRec& stroke,
1889 GrShaderCaps* shaderCaps) {
1890 if (rrect.isOval()) {
1891 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
1892 }
1893
1894 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
1895 return nullptr;
1896 }
1897
1898 return create_rrect_batch(color, viewMatrix, rrect, stroke);
1899 }
1900
1901 ///////////////////////////////////////////////////////////////////////////////////////////////////
1902
1903 #ifdef GR_TEST_UTILS
1904
DRAW_BATCH_TEST_DEFINE(CircleBatch)1905 DRAW_BATCH_TEST_DEFINE(CircleBatch) {
1906 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1907 GrColor color = GrRandomColor(random);
1908 SkRect circle = GrTest::TestSquare(random);
1909 return create_circle_batch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
1910 }
1911
DRAW_BATCH_TEST_DEFINE(EllipseBatch)1912 DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
1913 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1914 GrColor color = GrRandomColor(random);
1915 SkRect ellipse = GrTest::TestSquare(random);
1916 return create_ellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
1917 }
1918
DRAW_BATCH_TEST_DEFINE(DIEllipseBatch)1919 DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
1920 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1921 GrColor color = GrRandomColor(random);
1922 SkRect ellipse = GrTest::TestSquare(random);
1923 return create_diellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
1924 }
1925
DRAW_BATCH_TEST_DEFINE(RRectBatch)1926 DRAW_BATCH_TEST_DEFINE(RRectBatch) {
1927 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1928 GrColor color = GrRandomColor(random);
1929 const SkRRect& rrect = GrTest::TestRRectSimple(random);
1930 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
1931 }
1932
1933 #endif
1934