blob: b17578617746ceb5dd7804cd498d5a7bf55fe323 [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"
24#include "glsl/GrGLSLVertexShaderBuilder.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)
71 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080072 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070073 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
74 kHigh_GrSLPrecision);
75 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
Jim Van Verth6750e912016-12-19 14:45:19 -050076 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType,
77 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -070078 if (clipPlane) {
79 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
80 } else {
81 fInClipPlane = nullptr;
82 }
83 if (isectPlane) {
84 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
85 } else {
86 fInIsectPlane = nullptr;
87 }
88 if (unionPlane) {
89 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
90 } else {
91 fInUnionPlane = nullptr;
92 }
bsalomoncdaa97b2016-03-08 08:30:14 -080093 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000094 }
95
Brian Salomond3b65972017-03-22 12:05:03 -040096 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
mtklein36352bf2015-03-25 18:17:31 -070098 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000099
Brian Salomon94efbf52016-11-29 13:43:05 -0500100 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700101 GLSLProcessor::GenKey(*this, caps, b);
102 }
103
Brian Salomon94efbf52016-11-29 13:43:05 -0500104 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700105 return new GLSLProcessor();
106 }
107
108private:
egdaniel57d3b032015-11-13 11:57:27 -0800109 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800111 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112
Brian Salomon289e3d82016-12-14 15:52:56 -0500113 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800114 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800115 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800116 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800117 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700118 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800119
joshualittabb52a12015-01-13 15:02:10 -0800120 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800121 varyingHandler->emitAttributes(cgp);
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400122 fragBuilder->codeAppend("highp float4 circleEdge;");
Jim Van Verth6750e912016-12-19 14:45:19 -0500123 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge",
124 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700125 if (cgp.fInClipPlane) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400126 fragBuilder->codeAppend("float3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700127 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
128 }
129 if (cgp.fInIsectPlane) {
130 SkASSERT(cgp.fInClipPlane);
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400131 fragBuilder->codeAppend("float3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700132 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
133 }
134 if (cgp.fInUnionPlane) {
135 SkASSERT(cgp.fInClipPlane);
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400136 fragBuilder->codeAppend("float3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700137 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
138 }
joshualittabb52a12015-01-13 15:02:10 -0800139
joshualittb8c241a2015-05-19 08:23:30 -0700140 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700141 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800142
joshualittabb52a12015-01-13 15:02:10 -0800143 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700144 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800145
146 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800147 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800148 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800149 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800150 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700151 cgp.fInPosition->fName,
152 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700153 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800154
Jim Van Verth6750e912016-12-19 14:45:19 -0500155 fragBuilder->codeAppend("highp float d = length(circleEdge.xy);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700156 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
157 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800158 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500159 fragBuilder->codeAppend(
160 "float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700161 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800162 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000163 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000164
bsalomon4f3a0ca2016-08-22 13:14:26 -0700165 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500166 fragBuilder->codeAppend(
167 "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
168 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700169 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500170 fragBuilder->codeAppend(
171 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
172 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700173 }
174 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500175 fragBuilder->codeAppend(
176 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
177 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700178 }
179 fragBuilder->codeAppend("edgeAlpha *= clip;");
180 }
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400181 fragBuilder->codeAppendf("%s = float4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000182 }
183
robertphillips46d36f02015-01-18 08:14:14 -0800184 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500185 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700186 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800187 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700188 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500189 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700190 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500191 key |= cgp.fInClipPlane ? 0x04 : 0x0;
192 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
193 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700194 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000195 }
196
bsalomona624bf32016-09-20 09:12:47 -0700197 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
198 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700199 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700200 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700201 }
202
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203 private:
egdaniele659a582015-11-13 09:55:43 -0800204 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000205 };
206
Brian Salomon289e3d82016-12-14 15:52:56 -0500207 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800208 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800209 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800210 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700211 const Attribute* fInClipPlane;
212 const Attribute* fInIsectPlane;
213 const Attribute* fInUnionPlane;
Brian Salomon289e3d82016-12-14 15:52:56 -0500214 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000215
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400216 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217
joshualitt249af152014-09-15 11:41:13 -0700218 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219};
220
bsalomoncdaa97b2016-03-08 08:30:14 -0800221GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000222
Hal Canary6f6961e2017-01-31 13:50:44 -0500223#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700224sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500225 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
226 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
227 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228}
Hal Canary6f6961e2017-01-31 13:50:44 -0500229#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230
231///////////////////////////////////////////////////////////////////////////////
232
233/**
234 * 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 +0000235 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
236 * in both x and y directions.
237 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000238 * 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 +0000239 */
240
bsalomoncdaa97b2016-03-08 08:30:14 -0800241class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500243 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800244 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700245 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
246 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
247 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
248 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800249 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250 }
251
Brian Salomond3b65972017-03-22 12:05:03 -0400252 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253
mtklein36352bf2015-03-25 18:17:31 -0700254 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800255
Brian Salomon94efbf52016-11-29 13:43:05 -0500256 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700257 GLSLProcessor::GenKey(*this, caps, b);
258 }
259
Brian Salomon94efbf52016-11-29 13:43:05 -0500260 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700261 return new GLSLProcessor();
262 }
263
264private:
egdaniel57d3b032015-11-13 11:57:27 -0800265 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800267 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000268
Brian Salomon289e3d82016-12-14 15:52:56 -0500269 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800270 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800271 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800272 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800273 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000274
joshualittabb52a12015-01-13 15:02:10 -0800275 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800276 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800277
egdaniel8dcdedc2015-11-11 06:27:20 -0800278 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800279 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800280 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700281 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000282
egdaniel8dcdedc2015-11-11 06:27:20 -0800283 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800284 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500285 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800286
cdalton85285412016-02-18 12:37:07 -0800287 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700288 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700289 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800290
joshualittabb52a12015-01-13 15:02:10 -0800291 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700292 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800293
294 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800295 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800296 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800297 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800298 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700299 egp.fInPosition->fName,
300 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700301 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800302
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000303 // for outer curve
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400304 fragBuilder->codeAppendf("float2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800305 ellipseRadii.fsIn());
306 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400307 fragBuilder->codeAppendf("float2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800308 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700309
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000310 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800311 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
312 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800313 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000314
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000315 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800316 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500317 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800318 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500319 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
320 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800321 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
322 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000323 }
324
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400325 fragBuilder->codeAppendf("%s = float4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000326 }
327
robertphillips46d36f02015-01-18 08:14:14 -0800328 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500329 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700330 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800331 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
332 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700333 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700334 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000335 }
336
bsalomona624bf32016-09-20 09:12:47 -0700337 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
338 FPCoordTransformIter&& transformIter) override {
339 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
340 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700341 }
342
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000343 private:
egdaniele659a582015-11-13 09:55:43 -0800344 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000345 };
346
joshualitt71c92602015-01-14 08:12:47 -0800347 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800348 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800349 const Attribute* fInEllipseOffset;
350 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700351 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352 bool fStroke;
353
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400354 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000355
joshualitt249af152014-09-15 11:41:13 -0700356 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000357};
358
bsalomoncdaa97b2016-03-08 08:30:14 -0800359GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360
Hal Canary6f6961e2017-01-31 13:50:44 -0500361#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700362sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
363 return sk_sp<GrGeometryProcessor>(
Brian Salomon289e3d82016-12-14 15:52:56 -0500364 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000365}
Hal Canary6f6961e2017-01-31 13:50:44 -0500366#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367
368///////////////////////////////////////////////////////////////////////////////
369
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000370/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000371 * 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 +0000372 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
373 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
374 * using differentials.
375 *
376 * The result is device-independent and can be used with any affine matrix.
377 */
378
bsalomoncdaa97b2016-03-08 08:30:14 -0800379enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000380
bsalomoncdaa97b2016-03-08 08:30:14 -0800381class DIEllipseGeometryProcessor : public GrGeometryProcessor {
382public:
383 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Brian Salomon289e3d82016-12-14 15:52:56 -0500384 : fViewMatrix(viewMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800385 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700386 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
387 kHigh_GrSLPrecision);
388 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
389 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
390 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800391 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000392 }
393
Brian Salomond3b65972017-03-22 12:05:03 -0400394 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000395
mtklein36352bf2015-03-25 18:17:31 -0700396 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000397
Brian Salomon94efbf52016-11-29 13:43:05 -0500398 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700399 GLSLProcessor::GenKey(*this, caps, b);
400 }
halcanary9d524f22016-03-29 09:03:52 -0700401
Brian Salomon94efbf52016-11-29 13:43:05 -0500402 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700403 return new GLSLProcessor();
404 }
405
406private:
egdaniel57d3b032015-11-13 11:57:27 -0800407 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000408 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500409 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000410
joshualitt465283c2015-09-11 08:19:35 -0700411 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800412 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800413 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800414 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800415 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000416
joshualittabb52a12015-01-13 15:02:10 -0800417 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800418 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800419
egdaniel8dcdedc2015-11-11 06:27:20 -0800420 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800421 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500422 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700423
egdaniel8dcdedc2015-11-11 06:27:20 -0800424 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800425 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500426 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800427
cdalton85285412016-02-18 12:37:07 -0800428 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700429 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800430
joshualittabb52a12015-01-13 15:02:10 -0800431 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800432 this->setupPosition(vertBuilder,
433 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800434 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700435 diegp.fInPosition->fName,
436 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700437 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800438
439 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800440 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800441 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800442 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800443 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700444 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700445 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800446
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000447 // for outer curve
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400448 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800449 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400450 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s);", offsets0.fsIn());
451 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500452 fragBuilder->codeAppendf(
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400453 "float2 grad = float2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
Brian Salomon289e3d82016-12-14 15:52:56 -0500454 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
455 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000456
egdaniel4ca2e602015-11-18 08:01:26 -0800457 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000458 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800459 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
460 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800461 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000462 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800463 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
464 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000465 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800466 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000467 }
468
469 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800470 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800471 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
472 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
473 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
474 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500475 fragBuilder->codeAppendf(
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400476 "grad = float2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
Brian Salomon289e3d82016-12-14 15:52:56 -0500477 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
478 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800479 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
480 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000481 }
482
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400483 fragBuilder->codeAppendf("%s = float4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000484 }
485
robertphillips46d36f02015-01-18 08:14:14 -0800486 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500487 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700488 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800489 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
490 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700491 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700492 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000493 }
494
bsalomona624bf32016-09-20 09:12:47 -0700495 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
496 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800497 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700498
bsalomon31df31c2016-08-17 09:00:24 -0700499 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
500 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700501 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800502 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700503 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
504 }
bsalomona624bf32016-09-20 09:12:47 -0700505 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000506 }
507
508 private:
joshualitt5559ca22015-05-21 15:50:36 -0700509 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700510 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800511
egdaniele659a582015-11-13 09:55:43 -0800512 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513 };
514
joshualitt71c92602015-01-14 08:12:47 -0800515 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800516 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800517 const Attribute* fInEllipseOffsets0;
518 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500519 SkMatrix fViewMatrix;
520 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000521
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400522 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000523
joshualitt249af152014-09-15 11:41:13 -0700524 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000525};
526
bsalomoncdaa97b2016-03-08 08:30:14 -0800527GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000528
Hal Canary6f6961e2017-01-31 13:50:44 -0500529#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700530sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500531 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
532 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533}
Hal Canary6f6961e2017-01-31 13:50:44 -0500534#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000535
536///////////////////////////////////////////////////////////////////////////////
537
jvanverth6ca48822016-10-07 06:57:32 -0700538// We have two possible cases for geometry for a circle:
539
540// In the case of a normal fill, we draw geometry for the circle as an octagon.
541static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500542 // enter the octagon
543 // clang-format off
544 0, 1, 8, 1, 2, 8,
545 2, 3, 8, 3, 4, 8,
546 4, 5, 8, 5, 6, 8,
547 6, 7, 8, 7, 0, 8
548 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700549};
550
551// For stroked circles, we use two nested octagons.
552static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500553 // enter the octagon
554 // clang-format off
555 0, 1, 9, 0, 9, 8,
556 1, 2, 10, 1, 10, 9,
557 2, 3, 11, 2, 11, 10,
558 3, 4, 12, 3, 12, 11,
559 4, 5, 13, 4, 13, 12,
560 5, 6, 14, 5, 14, 13,
561 6, 7, 15, 6, 15, 14,
562 7, 0, 8, 7, 8, 15,
563 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700564};
565
Brian Salomon289e3d82016-12-14 15:52:56 -0500566
jvanverth6ca48822016-10-07 06:57:32 -0700567static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
568static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
569static const int kVertsPerStrokeCircle = 16;
570static const int kVertsPerFillCircle = 9;
571
572static int circle_type_to_vert_count(bool stroked) {
573 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
574}
575
576static int circle_type_to_index_count(bool stroked) {
577 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
578}
579
580static const uint16_t* circle_type_to_indices(bool stroked) {
581 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
582}
583
584///////////////////////////////////////////////////////////////////////////////
585
Brian Salomon05441c42017-05-15 16:45:49 -0400586class CircleOp final : public GrMeshDrawOp {
587private:
588 using Helper = GrSimpleMeshDrawOpHelper;
589
joshualitt76e7fb62015-02-11 08:52:27 -0800590public:
Brian Salomon25a88092016-12-01 09:36:50 -0500591 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700592
bsalomon4f3a0ca2016-08-22 13:14:26 -0700593 /** Optional extra params to render a partial arc rather than a full circle. */
594 struct ArcParams {
595 SkScalar fStartAngleRadians;
596 SkScalar fSweepAngleRadians;
597 bool fUseCenter;
598 };
Brian Salomon05441c42017-05-15 16:45:49 -0400599
600 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
601 SkPoint center, SkScalar radius, const GrStyle& style,
602 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700603 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700604 if (style.hasPathEffect()) {
605 return nullptr;
606 }
Brian Salomon05441c42017-05-15 16:45:49 -0400607 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700608 SkStrokeRec::Style recStyle = stroke.getStyle();
609 if (arcParams) {
610 // Arc support depends on the style.
611 switch (recStyle) {
612 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500613 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700614 return nullptr;
615 case SkStrokeRec::kFill_Style:
616 // This supports all fills.
617 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500618 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700619 case SkStrokeRec::kHairline_Style:
620 // Strokes that don't use the center point are supported with butt cap.
621 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
622 return nullptr;
623 }
624 break;
625 }
626 }
Brian Salomon05441c42017-05-15 16:45:49 -0400627 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
628 arcParams);
629 }
630
631 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
632 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
633 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
634 const SkStrokeRec& stroke = style.strokeRec();
635 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700636
bsalomon4b4a7cc2016-07-08 04:42:54 -0700637 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700638 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700639 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800640
Brian Salomon289e3d82016-12-14 15:52:56 -0500641 bool isStrokeOnly =
642 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700643 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700644
jvanverth6ca48822016-10-07 06:57:32 -0700645 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700646 SkScalar outerRadius = radius;
647 SkScalar halfWidth = 0;
648 if (hasStroke) {
649 if (SkScalarNearlyZero(strokeWidth)) {
650 halfWidth = SK_ScalarHalf;
651 } else {
652 halfWidth = SkScalarHalf(strokeWidth);
653 }
654
655 outerRadius += halfWidth;
656 if (isStrokeOnly) {
657 innerRadius = radius - halfWidth;
658 }
659 }
660
661 // The radii are outset for two reasons. First, it allows the shader to simply perform
662 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
663 // Second, the outer radius is used to compute the verts of the bounding box that is
664 // rendered and the outset ensures the box will cover all partially covered by the circle.
665 outerRadius += SK_ScalarHalf;
666 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700667 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400668 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700669
bsalomon4f3a0ca2016-08-22 13:14:26 -0700670 // This makes every point fully inside the intersection plane.
671 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
672 // This makes every point fully outside the union plane.
673 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
674 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
675 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700676 if (arcParams) {
677 // The shader operates in a space where the circle is translated to be centered at the
678 // origin. Here we compute points on the unit circle at the starting and ending angles.
679 SkPoint startPoint, stopPoint;
680 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
681 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
682 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -0400683
684 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
685 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
686 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
687 startPoint.normalize();
688 stopPoint.normalize();
689
690 // If the matrix included scale (on one axis) we need to swap our start and end points
691 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
692 SkTSwap(startPoint, stopPoint);
693 }
694
bsalomon4f3a0ca2016-08-22 13:14:26 -0700695 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
696 // radial lines. However, in both cases we have to be careful about the half-circle.
697 // case. In that case the two radial lines are equal and so that edge gets clipped
698 // twice. Since the shared edge goes through the center we fall back on the useCenter
699 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500700 bool useCenter =
701 (arcParams->fUseCenter || isStrokeOnly) &&
702 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700703 if (useCenter) {
704 SkVector norm0 = {startPoint.fY, -startPoint.fX};
705 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
706 if (arcParams->fSweepAngleRadians > 0) {
707 norm0.negate();
708 } else {
709 norm1.negate();
710 }
Brian Salomon05441c42017-05-15 16:45:49 -0400711 fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700712 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -0400713 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700714 color,
715 innerRadius,
716 outerRadius,
717 {norm0.fX, norm0.fY, 0.5f},
718 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
719 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700720 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500721 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400722 fClipPlaneIsect = false;
723 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700724 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400725 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700726 color,
727 innerRadius,
728 outerRadius,
729 {norm0.fX, norm0.fY, 0.5f},
730 {norm1.fX, norm1.fY, 0.5f},
731 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700732 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500733 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400734 fClipPlaneIsect = true;
735 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700736 }
737 } else {
738 // We clip to a secant of the original circle.
739 startPoint.scale(radius);
740 stopPoint.scale(radius);
741 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
742 norm.normalize();
743 if (arcParams->fSweepAngleRadians > 0) {
744 norm.negate();
745 }
746 SkScalar d = -norm.dot(startPoint) + 0.5f;
747
Brian Salomon05441c42017-05-15 16:45:49 -0400748 fCircles.emplace_back(
749 Circle{color,
750 innerRadius,
751 outerRadius,
752 {norm.fX, norm.fY, d},
753 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
754 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
755 devBounds,
756 stroked});
757 fClipPlane = true;
758 fClipPlaneIsect = false;
759 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700760 }
761 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400762 fCircles.emplace_back(
763 Circle{color,
764 innerRadius,
765 outerRadius,
766 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
767 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
768 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
769 devBounds,
770 stroked});
771 fClipPlane = false;
772 fClipPlaneIsect = false;
773 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700774 }
bsalomon88cf17d2016-07-08 06:40:56 -0700775 // Use the original radius and stroke radius for the bounds so that it does not include the
776 // AA bloat.
777 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -0400778 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -0500779 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
780 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -0400781 fVertCount = circle_type_to_vert_count(stroked);
782 fIndexCount = circle_type_to_index_count(stroked);
783 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -0800784 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700785
Brian Salomon289e3d82016-12-14 15:52:56 -0500786 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800787
robertphillipse004bfc2015-11-16 09:06:59 -0800788 SkString dumpInfo() const override {
789 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -0400790 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500791 string.appendf(
792 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
793 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -0400794 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
795 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
796 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800797 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -0400798 string += fHelper.dumpInfo();
799 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -0800800 return string;
801 }
802
Brian Salomonf86d37b2017-06-16 10:04:34 -0400803 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon05441c42017-05-15 16:45:49 -0400804 GrColor* color = &fCircles.front().fColor;
805 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
806 color);
807 }
808
809 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
810
bsalomone46f9fe2015-08-18 06:05:14 -0700811private:
Brian Salomon91326c32017-08-09 16:02:19 -0400812 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800813 SkMatrix localMatrix;
814 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800815 return;
816 }
817
818 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500819 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
820 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700821
822 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500823 SkPoint fPos;
824 GrColor fColor;
825 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700826 SkScalar fOuterRadius;
827 SkScalar fInnerRadius;
828 // These planes may or may not be present in the vertex buffer.
829 SkScalar fHalfPlanes[3][3];
830 };
joshualitt76e7fb62015-02-11 08:52:27 -0800831
joshualitt76e7fb62015-02-11 08:52:27 -0800832 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500833 SkASSERT(vertexStride ==
834 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
835 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
836 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700837
838 const GrBuffer* vertexBuffer;
839 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500840 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
841 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700842 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700843 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800844 return;
845 }
846
jvanverth6ca48822016-10-07 06:57:32 -0700847 const GrBuffer* indexBuffer = nullptr;
848 int firstIndex = 0;
849 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
850 if (!indices) {
851 SkDebugf("Could not allocate indices\n");
852 return;
853 }
854
855 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -0400856 for (const auto& circle : fCircles) {
857 SkScalar innerRadius = circle.fInnerRadius;
858 SkScalar outerRadius = circle.fOuterRadius;
859 GrColor color = circle.fColor;
860 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800861
Brian Salomon289e3d82016-12-14 15:52:56 -0500862 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
863 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
864 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
865 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
866 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
867 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
868 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
869 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800870
871 // The inner radius in the vertex data must be specified in normalized space.
872 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700873
874 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500875 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700876 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500877
Brian Salomon289e3d82016-12-14 15:52:56 -0500878 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700879 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700880 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700881 v0->fOuterRadius = outerRadius;
882 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800883
Brian Salomon289e3d82016-12-14 15:52:56 -0500884 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700885 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700886 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700887 v1->fOuterRadius = outerRadius;
888 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800889
Brian Salomon289e3d82016-12-14 15:52:56 -0500890 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700891 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700892 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700893 v2->fOuterRadius = outerRadius;
894 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800895
Brian Salomon289e3d82016-12-14 15:52:56 -0500896 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700897 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700898 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700899 v3->fOuterRadius = outerRadius;
900 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800901
Brian Salomon289e3d82016-12-14 15:52:56 -0500902 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700903 v4->fColor = color;
904 v4->fOffset = SkPoint::Make(octOffset, 1);
905 v4->fOuterRadius = outerRadius;
906 v4->fInnerRadius = innerRadius;
907
Brian Salomon289e3d82016-12-14 15:52:56 -0500908 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700909 v5->fColor = color;
910 v5->fOffset = SkPoint::Make(-octOffset, 1);
911 v5->fOuterRadius = outerRadius;
912 v5->fInnerRadius = innerRadius;
913
Brian Salomon289e3d82016-12-14 15:52:56 -0500914 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700915 v6->fColor = color;
916 v6->fOffset = SkPoint::Make(-1, octOffset);
917 v6->fOuterRadius = outerRadius;
918 v6->fInnerRadius = innerRadius;
919
Brian Salomon289e3d82016-12-14 15:52:56 -0500920 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700921 v7->fColor = color;
922 v7->fOffset = SkPoint::Make(-1, -octOffset);
923 v7->fOuterRadius = outerRadius;
924 v7->fInnerRadius = innerRadius;
925
bsalomon4f3a0ca2016-08-22 13:14:26 -0700926 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -0400927 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
928 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
929 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
930 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
931 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
932 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
933 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
934 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700935 }
936 int unionIdx = 1;
937 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -0400938 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
939 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
940 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
941 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
942 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
943 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
944 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
945 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700946 unionIdx = 2;
947 }
948 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -0400949 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
950 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
951 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
952 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
953 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
954 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
955 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
956 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700957 }
jvanverth6ca48822016-10-07 06:57:32 -0700958
Brian Salomon05441c42017-05-15 16:45:49 -0400959 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -0700960 // compute the inner ring
961 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
962 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
963 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
964 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
965 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
966 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
967 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
968 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
969
970 // cosine and sine of pi/8
971 SkScalar c = 0.923579533f;
972 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -0400973 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700974
Brian Salomon289e3d82016-12-14 15:52:56 -0500975 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700976 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500977 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700978 v0->fOuterRadius = outerRadius;
979 v0->fInnerRadius = innerRadius;
980
Brian Salomon289e3d82016-12-14 15:52:56 -0500981 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700982 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500983 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700984 v1->fOuterRadius = outerRadius;
985 v1->fInnerRadius = innerRadius;
986
Brian Salomon289e3d82016-12-14 15:52:56 -0500987 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700988 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500989 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700990 v2->fOuterRadius = outerRadius;
991 v2->fInnerRadius = innerRadius;
992
Brian Salomon289e3d82016-12-14 15:52:56 -0500993 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700994 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500995 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700996 v3->fOuterRadius = outerRadius;
997 v3->fInnerRadius = innerRadius;
998
Brian Salomon289e3d82016-12-14 15:52:56 -0500999 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001000 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001001 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001002 v4->fOuterRadius = outerRadius;
1003 v4->fInnerRadius = innerRadius;
1004
Brian Salomon289e3d82016-12-14 15:52:56 -05001005 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001006 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001007 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001008 v5->fOuterRadius = outerRadius;
1009 v5->fInnerRadius = innerRadius;
1010
Brian Salomon289e3d82016-12-14 15:52:56 -05001011 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001012 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001013 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001014 v6->fOuterRadius = outerRadius;
1015 v6->fInnerRadius = innerRadius;
1016
Brian Salomon289e3d82016-12-14 15:52:56 -05001017 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001018 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001019 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001020 v7->fOuterRadius = outerRadius;
1021 v7->fInnerRadius = innerRadius;
1022
1023 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001024 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1025 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1026 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1027 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1028 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1029 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1030 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1031 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001032 }
1033 int unionIdx = 1;
1034 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001035 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1036 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1037 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1038 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1039 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1040 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1041 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1042 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001043 unionIdx = 2;
1044 }
1045 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001046 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1047 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1048 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1049 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1050 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1051 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1052 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1053 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001054 }
1055 } else {
1056 // filled
1057 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1058 v8->fPos = center;
1059 v8->fColor = color;
1060 v8->fOffset = SkPoint::Make(0, 0);
1061 v8->fOuterRadius = outerRadius;
1062 v8->fInnerRadius = innerRadius;
1063 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001064 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001065 }
1066 int unionIdx = 1;
1067 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001068 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001069 unionIdx = 2;
1070 }
1071 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001072 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001073 }
1074 }
1075
Brian Salomon05441c42017-05-15 16:45:49 -04001076 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1077 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001078 for (int i = 0; i < primIndexCount; ++i) {
1079 *indices++ = primIndices[i] + currStartVertex;
1080 }
1081
Brian Salomon05441c42017-05-15 16:45:49 -04001082 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1083 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001084 }
jvanverth6ca48822016-10-07 06:57:32 -07001085
Chris Dalton3809bab2017-06-13 10:55:06 -06001086 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06001087 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1088 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001089 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001090 }
1091
Brian Salomon25a88092016-12-01 09:36:50 -05001092 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001093 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001094
1095 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001096 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001097 return false;
1098 }
1099
Brian Salomon05441c42017-05-15 16:45:49 -04001100 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001101 return false;
1102 }
1103
Brian Salomon05441c42017-05-15 16:45:49 -04001104 if (fHelper.usesLocalCoords() &&
1105 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001106 return false;
1107 }
1108
Brian Salomon289e3d82016-12-14 15:52:56 -05001109 // Because we've set up the ops that don't use the planes with noop values
1110 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001111 fClipPlane |= that->fClipPlane;
1112 fClipPlaneIsect |= that->fClipPlaneIsect;
1113 fClipPlaneUnion |= that->fClipPlaneUnion;
1114
Brian Salomon05441c42017-05-15 16:45:49 -04001115 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001116 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001117 fVertCount += that->fVertCount;
1118 fIndexCount += that->fIndexCount;
1119 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001120 return true;
1121 }
1122
Brian Salomon05441c42017-05-15 16:45:49 -04001123 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001124 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001125 SkScalar fInnerRadius;
1126 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001127 SkScalar fClipPlane[3];
1128 SkScalar fIsectPlane[3];
1129 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001130 SkRect fDevBounds;
1131 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001132 };
1133
Brian Salomon289e3d82016-12-14 15:52:56 -05001134 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001135 Helper fHelper;
1136 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001137 int fVertCount;
1138 int fIndexCount;
1139 bool fAllFill;
1140 bool fClipPlane;
1141 bool fClipPlaneIsect;
1142 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001143
Brian Salomon05441c42017-05-15 16:45:49 -04001144 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001145};
1146
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001147///////////////////////////////////////////////////////////////////////////////
1148
Brian Salomon05441c42017-05-15 16:45:49 -04001149class EllipseOp : public GrMeshDrawOp {
1150private:
1151 using Helper = GrSimpleMeshDrawOpHelper;
1152
1153 struct DeviceSpaceParams {
1154 SkPoint fCenter;
1155 SkScalar fXRadius;
1156 SkScalar fYRadius;
1157 SkScalar fInnerXRadius;
1158 SkScalar fInnerYRadius;
1159 };
1160
joshualitt76e7fb62015-02-11 08:52:27 -08001161public:
Brian Salomon25a88092016-12-01 09:36:50 -05001162 DEFINE_OP_CLASS_ID
Brian Salomon05441c42017-05-15 16:45:49 -04001163 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1164 const SkRect& ellipse, const SkStrokeRec& stroke) {
1165 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001166 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001167 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1168 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001169 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1170 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001171 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1172 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1173 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1174 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001175
bsalomon4b4a7cc2016-07-08 04:42:54 -07001176 // do (potentially) anisotropic mapping of stroke
1177 SkVector scaledStroke;
1178 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001179 scaledStroke.fX = SkScalarAbs(
1180 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1181 scaledStroke.fY = SkScalarAbs(
1182 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001183
1184 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001185 bool isStrokeOnly =
1186 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001187 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1188
Brian Salomon05441c42017-05-15 16:45:49 -04001189 params.fInnerXRadius = 0;
1190 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001191 if (hasStroke) {
1192 if (SkScalarNearlyZero(scaledStroke.length())) {
1193 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1194 } else {
1195 scaledStroke.scale(SK_ScalarHalf);
1196 }
1197
1198 // we only handle thick strokes for near-circular ellipses
1199 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001200 (0.5f * params.fXRadius > params.fYRadius ||
1201 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001202 return nullptr;
1203 }
1204
1205 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001206 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1207 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1208 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1209 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001210 return nullptr;
1211 }
1212
1213 // this is legit only if scale & translation (which should be the case at the moment)
1214 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001215 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1216 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001217 }
1218
Brian Salomon05441c42017-05-15 16:45:49 -04001219 params.fXRadius += scaledStroke.fX;
1220 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001221 }
Brian Salomon05441c42017-05-15 16:45:49 -04001222 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
1223 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001224
Brian Salomon05441c42017-05-15 16:45:49 -04001225 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1226 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1227 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1228 SkStrokeRec::Style style = stroke.getStyle();
1229 bool isStrokeOnly =
1230 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001231
Brian Salomon05441c42017-05-15 16:45:49 -04001232 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1233 params.fInnerXRadius, params.fInnerYRadius,
1234 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1235 params.fCenter.fY - params.fYRadius,
1236 params.fCenter.fX + params.fXRadius,
1237 params.fCenter.fY + params.fYRadius)});
1238
1239 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001240
bsalomon4b4a7cc2016-07-08 04:42:54 -07001241 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001242 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001243
Brian Salomon05441c42017-05-15 16:45:49 -04001244 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1245 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001246 }
joshualitt76e7fb62015-02-11 08:52:27 -08001247
Brian Salomon289e3d82016-12-14 15:52:56 -05001248 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001249
Brian Salomon7c3e7182016-12-01 09:35:30 -05001250 SkString dumpInfo() const override {
1251 SkString string;
1252 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001253 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001254 string.appendf(
1255 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1256 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1257 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1258 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1259 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001260 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001261 string += fHelper.dumpInfo();
1262 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001263 return string;
1264 }
1265
Brian Salomonf86d37b2017-06-16 10:04:34 -04001266 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001267 GrColor* color = &fEllipses.front().fColor;
1268 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1269 color);
1270 }
1271
1272 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1273
bsalomone46f9fe2015-08-18 06:05:14 -07001274private:
Brian Salomon91326c32017-08-09 16:02:19 -04001275 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001276 SkMatrix localMatrix;
1277 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001278 return;
1279 }
1280
1281 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001282 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001283
bsalomonb5238a72015-05-05 07:49:49 -07001284 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001285 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001286 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon05441c42017-05-15 16:45:49 -04001287 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1288 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001289 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001290 return;
1291 }
1292
Brian Salomon05441c42017-05-15 16:45:49 -04001293 for (const auto& ellipse : fEllipses) {
1294 GrColor color = ellipse.fColor;
1295 SkScalar xRadius = ellipse.fXRadius;
1296 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001297
1298 // Compute the reciprocals of the radii here to save time in the shader
1299 SkScalar xRadRecip = SkScalarInvert(xRadius);
1300 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001301 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1302 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001303
vjiaoblack977996d2016-06-30 12:20:54 -07001304 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1305 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1306 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1307
joshualitt76e7fb62015-02-11 08:52:27 -08001308 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001309 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001310 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001311 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001312 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1313 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1314
Brian Salomon05441c42017-05-15 16:45:49 -04001315 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001316 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001317 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001318 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1319 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1320
Brian Salomon05441c42017-05-15 16:45:49 -04001321 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001322 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001323 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001324 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1325 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1326
Brian Salomon05441c42017-05-15 16:45:49 -04001327 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001328 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001329 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001330 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1331 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1332
bsalomonb5238a72015-05-05 07:49:49 -07001333 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001334 }
Brian Salomon05441c42017-05-15 16:45:49 -04001335 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001336 }
1337
Brian Salomon25a88092016-12-01 09:36:50 -05001338 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001339 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001340
Brian Salomon05441c42017-05-15 16:45:49 -04001341 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001342 return false;
1343 }
1344
bsalomoncdaa97b2016-03-08 08:30:14 -08001345 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001346 return false;
1347 }
1348
Brian Salomon05441c42017-05-15 16:45:49 -04001349 if (fHelper.usesLocalCoords() &&
1350 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001351 return false;
1352 }
1353
Brian Salomon05441c42017-05-15 16:45:49 -04001354 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001355 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001356 return true;
1357 }
1358
Brian Salomon05441c42017-05-15 16:45:49 -04001359 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001360 GrColor fColor;
1361 SkScalar fXRadius;
1362 SkScalar fYRadius;
1363 SkScalar fInnerXRadius;
1364 SkScalar fInnerYRadius;
1365 SkRect fDevBounds;
1366 };
joshualitt76e7fb62015-02-11 08:52:27 -08001367
Brian Salomon289e3d82016-12-14 15:52:56 -05001368 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001369 Helper fHelper;
1370 bool fStroked;
1371 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001372
Brian Salomon05441c42017-05-15 16:45:49 -04001373 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001374};
1375
joshualitt76e7fb62015-02-11 08:52:27 -08001376/////////////////////////////////////////////////////////////////////////////////////////////////
1377
Brian Salomon05441c42017-05-15 16:45:49 -04001378class DIEllipseOp : public GrMeshDrawOp {
1379private:
1380 using Helper = GrSimpleMeshDrawOpHelper;
1381
1382 struct DeviceSpaceParams {
1383 SkPoint fCenter;
1384 SkScalar fXRadius;
1385 SkScalar fYRadius;
1386 SkScalar fInnerXRadius;
1387 SkScalar fInnerYRadius;
1388 DIEllipseStyle fStyle;
1389 };
1390
joshualitt76e7fb62015-02-11 08:52:27 -08001391public:
Brian Salomon25a88092016-12-01 09:36:50 -05001392 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001393
Brian Salomon05441c42017-05-15 16:45:49 -04001394 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1395 const SkRect& ellipse, const SkStrokeRec& stroke) {
1396 DeviceSpaceParams params;
1397 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1398 params.fXRadius = SkScalarHalf(ellipse.width());
1399 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001400
bsalomon4b4a7cc2016-07-08 04:42:54 -07001401 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04001402 params.fStyle = (SkStrokeRec::kStroke_Style == style)
1403 ? DIEllipseStyle::kStroke
1404 : (SkStrokeRec::kHairline_Style == style)
1405 ? DIEllipseStyle::kHairline
1406 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001407
Brian Salomon05441c42017-05-15 16:45:49 -04001408 params.fInnerXRadius = 0;
1409 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001410 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1411 SkScalar strokeWidth = stroke.getWidth();
1412
1413 if (SkScalarNearlyZero(strokeWidth)) {
1414 strokeWidth = SK_ScalarHalf;
1415 } else {
1416 strokeWidth *= SK_ScalarHalf;
1417 }
1418
1419 // we only handle thick strokes for near-circular ellipses
1420 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001421 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1422 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001423 return nullptr;
1424 }
1425
1426 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001427 if (strokeWidth * (params.fYRadius * params.fYRadius) <
1428 (strokeWidth * strokeWidth) * params.fXRadius) {
1429 return nullptr;
1430 }
1431 if (strokeWidth * (params.fXRadius * params.fXRadius) <
1432 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001433 return nullptr;
1434 }
1435
1436 // set inner radius (if needed)
1437 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04001438 params.fInnerXRadius = params.fXRadius - strokeWidth;
1439 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001440 }
1441
Brian Salomon05441c42017-05-15 16:45:49 -04001442 params.fXRadius += strokeWidth;
1443 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001444 }
Brian Salomon05441c42017-05-15 16:45:49 -04001445 if (DIEllipseStyle::kStroke == params.fStyle &&
1446 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1447 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001448 }
Brian Salomon05441c42017-05-15 16:45:49 -04001449 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
1450 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001451
Brian Salomon05441c42017-05-15 16:45:49 -04001452 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
1453 const SkMatrix& viewMatrix)
1454 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001455 // This expands the outer rect so that after CTM we end up with a half-pixel border
1456 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1457 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1458 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1459 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001460 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1461 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001462
Brian Salomon05441c42017-05-15 16:45:49 -04001463 fEllipses.emplace_back(
1464 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1465 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1466 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1467 params.fCenter.fY - params.fYRadius - geoDy,
1468 params.fCenter.fX + params.fXRadius + geoDx,
1469 params.fCenter.fY + params.fYRadius + geoDy)});
1470 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1471 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08001472 }
1473
Brian Salomon289e3d82016-12-14 15:52:56 -05001474 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001475
Brian Salomon7c3e7182016-12-01 09:35:30 -05001476 SkString dumpInfo() const override {
1477 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001478 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001479 string.appendf(
1480 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1481 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1482 "GeoDY: %.2f\n",
1483 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1484 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1485 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001486 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001487 string += fHelper.dumpInfo();
1488 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001489 return string;
1490 }
1491
Brian Salomonf86d37b2017-06-16 10:04:34 -04001492 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001493 GrColor* color = &fEllipses.front().fColor;
1494 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1495 color);
1496 }
1497
1498 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1499
bsalomone46f9fe2015-08-18 06:05:14 -07001500private:
Brian Salomon91326c32017-08-09 16:02:19 -04001501 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001502 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001503 sk_sp<GrGeometryProcessor> gp(
1504 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001505
joshualitt76e7fb62015-02-11 08:52:27 -08001506 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001507 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001508 QuadHelper helper;
1509 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon05441c42017-05-15 16:45:49 -04001510 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001511 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001512 return;
1513 }
1514
Brian Salomon05441c42017-05-15 16:45:49 -04001515 for (const auto& ellipse : fEllipses) {
1516 GrColor color = ellipse.fColor;
1517 SkScalar xRadius = ellipse.fXRadius;
1518 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001519
Brian Salomon05441c42017-05-15 16:45:49 -04001520 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001521
1522 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04001523 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
1524 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001525
Brian Salomon05441c42017-05-15 16:45:49 -04001526 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
1527 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001528
1529 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001530 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001531 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1532 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1533
Brian Salomon289e3d82016-12-14 15:52:56 -05001534 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001535 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001536 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1537 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1538
1539 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001540 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001541 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1542 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1543
1544 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001545 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001546 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1547 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1548
bsalomonb5238a72015-05-05 07:49:49 -07001549 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001550 }
Brian Salomon05441c42017-05-15 16:45:49 -04001551 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001552 }
halcanary9d524f22016-03-29 09:03:52 -07001553
Brian Salomon25a88092016-12-01 09:36:50 -05001554 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001555 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04001556 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001557 return false;
1558 }
1559
bsalomoncdaa97b2016-03-08 08:30:14 -08001560 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001561 return false;
1562 }
1563
joshualittd96a67b2015-05-05 14:09:05 -07001564 // TODO rewrite to allow positioning on CPU
1565 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001566 return false;
1567 }
1568
Brian Salomon05441c42017-05-15 16:45:49 -04001569 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001570 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001571 return true;
1572 }
1573
Brian Salomon05441c42017-05-15 16:45:49 -04001574 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
1575 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001576
Brian Salomon05441c42017-05-15 16:45:49 -04001577 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001578 SkMatrix fViewMatrix;
1579 GrColor fColor;
1580 SkScalar fXRadius;
1581 SkScalar fYRadius;
1582 SkScalar fInnerXRadius;
1583 SkScalar fInnerYRadius;
1584 SkScalar fGeoDx;
1585 SkScalar fGeoDy;
1586 DIEllipseStyle fStyle;
1587 SkRect fBounds;
1588 };
1589
Brian Salomon05441c42017-05-15 16:45:49 -04001590 Helper fHelper;
1591 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001592
Brian Salomon05441c42017-05-15 16:45:49 -04001593 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001594};
1595
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001596///////////////////////////////////////////////////////////////////////////////
1597
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001598// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001599//
1600// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1601// ____________
1602// |_|________|_|
1603// | | | |
1604// | | | |
1605// | | | |
1606// |_|________|_|
1607// |_|________|_|
1608//
1609// For strokes, we don't draw the center quad.
1610//
1611// For circular roundrects, in the case where the stroke width is greater than twice
1612// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001613// in the center. The shared vertices are duplicated so we can set a different outer radius
1614// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001615// ____________
1616// |_|________|_|
1617// | |\ ____ /| |
1618// | | | | | |
1619// | | |____| | |
1620// |_|/______\|_|
1621// |_|________|_|
1622//
1623// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001624//
1625// For filled rrects that need to provide a distance vector we resuse the overstroke
1626// geometry but make the inner rect degenerate (either a point or a horizontal or
1627// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001628
jvanverth84839f62016-08-29 10:16:40 -07001629static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001630 // clang-format off
1631 // overstroke quads
1632 // we place this at the beginning so that we can skip these indices when rendering normally
1633 16, 17, 19, 16, 19, 18,
1634 19, 17, 23, 19, 23, 21,
1635 21, 23, 22, 21, 22, 20,
1636 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001637
Brian Salomon289e3d82016-12-14 15:52:56 -05001638 // corners
1639 0, 1, 5, 0, 5, 4,
1640 2, 3, 7, 2, 7, 6,
1641 8, 9, 13, 8, 13, 12,
1642 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001643
Brian Salomon289e3d82016-12-14 15:52:56 -05001644 // edges
1645 1, 2, 6, 1, 6, 5,
1646 4, 5, 9, 4, 9, 8,
1647 6, 7, 11, 6, 11, 10,
1648 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001649
Brian Salomon289e3d82016-12-14 15:52:56 -05001650 // center
1651 // we place this at the end so that we can ignore these indices when not rendering as filled
1652 5, 6, 10, 5, 10, 9,
1653 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001654};
Brian Salomon289e3d82016-12-14 15:52:56 -05001655
jvanverth84839f62016-08-29 10:16:40 -07001656// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001657static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001658
jvanverth84839f62016-08-29 10:16:40 -07001659// overstroke count is arraysize minus the center indices
1660static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1661// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001662static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001663// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001664static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1665static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001666static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001667
jvanverthc3d0e422016-08-25 08:12:35 -07001668enum RRectType {
1669 kFill_RRectType,
1670 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001671 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07001672};
1673
jvanverth84839f62016-08-29 10:16:40 -07001674static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001675 switch (type) {
1676 case kFill_RRectType:
1677 case kStroke_RRectType:
1678 return kVertsPerStandardRRect;
1679 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001680 return kVertsPerOverstrokeRRect;
1681 }
1682 SkFAIL("Invalid type");
1683 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001684}
1685
1686static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001687 switch (type) {
1688 case kFill_RRectType:
1689 return kIndicesPerFillRRect;
1690 case kStroke_RRectType:
1691 return kIndicesPerStrokeRRect;
1692 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001693 return kIndicesPerOverstrokeRRect;
1694 }
1695 SkFAIL("Invalid type");
1696 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001697}
1698
1699static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001700 switch (type) {
1701 case kFill_RRectType:
1702 case kStroke_RRectType:
1703 return gStandardRRectIndices;
1704 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001705 return gOverstrokeRRectIndices;
1706 }
1707 SkFAIL("Invalid type");
1708 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001709}
1710
joshualitt76e7fb62015-02-11 08:52:27 -08001711///////////////////////////////////////////////////////////////////////////////////////////////////
1712
Robert Phillips79839d42016-10-06 15:03:34 -04001713// For distance computations in the interior of filled rrects we:
1714//
1715// add a interior degenerate (point or line) rect
1716// each vertex of that rect gets -outerRad as its radius
1717// this makes the computation of the distance to the outer edge be negative
1718// negative values are caught and then handled differently in the GP's onEmitCode
1719// each vertex is also given the normalized x & y distance from the interior rect's edge
1720// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1721
Brian Salomon05441c42017-05-15 16:45:49 -04001722class CircularRRectOp : public GrMeshDrawOp {
1723private:
1724 using Helper = GrSimpleMeshDrawOpHelper;
1725
joshualitt76e7fb62015-02-11 08:52:27 -08001726public:
Brian Salomon25a88092016-12-01 09:36:50 -05001727 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001728
bsalomon4b4a7cc2016-07-08 04:42:54 -07001729 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1730 // whether the rrect is only stroked or stroked and filled.
Brian Salomone23bffd2017-06-02 11:01:10 -04001731 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1732 const SkRect& devRect, float devRadius,
1733 float devStrokeWidth, bool strokeOnly) {
1734 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
1735 devRadius, devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04001736 }
Brian Salomone23bffd2017-06-02 11:01:10 -04001737 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1738 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04001739 : INHERITED(ClassID())
1740 , fViewMatrixIfUsingLocalCoords(viewMatrix)
1741 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001742 SkRect bounds = devRect;
1743 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1744 SkScalar innerRadius = 0.0f;
1745 SkScalar outerRadius = devRadius;
1746 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001747 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001748 if (devStrokeWidth > 0) {
1749 if (SkScalarNearlyZero(devStrokeWidth)) {
1750 halfWidth = SK_ScalarHalf;
1751 } else {
1752 halfWidth = SkScalarHalf(devStrokeWidth);
1753 }
joshualitt76e7fb62015-02-11 08:52:27 -08001754
bsalomon4b4a7cc2016-07-08 04:42:54 -07001755 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001756 // Outset stroke by 1/4 pixel
1757 devStrokeWidth += 0.25f;
1758 // If stroke is greater than width or height, this is still a fill
1759 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001760 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001761 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001762 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001763 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001764 }
1765 outerRadius += halfWidth;
1766 bounds.outset(halfWidth, halfWidth);
1767 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001768
bsalomon4b4a7cc2016-07-08 04:42:54 -07001769 // The radii are outset for two reasons. First, it allows the shader to simply perform
1770 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1771 // Second, the outer radius is used to compute the verts of the bounding box that is
1772 // rendered and the outset ensures the box will cover all partially covered by the rrect
1773 // corners.
1774 outerRadius += SK_ScalarHalf;
1775 innerRadius -= SK_ScalarHalf;
1776
bsalomon88cf17d2016-07-08 06:40:56 -07001777 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1778
1779 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001780 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1781
Brian Salomon05441c42017-05-15 16:45:49 -04001782 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001783 fVertCount = rrect_type_to_vert_count(type);
1784 fIndexCount = rrect_type_to_index_count(type);
1785 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001786 }
1787
Brian Salomon289e3d82016-12-14 15:52:56 -05001788 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001789
jvanverthc3d0e422016-08-25 08:12:35 -07001790 SkString dumpInfo() const override {
1791 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001792 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001793 string.appendf(
1794 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1795 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001796 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
1797 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
1798 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001799 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001800 string += fHelper.dumpInfo();
1801 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07001802 return string;
1803 }
1804
Brian Salomonf86d37b2017-06-16 10:04:34 -04001805 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001806 GrColor* color = &fRRects.front().fColor;
1807 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1808 color);
1809 }
1810
1811 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1812
Brian Salomon92aee3d2016-12-21 09:20:25 -05001813private:
Robert Phillips79839d42016-10-06 15:03:34 -04001814 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001815 SkPoint fPos;
1816 GrColor fColor;
1817 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001818 SkScalar fOuterRadius;
1819 SkScalar fInnerRadius;
1820 // No half plane, we don't use it here.
1821 };
1822
Brian Salomon289e3d82016-12-14 15:52:56 -05001823 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1824 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1825 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001826 SkASSERT(smInset < bigInset);
1827
1828 // TL
1829 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1830 (*verts)->fColor = color;
1831 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1832 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001833 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001834 (*verts)++;
1835
1836 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001837 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001838 (*verts)->fColor = color;
1839 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1840 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001841 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001842 (*verts)++;
1843
Brian Salomon289e3d82016-12-14 15:52:56 -05001844 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001845 (*verts)->fColor = color;
1846 (*verts)->fOffset = SkPoint::Make(0, 0);
1847 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001848 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001849 (*verts)++;
1850
Brian Salomon289e3d82016-12-14 15:52:56 -05001851 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001852 (*verts)->fColor = color;
1853 (*verts)->fOffset = SkPoint::Make(0, 0);
1854 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001855 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001856 (*verts)++;
1857
1858 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1859 (*verts)->fColor = color;
1860 (*verts)->fOffset = SkPoint::Make(0, 0);
1861 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001862 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001863 (*verts)++;
1864
1865 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1866 (*verts)->fColor = color;
1867 (*verts)->fOffset = SkPoint::Make(0, 0);
1868 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001869 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001870 (*verts)++;
1871
1872 // BL
1873 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1874 (*verts)->fColor = color;
1875 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1876 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001877 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001878 (*verts)++;
1879
1880 // BR
1881 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1882 (*verts)->fColor = color;
1883 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1884 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001885 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001886 (*verts)++;
1887 }
1888
Brian Salomon91326c32017-08-09 16:02:19 -04001889 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001890 // Invert the view matrix as a local matrix (if any other processors require coords).
1891 SkMatrix localMatrix;
1892 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001893 return;
1894 }
1895
1896 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001897 sk_sp<GrGeometryProcessor> gp(
1898 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001899
joshualitt76e7fb62015-02-11 08:52:27 -08001900 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001901 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001902
jvanverth84839f62016-08-29 10:16:40 -07001903 const GrBuffer* vertexBuffer;
1904 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001905
Brian Salomon289e3d82016-12-14 15:52:56 -05001906 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1907 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001908 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001909 SkDebugf("Could not allocate vertices\n");
1910 return;
1911 }
1912
jvanverth84839f62016-08-29 10:16:40 -07001913 const GrBuffer* indexBuffer = nullptr;
1914 int firstIndex = 0;
1915 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1916 if (!indices) {
1917 SkDebugf("Could not allocate indices\n");
1918 return;
1919 }
1920
1921 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001922 for (const auto& rrect : fRRects) {
1923 GrColor color = rrect.fColor;
1924 SkScalar outerRadius = rrect.fOuterRadius;
1925 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001926
Brian Salomon289e3d82016-12-14 15:52:56 -05001927 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1928 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001929
Brian Salomon289e3d82016-12-14 15:52:56 -05001930 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001931 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001932 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04001933 SkScalar innerRadius = rrect.fType != kFill_RRectType
1934 ? rrect.fInnerRadius / rrect.fOuterRadius
1935 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001936 for (int i = 0; i < 4; ++i) {
1937 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001938 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001939 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1940 verts->fOuterRadius = outerRadius;
1941 verts->fInnerRadius = innerRadius;
1942 verts++;
1943
1944 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001945 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001946 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1947 verts->fOuterRadius = outerRadius;
1948 verts->fInnerRadius = innerRadius;
1949 verts++;
1950
1951 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001952 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001953 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1954 verts->fOuterRadius = outerRadius;
1955 verts->fInnerRadius = innerRadius;
1956 verts++;
1957
1958 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001959 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001960 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1961 verts->fOuterRadius = outerRadius;
1962 verts->fInnerRadius = innerRadius;
1963 verts++;
1964 }
jvanverthc3d0e422016-08-25 08:12:35 -07001965 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001966 // Effectively this is an additional stroked rrect, with its
1967 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1968 // This will give us correct AA in the center and the correct
1969 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001970 //
jvanvertha4f1af82016-08-29 07:17:47 -07001971 // Also, the outer offset is a constant vector pointing to the right, which
1972 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04001973 if (kOverstroke_RRectType == rrect.fType) {
1974 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04001975
Brian Salomon05441c42017-05-15 16:45:49 -04001976 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001977 // this is the normalized distance from the outer rectangle of this
1978 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04001979 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001980
Brian Salomon289e3d82016-12-14 15:52:56 -05001981 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04001982 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04001983 }
jvanverth6a397612016-08-26 08:15:33 -07001984
Brian Salomon05441c42017-05-15 16:45:49 -04001985 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
1986 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07001987 for (int i = 0; i < primIndexCount; ++i) {
1988 *indices++ = primIndices[i] + currStartVertex;
1989 }
1990
Brian Salomon05441c42017-05-15 16:45:49 -04001991 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001992 }
1993
Chris Dalton3809bab2017-06-13 10:55:06 -06001994 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06001995 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1996 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001997 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001998 }
1999
Brian Salomon25a88092016-12-01 09:36:50 -05002000 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002001 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002002
2003 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002004 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002005 return false;
2006 }
2007
Brian Salomon05441c42017-05-15 16:45:49 -04002008 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002009 return false;
2010 }
2011
Brian Salomon05441c42017-05-15 16:45:49 -04002012 if (fHelper.usesLocalCoords() &&
2013 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002014 return false;
2015 }
2016
Brian Salomon05441c42017-05-15 16:45:49 -04002017 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002018 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002019 fVertCount += that->fVertCount;
2020 fIndexCount += that->fIndexCount;
2021 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002022 return true;
2023 }
2024
Brian Salomon05441c42017-05-15 16:45:49 -04002025 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002026 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002027 SkScalar fInnerRadius;
2028 SkScalar fOuterRadius;
2029 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002030 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002031 };
2032
Brian Salomon289e3d82016-12-14 15:52:56 -05002033 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002034 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002035 int fVertCount;
2036 int fIndexCount;
2037 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002038 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002039
Brian Salomon05441c42017-05-15 16:45:49 -04002040 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002041};
2042
jvanverth84839f62016-08-29 10:16:40 -07002043static const int kNumRRectsInIndexBuffer = 256;
2044
2045GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2046GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2047static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2048 GrResourceProvider* resourceProvider) {
2049 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2050 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2051 switch (type) {
2052 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002053 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002054 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2055 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002056 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002057 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002058 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2059 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002060 default:
2061 SkASSERT(false);
2062 return nullptr;
2063 };
2064}
2065
Brian Salomon05441c42017-05-15 16:45:49 -04002066class EllipticalRRectOp : public GrMeshDrawOp {
2067private:
2068 using Helper = GrSimpleMeshDrawOpHelper;
2069
joshualitt76e7fb62015-02-11 08:52:27 -08002070public:
Brian Salomon25a88092016-12-01 09:36:50 -05002071 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002072
bsalomon4b4a7cc2016-07-08 04:42:54 -07002073 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2074 // whether the rrect is only stroked or stroked and filled.
Brian Salomon05441c42017-05-15 16:45:49 -04002075 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
2076 const SkRect& devRect, float devXRadius, float devYRadius,
2077 SkVector devStrokeWidths, bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002078 SkASSERT(devXRadius > 0.5);
2079 SkASSERT(devYRadius > 0.5);
2080 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2081 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002082 if (devStrokeWidths.fX > 0) {
2083 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2084 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2085 } else {
2086 devStrokeWidths.scale(SK_ScalarHalf);
2087 }
joshualitt76e7fb62015-02-11 08:52:27 -08002088
bsalomon4b4a7cc2016-07-08 04:42:54 -07002089 // we only handle thick strokes for near-circular ellipses
2090 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002091 (SK_ScalarHalf * devXRadius > devYRadius ||
2092 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002093 return nullptr;
2094 }
2095
2096 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002097 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2098 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002099 return nullptr;
2100 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002101 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2102 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002103 return nullptr;
2104 }
Brian Salomon05441c42017-05-15 16:45:49 -04002105 }
2106 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
2107 devXRadius, devYRadius, devStrokeWidths,
2108 strokeOnly);
2109 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002110
Brian Salomon05441c42017-05-15 16:45:49 -04002111 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2112 const SkRect& devRect, float devXRadius, float devYRadius,
2113 SkVector devStrokeHalfWidths, bool strokeOnly)
2114 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
2115 SkScalar innerXRadius = 0.0f;
2116 SkScalar innerYRadius = 0.0f;
2117 SkRect bounds = devRect;
2118 bool stroked = false;
2119 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002120 // this is legit only if scale & translation (which should be the case at the moment)
2121 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002122 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2123 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002124 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2125 }
2126
Brian Salomon05441c42017-05-15 16:45:49 -04002127 devXRadius += devStrokeHalfWidths.fX;
2128 devYRadius += devStrokeHalfWidths.fY;
2129 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002130 }
2131
Brian Salomon05441c42017-05-15 16:45:49 -04002132 fStroked = stroked;
2133 fViewMatrixIfUsingLocalCoords = viewMatrix;
2134 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002135 // Expand the rect for aa in order to generate the correct vertices.
2136 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002137 fRRects.emplace_back(
2138 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002139 }
2140
Brian Salomon289e3d82016-12-14 15:52:56 -05002141 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002142
Brian Salomon7c3e7182016-12-01 09:35:30 -05002143 SkString dumpInfo() const override {
2144 SkString string;
2145 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002146 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002147 string.appendf(
2148 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2149 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2150 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2151 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2152 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002153 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002154 string += fHelper.dumpInfo();
2155 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002156 return string;
2157 }
2158
Brian Salomonf86d37b2017-06-16 10:04:34 -04002159 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002160 GrColor* color = &fRRects.front().fColor;
2161 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2162 color);
2163 }
2164
2165 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2166
bsalomone46f9fe2015-08-18 06:05:14 -07002167private:
Brian Salomon91326c32017-08-09 16:02:19 -04002168 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002169 SkMatrix localMatrix;
2170 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002171 return;
2172 }
2173
2174 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002175 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002176
joshualitt76e7fb62015-02-11 08:52:27 -08002177 size_t vertexStride = gp->getVertexStride();
2178 SkASSERT(vertexStride == sizeof(EllipseVertex));
2179
bsalomonb5238a72015-05-05 07:49:49 -07002180 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002181 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomon289e3d82016-12-14 15:52:56 -05002182 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2183 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002184
Chris Dalton3809bab2017-06-13 10:55:06 -06002185 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002186 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Chris Daltonbca46e22017-05-15 11:03:26 -06002187 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002188 indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002189 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002190 SkDebugf("Could not allocate vertices\n");
2191 return;
2192 }
2193
Brian Salomon05441c42017-05-15 16:45:49 -04002194 for (const auto& rrect : fRRects) {
2195 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002196 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002197 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2198 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2199 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2200 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002201
2202 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002203 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2204 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002205
Brian Salomon05441c42017-05-15 16:45:49 -04002206 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002207
Brian Salomon289e3d82016-12-14 15:52:56 -05002208 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2209 bounds.fBottom - yOuterRadius, bounds.fBottom};
2210 SkScalar yOuterOffsets[4] = {yOuterRadius,
2211 SK_ScalarNearlyZero, // we're using inversesqrt() in
2212 // shader, so can't be exactly 0
2213 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002214
2215 for (int i = 0; i < 4; ++i) {
2216 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002217 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002218 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2219 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2220 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2221 verts++;
2222
2223 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002224 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002225 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2226 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2227 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2228 verts++;
2229
2230 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002231 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002232 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2233 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2234 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2235 verts++;
2236
2237 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002238 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002239 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2240 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2241 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2242 verts++;
2243 }
2244 }
Brian Salomon05441c42017-05-15 16:45:49 -04002245 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002246 }
2247
Brian Salomon25a88092016-12-01 09:36:50 -05002248 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002249 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002250
Brian Salomon05441c42017-05-15 16:45:49 -04002251 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002252 return false;
2253 }
2254
bsalomoncdaa97b2016-03-08 08:30:14 -08002255 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002256 return false;
2257 }
2258
Brian Salomon05441c42017-05-15 16:45:49 -04002259 if (fHelper.usesLocalCoords() &&
2260 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002261 return false;
2262 }
2263
Brian Salomon05441c42017-05-15 16:45:49 -04002264 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002265 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002266 return true;
2267 }
2268
Brian Salomon05441c42017-05-15 16:45:49 -04002269 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002270 GrColor fColor;
2271 SkScalar fXRadius;
2272 SkScalar fYRadius;
2273 SkScalar fInnerXRadius;
2274 SkScalar fInnerYRadius;
2275 SkRect fDevBounds;
2276 };
2277
Brian Salomon289e3d82016-12-14 15:52:56 -05002278 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002279 Helper fHelper;
2280 bool fStroked;
2281 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002282
Brian Salomon05441c42017-05-15 16:45:49 -04002283 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002284};
2285
Brian Salomon05441c42017-05-15 16:45:49 -04002286static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002287 const SkMatrix& viewMatrix,
2288 const SkRRect& rrect,
2289 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002290 SkASSERT(viewMatrix.rectStaysRect());
2291 SkASSERT(rrect.isSimple());
2292 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002293
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002294 // RRect ops only handle simple, but not too simple, rrects.
2295 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002296 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002297 SkRect bounds;
2298 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002299
2300 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002301 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2302 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2303 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2304 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002305
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002306 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002307
bsalomon4b4a7cc2016-07-08 04:42:54 -07002308 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2309 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002310 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002311
Brian Salomon289e3d82016-12-14 15:52:56 -05002312 bool isStrokeOnly =
2313 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002314 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2315
jvanverthc3d0e422016-08-25 08:12:35 -07002316 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002317 if (hasStroke) {
2318 if (SkStrokeRec::kHairline_Style == style) {
2319 scaledStroke.set(1, 1);
2320 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002321 scaledStroke.fX = SkScalarAbs(
2322 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2323 scaledStroke.fY = SkScalarAbs(
2324 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002325 }
2326
jvanverthc3d0e422016-08-25 08:12:35 -07002327 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2328 // for non-circular rrects, if half of strokewidth is greater than radius,
2329 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002330 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2331 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002332 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002333 }
2334 }
2335
2336 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2337 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2338 // patch will have fractional coverage. This only matters when the interior is actually filled.
2339 // We could consider falling back to rect rendering here, since a tiny radius is
2340 // indistinguishable from a square corner.
2341 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002342 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002343 }
2344
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002345 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002346 if (isCircular) {
Brian Salomone23bffd2017-06-02 11:01:10 -04002347 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
2348 isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05002349 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002350 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04002351 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
2352 scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002353 }
joshualitt3e708c52015-04-30 13:49:27 -07002354}
2355
Brian Salomon05441c42017-05-15 16:45:49 -04002356std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002357 const SkMatrix& viewMatrix,
2358 const SkRRect& rrect,
2359 const SkStrokeRec& stroke,
2360 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002361 if (rrect.isOval()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002362 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002363 }
2364
2365 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002366 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002367 }
2368
Brian Salomone23bffd2017-06-02 11:01:10 -04002369 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002370}
joshualitt3e708c52015-04-30 13:49:27 -07002371
bsalomon4b4a7cc2016-07-08 04:42:54 -07002372///////////////////////////////////////////////////////////////////////////////
2373
Brian Salomon05441c42017-05-15 16:45:49 -04002374std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
2375 const SkMatrix& viewMatrix,
2376 const SkRect& oval,
2377 const SkStrokeRec& stroke,
2378 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002379 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002380 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04002381 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2382 circle_stays_circle(viewMatrix)) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002383 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon05441c42017-05-15 16:45:49 -04002384 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f,
2385 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002386 }
2387
Stan Ilieveb868aa2017-02-21 11:06:16 -05002388 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002389 if (viewMatrix.rectStaysRect()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002390 return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002391 }
2392
Stan Ilieveb868aa2017-02-21 11:06:16 -05002393 // Otherwise, if we have shader derivative support, render as device-independent
2394 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002395 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
Stan Ilieveb868aa2017-02-21 11:06:16 -05002396 }
2397
bsalomon4b4a7cc2016-07-08 04:42:54 -07002398 return nullptr;
2399}
2400
2401///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002402
Brian Salomon05441c42017-05-15 16:45:49 -04002403std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
2404 const SkRect& oval, SkScalar startAngle,
2405 SkScalar sweepAngle, bool useCenter,
2406 const GrStyle& style,
2407 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002408 SkASSERT(!oval.isEmpty());
2409 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002410 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002411 if (SkScalarAbs(sweepAngle) >= 360.f) {
2412 return nullptr;
2413 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002414 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2415 return nullptr;
2416 }
2417 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002418 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2419 useCenter};
Brian Salomon05441c42017-05-15 16:45:49 -04002420 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002421}
2422
2423///////////////////////////////////////////////////////////////////////////////
2424
Hal Canary6f6961e2017-01-31 13:50:44 -05002425#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002426
Brian Salomon05441c42017-05-15 16:45:49 -04002427GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002428 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002429 SkScalar rotate = random->nextSScalar1() * 360.f;
2430 SkScalar translateX = random->nextSScalar1() * 1000.f;
2431 SkScalar translateY = random->nextSScalar1() * 1000.f;
2432 SkScalar scale = random->nextSScalar1() * 100.f;
2433 SkMatrix viewMatrix;
2434 viewMatrix.setRotate(rotate);
2435 viewMatrix.postTranslate(translateX, translateY);
2436 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002437 SkRect circle = GrTest::TestSquare(random);
2438 SkPoint center = {circle.centerX(), circle.centerY()};
2439 SkScalar radius = circle.width() / 2.f;
2440 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002441 CircleOp::ArcParams arcParamsTmp;
2442 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002443 if (random->nextBool()) {
2444 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002445 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2446 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002447 arcParams = &arcParamsTmp;
2448 }
Brian Salomon05441c42017-05-15 16:45:49 -04002449 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
2450 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002451 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002452 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002453 }
2454 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002455}
2456
Brian Salomon05441c42017-05-15 16:45:49 -04002457GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002458 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07002459 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002460 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002461}
2462
Brian Salomon05441c42017-05-15 16:45:49 -04002463GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002464 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07002465 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002466 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002467}
2468
Brian Salomon05441c42017-05-15 16:45:49 -04002469GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002470 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07002471 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Brian Salomone23bffd2017-06-02 11:01:10 -04002472 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002473}
2474
2475#endif