blob: a3256602afc9fda92695db195007d28ac5a283c3 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
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
Brian Salomon289e3d82016-12-14 15:52:56 -05008#include "GrOvalOpFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -05009#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080010#include "GrGeometryProcessor.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050011#include "GrOpFlushState.h"
joshualitt76e7fb62015-02-11 08:52:27 -080012#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070013#include "GrResourceProvider.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050014#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070015#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000016#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000017#include "SkStrokeRec.h"
egdaniel2d721d32015-11-11 13:06:05 -080018#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080019#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070020#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080021#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080022#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050023#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060024#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050025#include "ops/GrMeshDrawOp.h"
Brian Salomon05441c42017-05-15 16:45:49 -040026#include "ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080027
commit-bot@chromium.org81312832013-03-22 18:34:09 +000028namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080029
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050031 SkPoint fPos;
32 GrColor fColor;
33 SkPoint fOffset;
34 SkPoint fOuterRadii;
35 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000036};
37
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000038struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050039 SkPoint fPos;
40 GrColor fColor;
41 SkPoint fOuterOffset;
42 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000043};
44
Brian Salomon289e3d82016-12-14 15:52:56 -050045static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000046}
47
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000048///////////////////////////////////////////////////////////////////////////////
49
50/**
bsalomonce1c8862014-12-15 07:11:22 -080051 * The output of this effect is a modulation of the input color and coverage for a circle. It
52 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080053 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080054 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080055 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080056 * vec4f : (p.xy, outerRad, innerRad)
57 * p is the position in the normalized space.
58 * outerRad is the outerRadius in device space.
59 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * Additional clip planes are supported for rendering circular arcs. The additional planes are
61 * either intersected or unioned together. Up to three planes are supported (an initial plane,
62 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050063 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070064 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000065 */
66
bsalomoncdaa97b2016-03-08 08:30:14 -080067class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000068public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070069 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
70 const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040071 : INHERITED(kCircleGeometryProcessor_ClassID)
72 , fLocalMatrix(localMatrix) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040073 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
74 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
75 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070076 if (clipPlane) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040077 fInClipPlane = &this->addVertexAttrib("inClipPlane", kHalf3_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070078 } else {
79 fInClipPlane = nullptr;
80 }
81 if (isectPlane) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040082 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kHalf3_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070083 } else {
84 fInIsectPlane = nullptr;
85 }
86 if (unionPlane) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040087 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kHalf3_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070088 } else {
89 fInUnionPlane = nullptr;
90 }
bsalomoncdaa97b2016-03-08 08:30:14 -080091 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000092 }
93
Brian Salomond3b65972017-03-22 12:05:03 -040094 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000095
mtklein36352bf2015-03-25 18:17:31 -070096 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
Brian Salomon94efbf52016-11-29 13:43:05 -050098 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -070099 GLSLProcessor::GenKey(*this, caps, b);
100 }
101
Brian Salomon94efbf52016-11-29 13:43:05 -0500102 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700103 return new GLSLProcessor();
104 }
105
106private:
egdaniel57d3b032015-11-13 11:57:27 -0800107 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000108 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800109 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110
Brian Salomon289e3d82016-12-14 15:52:56 -0500111 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800112 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800113 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800114 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800115 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700116 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800117
joshualittabb52a12015-01-13 15:02:10 -0800118 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800119 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400120 fragBuilder->codeAppend("float4 circleEdge;");
Chris Daltonfdde34e2017-10-16 14:15:26 -0600121 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700122 if (cgp.fInClipPlane) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400123 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700124 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
125 }
126 if (cgp.fInIsectPlane) {
127 SkASSERT(cgp.fInClipPlane);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400128 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700129 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
130 }
131 if (cgp.fInUnionPlane) {
132 SkASSERT(cgp.fInClipPlane);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400133 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
135 }
joshualittabb52a12015-01-13 15:02:10 -0800136
joshualittb8c241a2015-05-19 08:23:30 -0700137 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700138 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800139
joshualittabb52a12015-01-13 15:02:10 -0800140 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400141 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800142
143 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800144 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800145 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800146 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800147 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700148 cgp.fInPosition->fName,
149 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700150 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800151
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400152 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400153 fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);");
154 fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800155 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500156 fragBuilder->codeAppend(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400157 "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
158 fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800159 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000160 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000161
bsalomon4f3a0ca2016-08-22 13:14:26 -0700162 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500163 fragBuilder->codeAppend(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400164 "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
Brian Salomon289e3d82016-12-14 15:52:56 -0500165 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700166 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500167 fragBuilder->codeAppend(
168 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
169 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700170 }
171 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500172 fragBuilder->codeAppend(
173 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
174 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700175 }
176 fragBuilder->codeAppend("edgeAlpha *= clip;");
177 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400178 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000179 }
180
robertphillips46d36f02015-01-18 08:14:14 -0800181 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500182 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700183 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800184 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700185 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500186 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700187 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500188 key |= cgp.fInClipPlane ? 0x04 : 0x0;
189 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
190 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700191 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000192 }
193
bsalomona624bf32016-09-20 09:12:47 -0700194 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
195 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700196 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700197 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700198 }
199
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000200 private:
egdaniele659a582015-11-13 09:55:43 -0800201 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000202 };
203
Brian Salomon289e3d82016-12-14 15:52:56 -0500204 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800205 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800206 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800207 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700208 const Attribute* fInClipPlane;
209 const Attribute* fInIsectPlane;
210 const Attribute* fInUnionPlane;
Brian Salomon289e3d82016-12-14 15:52:56 -0500211 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000212
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400213 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214
joshualitt249af152014-09-15 11:41:13 -0700215 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216};
217
bsalomoncdaa97b2016-03-08 08:30:14 -0800218GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219
Hal Canary6f6961e2017-01-31 13:50:44 -0500220#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700221sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500222 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
223 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
224 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225}
Hal Canary6f6961e2017-01-31 13:50:44 -0500226#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227
228///////////////////////////////////////////////////////////////////////////////
229
230/**
231 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000232 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
233 * in both x and y directions.
234 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000235 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000236 */
237
bsalomoncdaa97b2016-03-08 08:30:14 -0800238class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239public:
Ethan Nicholasabff9562017-10-09 10:54:08 -0400240 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
241 : INHERITED(kEllipseGeometryProcessor_ClassID)
242 , fLocalMatrix(localMatrix) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -0400243 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
244 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
245 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kHalf2_GrVertexAttribType);
246 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kHalf4_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800247 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248 }
249
Brian Salomond3b65972017-03-22 12:05:03 -0400250 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251
mtklein36352bf2015-03-25 18:17:31 -0700252 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800253
Brian Salomon94efbf52016-11-29 13:43:05 -0500254 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700255 GLSLProcessor::GenKey(*this, caps, b);
256 }
257
Brian Salomon94efbf52016-11-29 13:43:05 -0500258 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700259 return new GLSLProcessor();
260 }
261
262private:
egdaniel57d3b032015-11-13 11:57:27 -0800263 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800265 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266
Brian Salomon289e3d82016-12-14 15:52:56 -0500267 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800268 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800269 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800270 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800271 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000272
joshualittabb52a12015-01-13 15:02:10 -0800273 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800274 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800275
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400276 GrGLSLVertToFrag ellipseOffsets(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800277 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800278 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700279 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000280
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400281 GrGLSLVertToFrag ellipseRadii(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800282 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500283 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800284
cdalton85285412016-02-18 12:37:07 -0800285 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700286 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700287 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800288
joshualittabb52a12015-01-13 15:02:10 -0800289 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400290 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800291
292 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800293 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800294 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800295 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800296 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700297 egp.fInPosition->fName,
298 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700299 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800300
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000301 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400302 fragBuilder->codeAppendf("half2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800303 ellipseRadii.fsIn());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400304 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
305 fragBuilder->codeAppendf("half2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
306 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700307
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000308 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800309 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400310 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
311 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000312
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000313 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800314 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500315 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800316 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500317 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
318 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800319 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
320 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000321 }
322
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400323 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000324 }
325
robertphillips46d36f02015-01-18 08:14:14 -0800326 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500327 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700328 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800329 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
330 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700331 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700332 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000333 }
334
bsalomona624bf32016-09-20 09:12:47 -0700335 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
336 FPCoordTransformIter&& transformIter) override {
337 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
338 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700339 }
340
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000341 private:
egdaniele659a582015-11-13 09:55:43 -0800342 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000343 };
344
joshualitt71c92602015-01-14 08:12:47 -0800345 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800346 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800347 const Attribute* fInEllipseOffset;
348 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700349 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000350 bool fStroke;
351
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400352 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000353
joshualitt249af152014-09-15 11:41:13 -0700354 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000355};
356
bsalomoncdaa97b2016-03-08 08:30:14 -0800357GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000358
Hal Canary6f6961e2017-01-31 13:50:44 -0500359#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700360sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
361 return sk_sp<GrGeometryProcessor>(
Brian Salomon289e3d82016-12-14 15:52:56 -0500362 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000363}
Hal Canary6f6961e2017-01-31 13:50:44 -0500364#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000365
366///////////////////////////////////////////////////////////////////////////////
367
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000368/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000369 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000370 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
371 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
372 * using differentials.
373 *
374 * The result is device-independent and can be used with any affine matrix.
375 */
376
bsalomoncdaa97b2016-03-08 08:30:14 -0800377enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000378
bsalomoncdaa97b2016-03-08 08:30:14 -0800379class DIEllipseGeometryProcessor : public GrGeometryProcessor {
380public:
381 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400382 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
383 , fViewMatrix(viewMatrix) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -0400384 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
385 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
386 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kHalf2_GrVertexAttribType);
387 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kHalf2_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800388 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000389 }
390
Brian Salomond3b65972017-03-22 12:05:03 -0400391 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000392
mtklein36352bf2015-03-25 18:17:31 -0700393 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000394
Brian Salomon94efbf52016-11-29 13:43:05 -0500395 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700396 GLSLProcessor::GenKey(*this, caps, b);
397 }
halcanary9d524f22016-03-29 09:03:52 -0700398
Brian Salomon94efbf52016-11-29 13:43:05 -0500399 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700400 return new GLSLProcessor();
401 }
402
403private:
egdaniel57d3b032015-11-13 11:57:27 -0800404 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000405 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500406 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000407
joshualitt465283c2015-09-11 08:19:35 -0700408 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800409 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800410 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800411 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800412 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000413
joshualittabb52a12015-01-13 15:02:10 -0800414 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800415 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800416
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400417 GrGLSLVertToFrag offsets0(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800418 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500419 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700420
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400421 GrGLSLVertToFrag offsets1(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800422 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500423 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800424
cdalton85285412016-02-18 12:37:07 -0800425 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700426 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800427
joshualittabb52a12015-01-13 15:02:10 -0800428 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400429 this->writeOutputPosition(vertBuilder,
430 uniformHandler,
431 gpArgs,
432 diegp.fInPosition->fName,
433 diegp.fViewMatrix,
434 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800435
436 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800437 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800438 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800439 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800440 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700441 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700442 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800443
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000444 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400445 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
446 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
447 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
448 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500449 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400450 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
451 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500452 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000453
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400454 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000455 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800456 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400457 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800458 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000459 // can probably do this with one step
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400460 fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800461 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000462 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400463 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000464 }
465
466 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800467 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800468 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
469 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
470 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
471 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500472 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400473 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
474 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500475 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800476 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
477 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000478 }
479
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400480 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000481 }
482
robertphillips46d36f02015-01-18 08:14:14 -0800483 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500484 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700485 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800486 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
487 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700488 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700489 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000490 }
491
bsalomona624bf32016-09-20 09:12:47 -0700492 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
493 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800494 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700495
bsalomon31df31c2016-08-17 09:00:24 -0700496 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
497 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700498 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800499 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700500 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
501 }
bsalomona624bf32016-09-20 09:12:47 -0700502 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000503 }
504
505 private:
joshualitt5559ca22015-05-21 15:50:36 -0700506 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700507 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800508
egdaniele659a582015-11-13 09:55:43 -0800509 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000510 };
511
joshualitt71c92602015-01-14 08:12:47 -0800512 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800513 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800514 const Attribute* fInEllipseOffsets0;
515 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500516 SkMatrix fViewMatrix;
517 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000518
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400519 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000520
joshualitt249af152014-09-15 11:41:13 -0700521 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000522};
523
bsalomoncdaa97b2016-03-08 08:30:14 -0800524GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000525
Hal Canary6f6961e2017-01-31 13:50:44 -0500526#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700527sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500528 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
529 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000530}
Hal Canary6f6961e2017-01-31 13:50:44 -0500531#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000532
533///////////////////////////////////////////////////////////////////////////////
534
jvanverth6ca48822016-10-07 06:57:32 -0700535// We have two possible cases for geometry for a circle:
536
537// In the case of a normal fill, we draw geometry for the circle as an octagon.
538static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500539 // enter the octagon
540 // clang-format off
541 0, 1, 8, 1, 2, 8,
542 2, 3, 8, 3, 4, 8,
543 4, 5, 8, 5, 6, 8,
544 6, 7, 8, 7, 0, 8
545 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700546};
547
548// For stroked circles, we use two nested octagons.
549static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500550 // enter the octagon
551 // clang-format off
552 0, 1, 9, 0, 9, 8,
553 1, 2, 10, 1, 10, 9,
554 2, 3, 11, 2, 11, 10,
555 3, 4, 12, 3, 12, 11,
556 4, 5, 13, 4, 13, 12,
557 5, 6, 14, 5, 14, 13,
558 6, 7, 15, 6, 15, 14,
559 7, 0, 8, 7, 8, 15,
560 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700561};
562
Brian Salomon289e3d82016-12-14 15:52:56 -0500563
jvanverth6ca48822016-10-07 06:57:32 -0700564static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
565static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
566static const int kVertsPerStrokeCircle = 16;
567static const int kVertsPerFillCircle = 9;
568
569static int circle_type_to_vert_count(bool stroked) {
570 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
571}
572
573static int circle_type_to_index_count(bool stroked) {
574 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
575}
576
577static const uint16_t* circle_type_to_indices(bool stroked) {
578 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
579}
580
581///////////////////////////////////////////////////////////////////////////////
582
Brian Salomon05441c42017-05-15 16:45:49 -0400583class CircleOp final : public GrMeshDrawOp {
584private:
585 using Helper = GrSimpleMeshDrawOpHelper;
586
joshualitt76e7fb62015-02-11 08:52:27 -0800587public:
Brian Salomon25a88092016-12-01 09:36:50 -0500588 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700589
bsalomon4f3a0ca2016-08-22 13:14:26 -0700590 /** Optional extra params to render a partial arc rather than a full circle. */
591 struct ArcParams {
592 SkScalar fStartAngleRadians;
593 SkScalar fSweepAngleRadians;
594 bool fUseCenter;
595 };
Brian Salomon05441c42017-05-15 16:45:49 -0400596
597 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
598 SkPoint center, SkScalar radius, const GrStyle& style,
599 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700600 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700601 if (style.hasPathEffect()) {
602 return nullptr;
603 }
Brian Salomon05441c42017-05-15 16:45:49 -0400604 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700605 SkStrokeRec::Style recStyle = stroke.getStyle();
606 if (arcParams) {
607 // Arc support depends on the style.
608 switch (recStyle) {
609 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500610 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700611 return nullptr;
612 case SkStrokeRec::kFill_Style:
613 // This supports all fills.
614 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500615 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700616 case SkStrokeRec::kHairline_Style:
617 // Strokes that don't use the center point are supported with butt cap.
618 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
619 return nullptr;
620 }
621 break;
622 }
623 }
Brian Salomon05441c42017-05-15 16:45:49 -0400624 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
625 arcParams);
626 }
627
628 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
629 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
630 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
631 const SkStrokeRec& stroke = style.strokeRec();
632 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700633
bsalomon4b4a7cc2016-07-08 04:42:54 -0700634 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700635 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700636 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800637
Brian Salomon289e3d82016-12-14 15:52:56 -0500638 bool isStrokeOnly =
639 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700640 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700641
jvanverth6ca48822016-10-07 06:57:32 -0700642 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700643 SkScalar outerRadius = radius;
644 SkScalar halfWidth = 0;
645 if (hasStroke) {
646 if (SkScalarNearlyZero(strokeWidth)) {
647 halfWidth = SK_ScalarHalf;
648 } else {
649 halfWidth = SkScalarHalf(strokeWidth);
650 }
651
652 outerRadius += halfWidth;
653 if (isStrokeOnly) {
654 innerRadius = radius - halfWidth;
655 }
656 }
657
658 // The radii are outset for two reasons. First, it allows the shader to simply perform
659 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
660 // Second, the outer radius is used to compute the verts of the bounding box that is
661 // rendered and the outset ensures the box will cover all partially covered by the circle.
662 outerRadius += SK_ScalarHalf;
663 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700664 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400665 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700666
bsalomon4f3a0ca2016-08-22 13:14:26 -0700667 // This makes every point fully inside the intersection plane.
668 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
669 // This makes every point fully outside the union plane.
670 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
671 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
672 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700673 if (arcParams) {
674 // The shader operates in a space where the circle is translated to be centered at the
675 // origin. Here we compute points on the unit circle at the starting and ending angles.
676 SkPoint startPoint, stopPoint;
677 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
678 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
679 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -0400680
681 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
682 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
683 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
684 startPoint.normalize();
685 stopPoint.normalize();
686
687 // If the matrix included scale (on one axis) we need to swap our start and end points
688 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
689 SkTSwap(startPoint, stopPoint);
690 }
691
bsalomon4f3a0ca2016-08-22 13:14:26 -0700692 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
693 // radial lines. However, in both cases we have to be careful about the half-circle.
694 // case. In that case the two radial lines are equal and so that edge gets clipped
695 // twice. Since the shared edge goes through the center we fall back on the useCenter
696 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500697 bool useCenter =
698 (arcParams->fUseCenter || isStrokeOnly) &&
699 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700700 if (useCenter) {
701 SkVector norm0 = {startPoint.fY, -startPoint.fX};
702 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
703 if (arcParams->fSweepAngleRadians > 0) {
704 norm0.negate();
705 } else {
706 norm1.negate();
707 }
Brian Salomon05441c42017-05-15 16:45:49 -0400708 fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700709 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -0400710 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700711 color,
712 innerRadius,
713 outerRadius,
714 {norm0.fX, norm0.fY, 0.5f},
715 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
716 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700717 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500718 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400719 fClipPlaneIsect = false;
720 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700721 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400722 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700723 color,
724 innerRadius,
725 outerRadius,
726 {norm0.fX, norm0.fY, 0.5f},
727 {norm1.fX, norm1.fY, 0.5f},
728 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700729 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500730 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400731 fClipPlaneIsect = true;
732 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700733 }
734 } else {
735 // We clip to a secant of the original circle.
736 startPoint.scale(radius);
737 stopPoint.scale(radius);
738 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
739 norm.normalize();
740 if (arcParams->fSweepAngleRadians > 0) {
741 norm.negate();
742 }
743 SkScalar d = -norm.dot(startPoint) + 0.5f;
744
Brian Salomon05441c42017-05-15 16:45:49 -0400745 fCircles.emplace_back(
746 Circle{color,
747 innerRadius,
748 outerRadius,
749 {norm.fX, norm.fY, d},
750 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
751 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
752 devBounds,
753 stroked});
754 fClipPlane = true;
755 fClipPlaneIsect = false;
756 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700757 }
758 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400759 fCircles.emplace_back(
760 Circle{color,
761 innerRadius,
762 outerRadius,
763 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
764 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
765 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
766 devBounds,
767 stroked});
768 fClipPlane = false;
769 fClipPlaneIsect = false;
770 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700771 }
bsalomon88cf17d2016-07-08 06:40:56 -0700772 // Use the original radius and stroke radius for the bounds so that it does not include the
773 // AA bloat.
774 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -0400775 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -0500776 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
777 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -0400778 fVertCount = circle_type_to_vert_count(stroked);
779 fIndexCount = circle_type_to_index_count(stroked);
780 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -0800781 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700782
Brian Salomon289e3d82016-12-14 15:52:56 -0500783 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800784
Robert Phillipsf1748f52017-09-14 14:11:24 -0400785 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400786 fHelper.visitProxies(func);
787 }
788
robertphillipse004bfc2015-11-16 09:06:59 -0800789 SkString dumpInfo() const override {
790 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -0400791 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500792 string.appendf(
793 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
794 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -0400795 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
796 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
797 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800798 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -0400799 string += fHelper.dumpInfo();
800 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -0800801 return string;
802 }
803
Brian Osman9a725dd2017-09-20 09:53:22 -0400804 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
805 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -0400806 GrColor* color = &fCircles.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -0400807 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
808 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -0400809 }
810
811 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
812
bsalomone46f9fe2015-08-18 06:05:14 -0700813private:
Brian Salomon91326c32017-08-09 16:02:19 -0400814 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800815 SkMatrix localMatrix;
816 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800817 return;
818 }
819
820 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500821 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
822 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700823
824 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500825 SkPoint fPos;
826 GrColor fColor;
827 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700828 SkScalar fOuterRadius;
829 SkScalar fInnerRadius;
830 // These planes may or may not be present in the vertex buffer.
831 SkScalar fHalfPlanes[3][3];
832 };
joshualitt76e7fb62015-02-11 08:52:27 -0800833
joshualitt76e7fb62015-02-11 08:52:27 -0800834 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500835 SkASSERT(vertexStride ==
836 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
837 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
838 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700839
840 const GrBuffer* vertexBuffer;
841 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500842 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
843 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700844 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700845 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800846 return;
847 }
848
jvanverth6ca48822016-10-07 06:57:32 -0700849 const GrBuffer* indexBuffer = nullptr;
850 int firstIndex = 0;
851 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
852 if (!indices) {
853 SkDebugf("Could not allocate indices\n");
854 return;
855 }
856
857 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -0400858 for (const auto& circle : fCircles) {
859 SkScalar innerRadius = circle.fInnerRadius;
860 SkScalar outerRadius = circle.fOuterRadius;
861 GrColor color = circle.fColor;
862 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800863
Brian Salomon289e3d82016-12-14 15:52:56 -0500864 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
865 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
866 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
867 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
868 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
869 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
870 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
871 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800872
873 // The inner radius in the vertex data must be specified in normalized space.
874 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700875
876 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500877 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700878 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500879
Brian Salomon289e3d82016-12-14 15:52:56 -0500880 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700881 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700882 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700883 v0->fOuterRadius = outerRadius;
884 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800885
Brian Salomon289e3d82016-12-14 15:52:56 -0500886 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700887 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700888 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700889 v1->fOuterRadius = outerRadius;
890 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800891
Brian Salomon289e3d82016-12-14 15:52:56 -0500892 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700893 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700894 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700895 v2->fOuterRadius = outerRadius;
896 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800897
Brian Salomon289e3d82016-12-14 15:52:56 -0500898 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700899 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700900 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700901 v3->fOuterRadius = outerRadius;
902 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800903
Brian Salomon289e3d82016-12-14 15:52:56 -0500904 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700905 v4->fColor = color;
906 v4->fOffset = SkPoint::Make(octOffset, 1);
907 v4->fOuterRadius = outerRadius;
908 v4->fInnerRadius = innerRadius;
909
Brian Salomon289e3d82016-12-14 15:52:56 -0500910 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700911 v5->fColor = color;
912 v5->fOffset = SkPoint::Make(-octOffset, 1);
913 v5->fOuterRadius = outerRadius;
914 v5->fInnerRadius = innerRadius;
915
Brian Salomon289e3d82016-12-14 15:52:56 -0500916 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700917 v6->fColor = color;
918 v6->fOffset = SkPoint::Make(-1, octOffset);
919 v6->fOuterRadius = outerRadius;
920 v6->fInnerRadius = innerRadius;
921
Brian Salomon289e3d82016-12-14 15:52:56 -0500922 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700923 v7->fColor = color;
924 v7->fOffset = SkPoint::Make(-1, -octOffset);
925 v7->fOuterRadius = outerRadius;
926 v7->fInnerRadius = innerRadius;
927
bsalomon4f3a0ca2016-08-22 13:14:26 -0700928 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -0400929 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
930 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
931 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
932 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
933 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
934 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
935 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
936 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700937 }
938 int unionIdx = 1;
939 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -0400940 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
941 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
942 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
943 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
944 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
945 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
946 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
947 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700948 unionIdx = 2;
949 }
950 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -0400951 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
952 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
953 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
954 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
955 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
956 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
957 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
958 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700959 }
jvanverth6ca48822016-10-07 06:57:32 -0700960
Brian Salomon05441c42017-05-15 16:45:49 -0400961 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -0700962 // compute the inner ring
963 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
964 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
965 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
966 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
967 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
968 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
969 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
970 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
971
972 // cosine and sine of pi/8
973 SkScalar c = 0.923579533f;
974 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -0400975 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700976
Brian Salomon289e3d82016-12-14 15:52:56 -0500977 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700978 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500979 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700980 v0->fOuterRadius = outerRadius;
981 v0->fInnerRadius = innerRadius;
982
Brian Salomon289e3d82016-12-14 15:52:56 -0500983 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700984 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500985 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700986 v1->fOuterRadius = outerRadius;
987 v1->fInnerRadius = innerRadius;
988
Brian Salomon289e3d82016-12-14 15:52:56 -0500989 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700990 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500991 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700992 v2->fOuterRadius = outerRadius;
993 v2->fInnerRadius = innerRadius;
994
Brian Salomon289e3d82016-12-14 15:52:56 -0500995 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700996 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500997 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700998 v3->fOuterRadius = outerRadius;
999 v3->fInnerRadius = innerRadius;
1000
Brian Salomon289e3d82016-12-14 15:52:56 -05001001 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001002 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001003 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001004 v4->fOuterRadius = outerRadius;
1005 v4->fInnerRadius = innerRadius;
1006
Brian Salomon289e3d82016-12-14 15:52:56 -05001007 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001008 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001009 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001010 v5->fOuterRadius = outerRadius;
1011 v5->fInnerRadius = innerRadius;
1012
Brian Salomon289e3d82016-12-14 15:52:56 -05001013 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001014 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001015 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001016 v6->fOuterRadius = outerRadius;
1017 v6->fInnerRadius = innerRadius;
1018
Brian Salomon289e3d82016-12-14 15:52:56 -05001019 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001020 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001021 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001022 v7->fOuterRadius = outerRadius;
1023 v7->fInnerRadius = innerRadius;
1024
1025 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001026 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1027 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1028 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1029 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1030 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1031 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1032 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1033 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001034 }
1035 int unionIdx = 1;
1036 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001037 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1038 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1039 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1040 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1041 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1042 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1043 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1044 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001045 unionIdx = 2;
1046 }
1047 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001048 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1049 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1050 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1051 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1052 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1053 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1054 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1055 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001056 }
1057 } else {
1058 // filled
1059 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1060 v8->fPos = center;
1061 v8->fColor = color;
1062 v8->fOffset = SkPoint::Make(0, 0);
1063 v8->fOuterRadius = outerRadius;
1064 v8->fInnerRadius = innerRadius;
1065 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001066 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001067 }
1068 int unionIdx = 1;
1069 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001070 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001071 unionIdx = 2;
1072 }
1073 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001074 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001075 }
1076 }
1077
Brian Salomon05441c42017-05-15 16:45:49 -04001078 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1079 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001080 for (int i = 0; i < primIndexCount; ++i) {
1081 *indices++ = primIndices[i] + currStartVertex;
1082 }
1083
Brian Salomon05441c42017-05-15 16:45:49 -04001084 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1085 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001086 }
jvanverth6ca48822016-10-07 06:57:32 -07001087
Chris Dalton3809bab2017-06-13 10:55:06 -06001088 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06001089 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1090 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001091 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001092 }
1093
Brian Salomon25a88092016-12-01 09:36:50 -05001094 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001095 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001096
1097 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001098 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001099 return false;
1100 }
1101
Brian Salomon05441c42017-05-15 16:45:49 -04001102 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001103 return false;
1104 }
1105
Brian Salomon05441c42017-05-15 16:45:49 -04001106 if (fHelper.usesLocalCoords() &&
1107 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001108 return false;
1109 }
1110
Brian Salomon289e3d82016-12-14 15:52:56 -05001111 // Because we've set up the ops that don't use the planes with noop values
1112 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001113 fClipPlane |= that->fClipPlane;
1114 fClipPlaneIsect |= that->fClipPlaneIsect;
1115 fClipPlaneUnion |= that->fClipPlaneUnion;
1116
Brian Salomon05441c42017-05-15 16:45:49 -04001117 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001118 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001119 fVertCount += that->fVertCount;
1120 fIndexCount += that->fIndexCount;
1121 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001122 return true;
1123 }
1124
Brian Salomon05441c42017-05-15 16:45:49 -04001125 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001126 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001127 SkScalar fInnerRadius;
1128 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001129 SkScalar fClipPlane[3];
1130 SkScalar fIsectPlane[3];
1131 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001132 SkRect fDevBounds;
1133 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001134 };
1135
Brian Salomon289e3d82016-12-14 15:52:56 -05001136 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001137 Helper fHelper;
1138 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001139 int fVertCount;
1140 int fIndexCount;
1141 bool fAllFill;
1142 bool fClipPlane;
1143 bool fClipPlaneIsect;
1144 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001145
Brian Salomon05441c42017-05-15 16:45:49 -04001146 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001147};
1148
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001149///////////////////////////////////////////////////////////////////////////////
1150
Brian Salomon05441c42017-05-15 16:45:49 -04001151class EllipseOp : public GrMeshDrawOp {
1152private:
1153 using Helper = GrSimpleMeshDrawOpHelper;
1154
1155 struct DeviceSpaceParams {
1156 SkPoint fCenter;
1157 SkScalar fXRadius;
1158 SkScalar fYRadius;
1159 SkScalar fInnerXRadius;
1160 SkScalar fInnerYRadius;
1161 };
1162
joshualitt76e7fb62015-02-11 08:52:27 -08001163public:
Brian Salomon25a88092016-12-01 09:36:50 -05001164 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001165
Brian Salomon05441c42017-05-15 16:45:49 -04001166 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1167 const SkRect& ellipse, const SkStrokeRec& stroke) {
1168 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001169 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001170 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1171 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001172 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1173 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001174 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1175 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1176 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1177 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001178
bsalomon4b4a7cc2016-07-08 04:42:54 -07001179 // do (potentially) anisotropic mapping of stroke
1180 SkVector scaledStroke;
1181 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001182 scaledStroke.fX = SkScalarAbs(
1183 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1184 scaledStroke.fY = SkScalarAbs(
1185 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001186
1187 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001188 bool isStrokeOnly =
1189 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001190 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1191
Brian Salomon05441c42017-05-15 16:45:49 -04001192 params.fInnerXRadius = 0;
1193 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001194 if (hasStroke) {
1195 if (SkScalarNearlyZero(scaledStroke.length())) {
1196 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1197 } else {
1198 scaledStroke.scale(SK_ScalarHalf);
1199 }
1200
1201 // we only handle thick strokes for near-circular ellipses
1202 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001203 (0.5f * params.fXRadius > params.fYRadius ||
1204 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001205 return nullptr;
1206 }
1207
1208 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001209 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1210 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1211 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1212 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001213 return nullptr;
1214 }
1215
1216 // this is legit only if scale & translation (which should be the case at the moment)
1217 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001218 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1219 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001220 }
1221
Brian Salomon05441c42017-05-15 16:45:49 -04001222 params.fXRadius += scaledStroke.fX;
1223 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001224 }
Brian Salomon05441c42017-05-15 16:45:49 -04001225 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
1226 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001227
Brian Salomon05441c42017-05-15 16:45:49 -04001228 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1229 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1230 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1231 SkStrokeRec::Style style = stroke.getStyle();
1232 bool isStrokeOnly =
1233 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001234
Brian Salomon05441c42017-05-15 16:45:49 -04001235 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1236 params.fInnerXRadius, params.fInnerYRadius,
1237 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1238 params.fCenter.fY - params.fYRadius,
1239 params.fCenter.fX + params.fXRadius,
1240 params.fCenter.fY + params.fYRadius)});
1241
1242 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001243
bsalomon4b4a7cc2016-07-08 04:42:54 -07001244 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001245 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001246
Brian Salomon05441c42017-05-15 16:45:49 -04001247 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1248 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001249 }
joshualitt76e7fb62015-02-11 08:52:27 -08001250
Brian Salomon289e3d82016-12-14 15:52:56 -05001251 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001252
Robert Phillipsf1748f52017-09-14 14:11:24 -04001253 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001254 fHelper.visitProxies(func);
1255 }
1256
Brian Salomon7c3e7182016-12-01 09:35:30 -05001257 SkString dumpInfo() const override {
1258 SkString string;
1259 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001260 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001261 string.appendf(
1262 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1263 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1264 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1265 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1266 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001267 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001268 string += fHelper.dumpInfo();
1269 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001270 return string;
1271 }
1272
Brian Osman9a725dd2017-09-20 09:53:22 -04001273 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1274 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001275 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001276 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1277 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001278 }
1279
1280 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1281
bsalomone46f9fe2015-08-18 06:05:14 -07001282private:
Brian Salomon91326c32017-08-09 16:02:19 -04001283 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001284 SkMatrix localMatrix;
1285 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001286 return;
1287 }
1288
1289 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001290 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001291
bsalomonb5238a72015-05-05 07:49:49 -07001292 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001293 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001294 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon05441c42017-05-15 16:45:49 -04001295 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1296 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001297 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001298 return;
1299 }
1300
Brian Salomon05441c42017-05-15 16:45:49 -04001301 for (const auto& ellipse : fEllipses) {
1302 GrColor color = ellipse.fColor;
1303 SkScalar xRadius = ellipse.fXRadius;
1304 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001305
1306 // Compute the reciprocals of the radii here to save time in the shader
1307 SkScalar xRadRecip = SkScalarInvert(xRadius);
1308 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001309 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1310 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001311
vjiaoblack977996d2016-06-30 12:20:54 -07001312 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1313 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1314 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1315
joshualitt76e7fb62015-02-11 08:52:27 -08001316 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001317 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001318 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001319 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001320 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1321 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1322
Brian Salomon05441c42017-05-15 16:45:49 -04001323 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001324 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001325 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001326 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1327 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1328
Brian Salomon57caa662017-10-18 12:21:05 +00001329 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001330 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001331 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001332 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1333 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1334
Brian Salomon57caa662017-10-18 12:21:05 +00001335 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001336 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001337 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001338 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1339 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1340
bsalomonb5238a72015-05-05 07:49:49 -07001341 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001342 }
Brian Salomon05441c42017-05-15 16:45:49 -04001343 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001344 }
1345
Brian Salomon25a88092016-12-01 09:36:50 -05001346 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001347 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001348
Brian Salomon05441c42017-05-15 16:45:49 -04001349 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001350 return false;
1351 }
1352
bsalomoncdaa97b2016-03-08 08:30:14 -08001353 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001354 return false;
1355 }
1356
Brian Salomon05441c42017-05-15 16:45:49 -04001357 if (fHelper.usesLocalCoords() &&
1358 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001359 return false;
1360 }
1361
Brian Salomon05441c42017-05-15 16:45:49 -04001362 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001363 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001364 return true;
1365 }
1366
Brian Salomon05441c42017-05-15 16:45:49 -04001367 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001368 GrColor fColor;
1369 SkScalar fXRadius;
1370 SkScalar fYRadius;
1371 SkScalar fInnerXRadius;
1372 SkScalar fInnerYRadius;
1373 SkRect fDevBounds;
1374 };
joshualitt76e7fb62015-02-11 08:52:27 -08001375
Brian Salomon289e3d82016-12-14 15:52:56 -05001376 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001377 Helper fHelper;
1378 bool fStroked;
1379 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001380
Brian Salomon05441c42017-05-15 16:45:49 -04001381 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001382};
1383
joshualitt76e7fb62015-02-11 08:52:27 -08001384/////////////////////////////////////////////////////////////////////////////////////////////////
1385
Brian Salomon05441c42017-05-15 16:45:49 -04001386class DIEllipseOp : public GrMeshDrawOp {
1387private:
1388 using Helper = GrSimpleMeshDrawOpHelper;
1389
1390 struct DeviceSpaceParams {
1391 SkPoint fCenter;
1392 SkScalar fXRadius;
1393 SkScalar fYRadius;
1394 SkScalar fInnerXRadius;
1395 SkScalar fInnerYRadius;
1396 DIEllipseStyle fStyle;
1397 };
1398
joshualitt76e7fb62015-02-11 08:52:27 -08001399public:
Brian Salomon25a88092016-12-01 09:36:50 -05001400 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001401
Brian Salomon05441c42017-05-15 16:45:49 -04001402 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1403 const SkRect& ellipse, const SkStrokeRec& stroke) {
1404 DeviceSpaceParams params;
1405 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1406 params.fXRadius = SkScalarHalf(ellipse.width());
1407 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001408
bsalomon4b4a7cc2016-07-08 04:42:54 -07001409 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04001410 params.fStyle = (SkStrokeRec::kStroke_Style == style)
1411 ? DIEllipseStyle::kStroke
1412 : (SkStrokeRec::kHairline_Style == style)
1413 ? DIEllipseStyle::kHairline
1414 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001415
Brian Salomon05441c42017-05-15 16:45:49 -04001416 params.fInnerXRadius = 0;
1417 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001418 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1419 SkScalar strokeWidth = stroke.getWidth();
1420
1421 if (SkScalarNearlyZero(strokeWidth)) {
1422 strokeWidth = SK_ScalarHalf;
1423 } else {
1424 strokeWidth *= SK_ScalarHalf;
1425 }
1426
1427 // we only handle thick strokes for near-circular ellipses
1428 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001429 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1430 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001431 return nullptr;
1432 }
1433
1434 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001435 if (strokeWidth * (params.fYRadius * params.fYRadius) <
1436 (strokeWidth * strokeWidth) * params.fXRadius) {
1437 return nullptr;
1438 }
1439 if (strokeWidth * (params.fXRadius * params.fXRadius) <
1440 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001441 return nullptr;
1442 }
1443
1444 // set inner radius (if needed)
1445 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04001446 params.fInnerXRadius = params.fXRadius - strokeWidth;
1447 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001448 }
1449
Brian Salomon05441c42017-05-15 16:45:49 -04001450 params.fXRadius += strokeWidth;
1451 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001452 }
Brian Salomon05441c42017-05-15 16:45:49 -04001453 if (DIEllipseStyle::kStroke == params.fStyle &&
1454 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1455 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001456 }
Brian Salomon05441c42017-05-15 16:45:49 -04001457 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
1458 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001459
Brian Salomon05441c42017-05-15 16:45:49 -04001460 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
1461 const SkMatrix& viewMatrix)
1462 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001463 // This expands the outer rect so that after CTM we end up with a half-pixel border
1464 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1465 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1466 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1467 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001468 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1469 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001470
Brian Salomon05441c42017-05-15 16:45:49 -04001471 fEllipses.emplace_back(
1472 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1473 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1474 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1475 params.fCenter.fY - params.fYRadius - geoDy,
1476 params.fCenter.fX + params.fXRadius + geoDx,
1477 params.fCenter.fY + params.fYRadius + geoDy)});
1478 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1479 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08001480 }
1481
Brian Salomon289e3d82016-12-14 15:52:56 -05001482 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001483
Robert Phillipsf1748f52017-09-14 14:11:24 -04001484 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001485 fHelper.visitProxies(func);
1486 }
1487
Brian Salomon7c3e7182016-12-01 09:35:30 -05001488 SkString dumpInfo() const override {
1489 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001490 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001491 string.appendf(
1492 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1493 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1494 "GeoDY: %.2f\n",
1495 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1496 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1497 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001498 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001499 string += fHelper.dumpInfo();
1500 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001501 return string;
1502 }
1503
Brian Osman9a725dd2017-09-20 09:53:22 -04001504 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1505 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001506 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001507 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1508 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001509 }
1510
1511 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1512
bsalomone46f9fe2015-08-18 06:05:14 -07001513private:
Brian Salomon91326c32017-08-09 16:02:19 -04001514 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001515 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001516 sk_sp<GrGeometryProcessor> gp(
1517 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001518
joshualitt76e7fb62015-02-11 08:52:27 -08001519 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001520 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001521 QuadHelper helper;
1522 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon05441c42017-05-15 16:45:49 -04001523 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001524 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001525 return;
1526 }
1527
Brian Salomon05441c42017-05-15 16:45:49 -04001528 for (const auto& ellipse : fEllipses) {
1529 GrColor color = ellipse.fColor;
1530 SkScalar xRadius = ellipse.fXRadius;
1531 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001532
Brian Salomon05441c42017-05-15 16:45:49 -04001533 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001534
1535 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04001536 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
1537 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001538
Brian Salomon05441c42017-05-15 16:45:49 -04001539 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
1540 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001541
1542 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001543 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001544 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1545 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1546
Brian Salomon289e3d82016-12-14 15:52:56 -05001547 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001548 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001549 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1550 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1551
Brian Salomon57caa662017-10-18 12:21:05 +00001552 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001553 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001554 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1555 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
joshualitt76e7fb62015-02-11 08:52:27 -08001556
Brian Salomon57caa662017-10-18 12:21:05 +00001557 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001558 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001559 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1560 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
joshualitt76e7fb62015-02-11 08:52:27 -08001561
bsalomonb5238a72015-05-05 07:49:49 -07001562 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001563 }
Brian Salomon05441c42017-05-15 16:45:49 -04001564 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001565 }
halcanary9d524f22016-03-29 09:03:52 -07001566
Brian Salomon25a88092016-12-01 09:36:50 -05001567 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001568 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04001569 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001570 return false;
1571 }
1572
bsalomoncdaa97b2016-03-08 08:30:14 -08001573 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001574 return false;
1575 }
1576
joshualittd96a67b2015-05-05 14:09:05 -07001577 // TODO rewrite to allow positioning on CPU
1578 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001579 return false;
1580 }
1581
Brian Salomon05441c42017-05-15 16:45:49 -04001582 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001583 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001584 return true;
1585 }
1586
Brian Salomon05441c42017-05-15 16:45:49 -04001587 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
1588 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001589
Brian Salomon05441c42017-05-15 16:45:49 -04001590 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001591 SkMatrix fViewMatrix;
1592 GrColor fColor;
1593 SkScalar fXRadius;
1594 SkScalar fYRadius;
1595 SkScalar fInnerXRadius;
1596 SkScalar fInnerYRadius;
1597 SkScalar fGeoDx;
1598 SkScalar fGeoDy;
1599 DIEllipseStyle fStyle;
1600 SkRect fBounds;
1601 };
1602
Brian Salomon05441c42017-05-15 16:45:49 -04001603 Helper fHelper;
1604 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001605
Brian Salomon05441c42017-05-15 16:45:49 -04001606 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001607};
1608
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001609///////////////////////////////////////////////////////////////////////////////
1610
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001611// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001612//
1613// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1614// ____________
1615// |_|________|_|
1616// | | | |
1617// | | | |
1618// | | | |
1619// |_|________|_|
1620// |_|________|_|
1621//
1622// For strokes, we don't draw the center quad.
1623//
1624// For circular roundrects, in the case where the stroke width is greater than twice
1625// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001626// in the center. The shared vertices are duplicated so we can set a different outer radius
1627// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001628// ____________
1629// |_|________|_|
1630// | |\ ____ /| |
1631// | | | | | |
1632// | | |____| | |
1633// |_|/______\|_|
1634// |_|________|_|
1635//
1636// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001637//
1638// For filled rrects that need to provide a distance vector we resuse the overstroke
1639// geometry but make the inner rect degenerate (either a point or a horizontal or
1640// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001641
jvanverth84839f62016-08-29 10:16:40 -07001642static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001643 // clang-format off
1644 // overstroke quads
1645 // we place this at the beginning so that we can skip these indices when rendering normally
1646 16, 17, 19, 16, 19, 18,
1647 19, 17, 23, 19, 23, 21,
1648 21, 23, 22, 21, 22, 20,
1649 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001650
Brian Salomon289e3d82016-12-14 15:52:56 -05001651 // corners
1652 0, 1, 5, 0, 5, 4,
1653 2, 3, 7, 2, 7, 6,
1654 8, 9, 13, 8, 13, 12,
1655 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001656
Brian Salomon289e3d82016-12-14 15:52:56 -05001657 // edges
1658 1, 2, 6, 1, 6, 5,
1659 4, 5, 9, 4, 9, 8,
1660 6, 7, 11, 6, 11, 10,
1661 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001662
Brian Salomon289e3d82016-12-14 15:52:56 -05001663 // center
1664 // we place this at the end so that we can ignore these indices when not rendering as filled
1665 5, 6, 10, 5, 10, 9,
1666 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001667};
Brian Salomon289e3d82016-12-14 15:52:56 -05001668
jvanverth84839f62016-08-29 10:16:40 -07001669// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001670static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001671
jvanverth84839f62016-08-29 10:16:40 -07001672// overstroke count is arraysize minus the center indices
1673static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1674// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001675static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001676// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001677static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1678static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001679static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001680
jvanverthc3d0e422016-08-25 08:12:35 -07001681enum RRectType {
1682 kFill_RRectType,
1683 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001684 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07001685};
1686
jvanverth84839f62016-08-29 10:16:40 -07001687static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001688 switch (type) {
1689 case kFill_RRectType:
1690 case kStroke_RRectType:
1691 return kVertsPerStandardRRect;
1692 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001693 return kVertsPerOverstrokeRRect;
1694 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04001695 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05001696 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001697}
1698
1699static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001700 switch (type) {
1701 case kFill_RRectType:
1702 return kIndicesPerFillRRect;
1703 case kStroke_RRectType:
1704 return kIndicesPerStrokeRRect;
1705 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001706 return kIndicesPerOverstrokeRRect;
1707 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04001708 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05001709 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001710}
1711
1712static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001713 switch (type) {
1714 case kFill_RRectType:
1715 case kStroke_RRectType:
1716 return gStandardRRectIndices;
1717 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001718 return gOverstrokeRRectIndices;
1719 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04001720 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05001721 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001722}
1723
joshualitt76e7fb62015-02-11 08:52:27 -08001724///////////////////////////////////////////////////////////////////////////////////////////////////
1725
Robert Phillips79839d42016-10-06 15:03:34 -04001726// For distance computations in the interior of filled rrects we:
1727//
1728// add a interior degenerate (point or line) rect
1729// each vertex of that rect gets -outerRad as its radius
1730// this makes the computation of the distance to the outer edge be negative
1731// negative values are caught and then handled differently in the GP's onEmitCode
1732// each vertex is also given the normalized x & y distance from the interior rect's edge
1733// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1734
Brian Salomon05441c42017-05-15 16:45:49 -04001735class CircularRRectOp : public GrMeshDrawOp {
1736private:
1737 using Helper = GrSimpleMeshDrawOpHelper;
1738
joshualitt76e7fb62015-02-11 08:52:27 -08001739public:
Brian Salomon25a88092016-12-01 09:36:50 -05001740 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001741
bsalomon4b4a7cc2016-07-08 04:42:54 -07001742 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1743 // whether the rrect is only stroked or stroked and filled.
Brian Salomone23bffd2017-06-02 11:01:10 -04001744 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1745 const SkRect& devRect, float devRadius,
1746 float devStrokeWidth, bool strokeOnly) {
1747 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
1748 devRadius, devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04001749 }
Brian Salomone23bffd2017-06-02 11:01:10 -04001750 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1751 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04001752 : INHERITED(ClassID())
1753 , fViewMatrixIfUsingLocalCoords(viewMatrix)
1754 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001755 SkRect bounds = devRect;
1756 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1757 SkScalar innerRadius = 0.0f;
1758 SkScalar outerRadius = devRadius;
1759 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001760 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001761 if (devStrokeWidth > 0) {
1762 if (SkScalarNearlyZero(devStrokeWidth)) {
1763 halfWidth = SK_ScalarHalf;
1764 } else {
1765 halfWidth = SkScalarHalf(devStrokeWidth);
1766 }
joshualitt76e7fb62015-02-11 08:52:27 -08001767
bsalomon4b4a7cc2016-07-08 04:42:54 -07001768 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001769 // Outset stroke by 1/4 pixel
1770 devStrokeWidth += 0.25f;
1771 // If stroke is greater than width or height, this is still a fill
1772 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001773 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001774 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001775 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001776 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001777 }
1778 outerRadius += halfWidth;
1779 bounds.outset(halfWidth, halfWidth);
1780 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001781
bsalomon4b4a7cc2016-07-08 04:42:54 -07001782 // The radii are outset for two reasons. First, it allows the shader to simply perform
1783 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1784 // Second, the outer radius is used to compute the verts of the bounding box that is
1785 // rendered and the outset ensures the box will cover all partially covered by the rrect
1786 // corners.
1787 outerRadius += SK_ScalarHalf;
1788 innerRadius -= SK_ScalarHalf;
1789
bsalomon88cf17d2016-07-08 06:40:56 -07001790 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1791
1792 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001793 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1794
Brian Salomon05441c42017-05-15 16:45:49 -04001795 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001796 fVertCount = rrect_type_to_vert_count(type);
1797 fIndexCount = rrect_type_to_index_count(type);
1798 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001799 }
1800
Brian Salomon289e3d82016-12-14 15:52:56 -05001801 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001802
Robert Phillipsf1748f52017-09-14 14:11:24 -04001803 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001804 fHelper.visitProxies(func);
1805 }
1806
jvanverthc3d0e422016-08-25 08:12:35 -07001807 SkString dumpInfo() const override {
1808 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001809 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001810 string.appendf(
1811 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1812 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001813 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
1814 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
1815 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001816 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001817 string += fHelper.dumpInfo();
1818 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07001819 return string;
1820 }
1821
Brian Osman9a725dd2017-09-20 09:53:22 -04001822 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1823 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001824 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001825 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1826 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001827 }
1828
1829 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1830
Brian Salomon92aee3d2016-12-21 09:20:25 -05001831private:
Robert Phillips79839d42016-10-06 15:03:34 -04001832 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001833 SkPoint fPos;
1834 GrColor fColor;
1835 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001836 SkScalar fOuterRadius;
1837 SkScalar fInnerRadius;
1838 // No half plane, we don't use it here.
1839 };
1840
Brian Salomon289e3d82016-12-14 15:52:56 -05001841 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1842 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1843 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001844 SkASSERT(smInset < bigInset);
1845
1846 // TL
1847 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1848 (*verts)->fColor = color;
1849 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1850 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001851 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001852 (*verts)++;
1853
1854 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001855 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001856 (*verts)->fColor = color;
1857 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1858 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001859 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001860 (*verts)++;
1861
Brian Salomon289e3d82016-12-14 15:52:56 -05001862 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001863 (*verts)->fColor = color;
1864 (*verts)->fOffset = SkPoint::Make(0, 0);
1865 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001866 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001867 (*verts)++;
1868
Brian Salomon289e3d82016-12-14 15:52:56 -05001869 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001870 (*verts)->fColor = color;
1871 (*verts)->fOffset = SkPoint::Make(0, 0);
1872 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001873 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001874 (*verts)++;
1875
1876 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1877 (*verts)->fColor = color;
1878 (*verts)->fOffset = SkPoint::Make(0, 0);
1879 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001880 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001881 (*verts)++;
1882
1883 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1884 (*verts)->fColor = color;
1885 (*verts)->fOffset = SkPoint::Make(0, 0);
1886 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001887 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001888 (*verts)++;
1889
1890 // BL
1891 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1892 (*verts)->fColor = color;
1893 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1894 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001895 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001896 (*verts)++;
1897
1898 // BR
1899 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1900 (*verts)->fColor = color;
1901 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1902 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001903 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001904 (*verts)++;
1905 }
1906
Brian Salomon91326c32017-08-09 16:02:19 -04001907 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001908 // Invert the view matrix as a local matrix (if any other processors require coords).
1909 SkMatrix localMatrix;
1910 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001911 return;
1912 }
1913
1914 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001915 sk_sp<GrGeometryProcessor> gp(
1916 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001917
joshualitt76e7fb62015-02-11 08:52:27 -08001918 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001919 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001920
jvanverth84839f62016-08-29 10:16:40 -07001921 const GrBuffer* vertexBuffer;
1922 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001923
Brian Salomon289e3d82016-12-14 15:52:56 -05001924 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1925 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001926 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001927 SkDebugf("Could not allocate vertices\n");
1928 return;
1929 }
1930
jvanverth84839f62016-08-29 10:16:40 -07001931 const GrBuffer* indexBuffer = nullptr;
1932 int firstIndex = 0;
1933 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1934 if (!indices) {
1935 SkDebugf("Could not allocate indices\n");
1936 return;
1937 }
1938
1939 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001940 for (const auto& rrect : fRRects) {
1941 GrColor color = rrect.fColor;
1942 SkScalar outerRadius = rrect.fOuterRadius;
1943 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001944
Brian Salomon289e3d82016-12-14 15:52:56 -05001945 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1946 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001947
Brian Salomon289e3d82016-12-14 15:52:56 -05001948 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001949 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001950 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04001951 SkScalar innerRadius = rrect.fType != kFill_RRectType
1952 ? rrect.fInnerRadius / rrect.fOuterRadius
1953 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001954 for (int i = 0; i < 4; ++i) {
1955 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001956 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001957 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1958 verts->fOuterRadius = outerRadius;
1959 verts->fInnerRadius = innerRadius;
1960 verts++;
1961
1962 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001963 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001964 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1965 verts->fOuterRadius = outerRadius;
1966 verts->fInnerRadius = innerRadius;
1967 verts++;
1968
1969 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001970 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001971 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1972 verts->fOuterRadius = outerRadius;
1973 verts->fInnerRadius = innerRadius;
1974 verts++;
1975
1976 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001977 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001978 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1979 verts->fOuterRadius = outerRadius;
1980 verts->fInnerRadius = innerRadius;
1981 verts++;
1982 }
jvanverthc3d0e422016-08-25 08:12:35 -07001983 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001984 // Effectively this is an additional stroked rrect, with its
1985 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1986 // This will give us correct AA in the center and the correct
1987 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001988 //
jvanvertha4f1af82016-08-29 07:17:47 -07001989 // Also, the outer offset is a constant vector pointing to the right, which
1990 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04001991 if (kOverstroke_RRectType == rrect.fType) {
1992 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04001993
Brian Salomon05441c42017-05-15 16:45:49 -04001994 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001995 // this is the normalized distance from the outer rectangle of this
1996 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04001997 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001998
Brian Salomon289e3d82016-12-14 15:52:56 -05001999 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04002000 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04002001 }
jvanverth6a397612016-08-26 08:15:33 -07002002
Brian Salomon05441c42017-05-15 16:45:49 -04002003 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2004 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002005 for (int i = 0; i < primIndexCount; ++i) {
2006 *indices++ = primIndices[i] + currStartVertex;
2007 }
2008
Brian Salomon05441c42017-05-15 16:45:49 -04002009 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002010 }
2011
Chris Dalton3809bab2017-06-13 10:55:06 -06002012 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06002013 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
2014 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04002015 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002016 }
2017
Brian Salomon25a88092016-12-01 09:36:50 -05002018 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002019 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002020
2021 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002022 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002023 return false;
2024 }
2025
Brian Salomon05441c42017-05-15 16:45:49 -04002026 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002027 return false;
2028 }
2029
Brian Salomon05441c42017-05-15 16:45:49 -04002030 if (fHelper.usesLocalCoords() &&
2031 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002032 return false;
2033 }
2034
Brian Salomon05441c42017-05-15 16:45:49 -04002035 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002036 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002037 fVertCount += that->fVertCount;
2038 fIndexCount += that->fIndexCount;
2039 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002040 return true;
2041 }
2042
Brian Salomon05441c42017-05-15 16:45:49 -04002043 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002044 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002045 SkScalar fInnerRadius;
2046 SkScalar fOuterRadius;
2047 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002048 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002049 };
2050
Brian Salomon289e3d82016-12-14 15:52:56 -05002051 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002052 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002053 int fVertCount;
2054 int fIndexCount;
2055 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002056 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002057
Brian Salomon05441c42017-05-15 16:45:49 -04002058 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002059};
2060
jvanverth84839f62016-08-29 10:16:40 -07002061static const int kNumRRectsInIndexBuffer = 256;
2062
2063GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2064GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002065static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2066 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002067 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2068 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2069 switch (type) {
2070 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002071 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002072 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2073 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002074 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002075 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002076 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2077 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002078 default:
2079 SkASSERT(false);
2080 return nullptr;
2081 };
2082}
2083
Brian Salomon05441c42017-05-15 16:45:49 -04002084class EllipticalRRectOp : public GrMeshDrawOp {
2085private:
2086 using Helper = GrSimpleMeshDrawOpHelper;
2087
joshualitt76e7fb62015-02-11 08:52:27 -08002088public:
Brian Salomon25a88092016-12-01 09:36:50 -05002089 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002090
bsalomon4b4a7cc2016-07-08 04:42:54 -07002091 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2092 // whether the rrect is only stroked or stroked and filled.
Brian Salomon05441c42017-05-15 16:45:49 -04002093 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
2094 const SkRect& devRect, float devXRadius, float devYRadius,
2095 SkVector devStrokeWidths, bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002096 SkASSERT(devXRadius > 0.5);
2097 SkASSERT(devYRadius > 0.5);
2098 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2099 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002100 if (devStrokeWidths.fX > 0) {
2101 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2102 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2103 } else {
2104 devStrokeWidths.scale(SK_ScalarHalf);
2105 }
joshualitt76e7fb62015-02-11 08:52:27 -08002106
bsalomon4b4a7cc2016-07-08 04:42:54 -07002107 // we only handle thick strokes for near-circular ellipses
2108 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002109 (SK_ScalarHalf * devXRadius > devYRadius ||
2110 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002111 return nullptr;
2112 }
2113
2114 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002115 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2116 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002117 return nullptr;
2118 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002119 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2120 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002121 return nullptr;
2122 }
Brian Salomon05441c42017-05-15 16:45:49 -04002123 }
2124 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
2125 devXRadius, devYRadius, devStrokeWidths,
2126 strokeOnly);
2127 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002128
Brian Salomon05441c42017-05-15 16:45:49 -04002129 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2130 const SkRect& devRect, float devXRadius, float devYRadius,
2131 SkVector devStrokeHalfWidths, bool strokeOnly)
2132 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
2133 SkScalar innerXRadius = 0.0f;
2134 SkScalar innerYRadius = 0.0f;
2135 SkRect bounds = devRect;
2136 bool stroked = false;
2137 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002138 // this is legit only if scale & translation (which should be the case at the moment)
2139 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002140 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2141 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002142 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2143 }
2144
Brian Salomon05441c42017-05-15 16:45:49 -04002145 devXRadius += devStrokeHalfWidths.fX;
2146 devYRadius += devStrokeHalfWidths.fY;
2147 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002148 }
2149
Brian Salomon05441c42017-05-15 16:45:49 -04002150 fStroked = stroked;
2151 fViewMatrixIfUsingLocalCoords = viewMatrix;
2152 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002153 // Expand the rect for aa in order to generate the correct vertices.
2154 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002155 fRRects.emplace_back(
2156 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002157 }
2158
Brian Salomon289e3d82016-12-14 15:52:56 -05002159 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002160
Robert Phillipsf1748f52017-09-14 14:11:24 -04002161 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002162 fHelper.visitProxies(func);
2163 }
2164
Brian Salomon7c3e7182016-12-01 09:35:30 -05002165 SkString dumpInfo() const override {
2166 SkString string;
2167 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002168 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002169 string.appendf(
2170 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2171 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2172 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2173 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2174 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002175 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002176 string += fHelper.dumpInfo();
2177 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002178 return string;
2179 }
2180
Brian Osman9a725dd2017-09-20 09:53:22 -04002181 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2182 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002183 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002184 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2185 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002186 }
2187
2188 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2189
bsalomone46f9fe2015-08-18 06:05:14 -07002190private:
Brian Salomon91326c32017-08-09 16:02:19 -04002191 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002192 SkMatrix localMatrix;
2193 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002194 return;
2195 }
2196
2197 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002198 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002199
joshualitt76e7fb62015-02-11 08:52:27 -08002200 size_t vertexStride = gp->getVertexStride();
2201 SkASSERT(vertexStride == sizeof(EllipseVertex));
2202
bsalomonb5238a72015-05-05 07:49:49 -07002203 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002204 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002205 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2206 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002207
Chris Dalton3809bab2017-06-13 10:55:06 -06002208 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002209 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Chris Daltonbca46e22017-05-15 11:03:26 -06002210 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002211 indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002212 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002213 SkDebugf("Could not allocate vertices\n");
2214 return;
2215 }
2216
Brian Salomon05441c42017-05-15 16:45:49 -04002217 for (const auto& rrect : fRRects) {
2218 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002219 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002220 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2221 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2222 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2223 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002224
2225 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002226 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2227 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002228
Brian Salomon05441c42017-05-15 16:45:49 -04002229 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002230
Brian Salomon289e3d82016-12-14 15:52:56 -05002231 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2232 bounds.fBottom - yOuterRadius, bounds.fBottom};
2233 SkScalar yOuterOffsets[4] = {yOuterRadius,
2234 SK_ScalarNearlyZero, // we're using inversesqrt() in
2235 // shader, so can't be exactly 0
2236 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002237
2238 for (int i = 0; i < 4; ++i) {
2239 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002240 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002241 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2242 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2243 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2244 verts++;
2245
2246 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002247 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002248 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2249 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2250 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2251 verts++;
2252
2253 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002254 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002255 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2256 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2257 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2258 verts++;
2259
2260 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002261 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002262 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2263 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2264 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2265 verts++;
2266 }
2267 }
Brian Salomon05441c42017-05-15 16:45:49 -04002268 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002269 }
2270
Brian Salomon25a88092016-12-01 09:36:50 -05002271 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002272 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002273
Brian Salomon05441c42017-05-15 16:45:49 -04002274 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002275 return false;
2276 }
2277
bsalomoncdaa97b2016-03-08 08:30:14 -08002278 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002279 return false;
2280 }
2281
Brian Salomon05441c42017-05-15 16:45:49 -04002282 if (fHelper.usesLocalCoords() &&
2283 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002284 return false;
2285 }
2286
Brian Salomon05441c42017-05-15 16:45:49 -04002287 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002288 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002289 return true;
2290 }
2291
Brian Salomon05441c42017-05-15 16:45:49 -04002292 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002293 GrColor fColor;
2294 SkScalar fXRadius;
2295 SkScalar fYRadius;
2296 SkScalar fInnerXRadius;
2297 SkScalar fInnerYRadius;
2298 SkRect fDevBounds;
2299 };
2300
Brian Salomon289e3d82016-12-14 15:52:56 -05002301 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002302 Helper fHelper;
2303 bool fStroked;
2304 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002305
Brian Salomon05441c42017-05-15 16:45:49 -04002306 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002307};
2308
Brian Salomon05441c42017-05-15 16:45:49 -04002309static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002310 const SkMatrix& viewMatrix,
2311 const SkRRect& rrect,
2312 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002313 SkASSERT(viewMatrix.rectStaysRect());
2314 SkASSERT(rrect.isSimple());
2315 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002316
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002317 // RRect ops only handle simple, but not too simple, rrects.
2318 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002319 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002320 SkRect bounds;
2321 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002322
2323 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002324 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2325 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2326 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2327 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002328
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002329 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002330
bsalomon4b4a7cc2016-07-08 04:42:54 -07002331 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2332 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002333 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002334
Brian Salomon289e3d82016-12-14 15:52:56 -05002335 bool isStrokeOnly =
2336 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002337 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2338
jvanverthc3d0e422016-08-25 08:12:35 -07002339 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002340 if (hasStroke) {
2341 if (SkStrokeRec::kHairline_Style == style) {
2342 scaledStroke.set(1, 1);
2343 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002344 scaledStroke.fX = SkScalarAbs(
2345 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2346 scaledStroke.fY = SkScalarAbs(
2347 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002348 }
2349
jvanverthc3d0e422016-08-25 08:12:35 -07002350 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2351 // for non-circular rrects, if half of strokewidth is greater than radius,
2352 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002353 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2354 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002355 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002356 }
2357 }
2358
2359 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2360 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2361 // patch will have fractional coverage. This only matters when the interior is actually filled.
2362 // We could consider falling back to rect rendering here, since a tiny radius is
2363 // indistinguishable from a square corner.
2364 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002365 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002366 }
2367
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002368 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002369 if (isCircular) {
Brian Salomone23bffd2017-06-02 11:01:10 -04002370 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
2371 isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05002372 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002373 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04002374 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
2375 scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002376 }
joshualitt3e708c52015-04-30 13:49:27 -07002377}
2378
Brian Salomon05441c42017-05-15 16:45:49 -04002379std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002380 const SkMatrix& viewMatrix,
2381 const SkRRect& rrect,
2382 const SkStrokeRec& stroke,
2383 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002384 if (rrect.isOval()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002385 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002386 }
2387
2388 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002389 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002390 }
2391
Brian Salomone23bffd2017-06-02 11:01:10 -04002392 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002393}
joshualitt3e708c52015-04-30 13:49:27 -07002394
bsalomon4b4a7cc2016-07-08 04:42:54 -07002395///////////////////////////////////////////////////////////////////////////////
2396
Brian Salomon05441c42017-05-15 16:45:49 -04002397std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
2398 const SkMatrix& viewMatrix,
2399 const SkRect& oval,
2400 const SkStrokeRec& stroke,
2401 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002402 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002403 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04002404 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2405 circle_stays_circle(viewMatrix)) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002406 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon05441c42017-05-15 16:45:49 -04002407 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f,
2408 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002409 }
2410
Stan Ilieveb868aa2017-02-21 11:06:16 -05002411 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002412 if (viewMatrix.rectStaysRect()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002413 return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002414 }
2415
Stan Ilieveb868aa2017-02-21 11:06:16 -05002416 // Otherwise, if we have shader derivative support, render as device-independent
2417 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002418 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
Stan Ilieveb868aa2017-02-21 11:06:16 -05002419 }
2420
bsalomon4b4a7cc2016-07-08 04:42:54 -07002421 return nullptr;
2422}
2423
2424///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002425
Brian Salomon05441c42017-05-15 16:45:49 -04002426std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
2427 const SkRect& oval, SkScalar startAngle,
2428 SkScalar sweepAngle, bool useCenter,
2429 const GrStyle& style,
2430 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002431 SkASSERT(!oval.isEmpty());
2432 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002433 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002434 if (SkScalarAbs(sweepAngle) >= 360.f) {
2435 return nullptr;
2436 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002437 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2438 return nullptr;
2439 }
2440 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002441 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2442 useCenter};
Brian Salomon05441c42017-05-15 16:45:49 -04002443 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002444}
2445
2446///////////////////////////////////////////////////////////////////////////////
2447
Hal Canary6f6961e2017-01-31 13:50:44 -05002448#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002449
Brian Salomon05441c42017-05-15 16:45:49 -04002450GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002451 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002452 SkScalar rotate = random->nextSScalar1() * 360.f;
2453 SkScalar translateX = random->nextSScalar1() * 1000.f;
2454 SkScalar translateY = random->nextSScalar1() * 1000.f;
2455 SkScalar scale = random->nextSScalar1() * 100.f;
2456 SkMatrix viewMatrix;
2457 viewMatrix.setRotate(rotate);
2458 viewMatrix.postTranslate(translateX, translateY);
2459 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002460 SkRect circle = GrTest::TestSquare(random);
2461 SkPoint center = {circle.centerX(), circle.centerY()};
2462 SkScalar radius = circle.width() / 2.f;
2463 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002464 CircleOp::ArcParams arcParamsTmp;
2465 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002466 if (random->nextBool()) {
2467 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002468 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2469 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002470 arcParams = &arcParamsTmp;
2471 }
Brian Salomon05441c42017-05-15 16:45:49 -04002472 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
2473 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002474 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002475 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002476 }
2477 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002478}
2479
Brian Salomon05441c42017-05-15 16:45:49 -04002480GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002481 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07002482 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002483 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002484}
2485
Brian Salomon05441c42017-05-15 16:45:49 -04002486GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002487 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07002488 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002489 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002490}
2491
Brian Salomon05441c42017-05-15 16:45:49 -04002492GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002493 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07002494 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Brian Salomone23bffd2017-06-02 11:01:10 -04002495 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002496}
2497
2498#endif