blob: feeef4f6eba011553d2120758a82267985510a87 [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"
Brian Osmanf9aabff2018-11-13 16:11:38 -050016#include "GrVertexWriter.h"
Mike Reed242135a2018-02-22 13:41:39 -050017#include "SkRRectPriv.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000018#include "SkStrokeRec.h"
egdaniel2d721d32015-11-11 13:06:05 -080019#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080020#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070021#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080022#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080023#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050024#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060025#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050026#include "ops/GrMeshDrawOp.h"
Brian Salomon05441c42017-05-15 16:45:49 -040027#include "ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080028
Ben Wagnerf08d1d02018-06-18 15:11:00 -040029#include <utility>
30
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080032
Brian Salomon289e3d82016-12-14 15:52:56 -050033static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050034
Brian Osman2b6e3902018-11-21 15:29:43 -050035// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
36static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
37 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
38};
39
commit-bot@chromium.org81312832013-03-22 18:34:09 +000040}
41
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000042///////////////////////////////////////////////////////////////////////////////
43
44/**
bsalomonce1c8862014-12-15 07:11:22 -080045 * The output of this effect is a modulation of the input color and coverage for a circle. It
46 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080047 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080048 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec4f : (p.xy, outerRad, innerRad)
51 * p is the position in the normalized space.
52 * outerRad is the outerRadius in device space.
53 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070054 * Additional clip planes are supported for rendering circular arcs. The additional planes are
55 * either intersected or unioned together. Up to three planes are supported (an initial plane,
56 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050057 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070058 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040059 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
60 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000061 */
62
bsalomoncdaa97b2016-03-08 08:30:14 -080063class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000064public:
Brian Salomonea26d6b2018-01-23 20:33:21 +000065 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050066 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040067 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040068 , fLocalMatrix(localMatrix)
69 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050070 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050071 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050072 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
73
bsalomon4f3a0ca2016-08-22 13:14:26 -070074 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040075 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070076 }
77 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040078 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070079 }
80 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040081 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070082 }
Brian Salomon45c92202018-04-10 10:53:58 -040083 if (roundCaps) {
84 SkASSERT(stroke);
85 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -040086 fInRoundCapCenters =
87 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -040088 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -050089 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000090 }
91
Brian Salomond3b65972017-03-22 12:05:03 -040092 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000093
mtklein36352bf2015-03-25 18:17:31 -070094 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000095
Brian Salomon94efbf52016-11-29 13:43:05 -050096 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -070097 GLSLProcessor::GenKey(*this, caps, b);
98 }
99
Brian Salomon94efbf52016-11-29 13:43:05 -0500100 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700101 return new GLSLProcessor();
102 }
103
104private:
egdaniel57d3b032015-11-13 11:57:27 -0800105 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000106 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800107 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000108
Brian Salomon289e3d82016-12-14 15:52:56 -0500109 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800110 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800111 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800112 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800113 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700114 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800115
joshualittabb52a12015-01-13 15:02:10 -0800116 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800117 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400118 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500119 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400120 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400121 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700122 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
123 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400124 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400125 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700126 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
127 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400128 if (cgp.fInUnionPlane.isInitialized()) {
129 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400130 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
132 }
Brian Salomon45c92202018-04-10 10:53:58 -0400133 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400134 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400135 fragBuilder->codeAppend("float4 roundCapCenters;");
136 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
137 varyingHandler->addVarying("capRadius", &capRadius,
138 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
139 // This is the cap radius in normalized space where the outer radius is 1 and
140 // circledEdge.w is the normalized inner radius.
141 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500142 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400143 }
joshualittabb52a12015-01-13 15:02:10 -0800144
joshualittb8c241a2015-05-19 08:23:30 -0700145 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500146 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800147
joshualittabb52a12015-01-13 15:02:10 -0800148 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500149 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800150
151 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800152 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800153 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800154 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500155 cgp.fInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700156 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700157 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800158
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400159 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400160 fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400161 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800162 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500163 fragBuilder->codeAppend(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400164 "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400165 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800166 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000167 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000168
Brian Salomon92be2f72018-06-19 14:33:47 -0400169 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500170 fragBuilder->codeAppend(
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400171 "half clip = saturate(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
172 "clipPlane.z);");
Brian Salomon92be2f72018-06-19 14:33:47 -0400173 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500174 fragBuilder->codeAppend(
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400175 "clip *= saturate(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
176 "isectPlane.z);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700177 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400178 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500179 fragBuilder->codeAppend(
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400180 "clip = saturate(clip + saturate(circleEdge.z * dot(circleEdge.xy, "
181 "unionPlane.xy) + unionPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700182 }
183 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400184 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400185 // We compute coverage of the round caps as circles at the butt caps produced
186 // by the clip planes. The inverse of the clip planes is applied so that there
187 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400188 fragBuilder->codeAppendf(
189 "half dcap1 = circleEdge.z * (%s - length(circleEdge.xy - "
190 " roundCapCenters.xy));"
191 "half dcap2 = circleEdge.z * (%s - length(circleEdge.xy - "
192 " roundCapCenters.zw));"
Brian Salomon33c44222018-04-19 08:44:21 -0400193 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400194 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
195 capRadius.fsIn(), capRadius.fsIn());
196 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700197 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000198 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000199 }
200
robertphillips46d36f02015-01-18 08:14:14 -0800201 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500202 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700203 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800204 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700205 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500206 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700207 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon92be2f72018-06-19 14:33:47 -0400208 key |= cgp.fInClipPlane.isInitialized() ? 0x04 : 0x0;
209 key |= cgp.fInIsectPlane.isInitialized() ? 0x08 : 0x0;
210 key |= cgp.fInUnionPlane.isInitialized() ? 0x10 : 0x0;
211 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x20 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700212 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000213 }
214
bsalomona624bf32016-09-20 09:12:47 -0700215 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
216 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700217 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700218 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700219 }
220
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 private:
egdaniele659a582015-11-13 09:55:43 -0800222 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223 };
224
Brian Salomon289e3d82016-12-14 15:52:56 -0500225 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400226
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500227 Attribute fInPosition;
228 Attribute fInColor;
229 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400230 // Optional attributes.
231 Attribute fInClipPlane;
232 Attribute fInIsectPlane;
233 Attribute fInUnionPlane;
234 Attribute fInRoundCapCenters;
235
Brian Salomon289e3d82016-12-14 15:52:56 -0500236 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400237 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000238
joshualitt249af152014-09-15 11:41:13 -0700239 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000240};
241
bsalomoncdaa97b2016-03-08 08:30:14 -0800242GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243
Hal Canary6f6961e2017-01-31 13:50:44 -0500244#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700245sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400246 bool stroke = d->fRandom->nextBool();
247 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500248 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400249 bool clipPlane = d->fRandom->nextBool();
250 bool isectPlane = d->fRandom->nextBool();
251 bool unionPlane = d->fRandom->nextBool();
252 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Brian Osmane3caf2d2018-11-21 13:48:36 -0500253 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
254 stroke, clipPlane, isectPlane, unionPlane, roundCaps, wideColor, matrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000255}
Hal Canary6f6961e2017-01-31 13:50:44 -0500256#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000257
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400258class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
259public:
Brian Osmane3caf2d2018-11-21 13:48:36 -0500260 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400261 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500262 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500263 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500264 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
265 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
266 this->setVertexAttributes(&fInPosition, 4);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400267 }
268
269 ~ButtCapDashedCircleGeometryProcessor() override {}
270
271 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
272
273 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
274 GLSLProcessor::GenKey(*this, caps, b);
275 }
276
277 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
278 return new GLSLProcessor();
279 }
280
281private:
282 class GLSLProcessor : public GrGLSLGeometryProcessor {
283 public:
284 GLSLProcessor() {}
285
286 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
287 const ButtCapDashedCircleGeometryProcessor& bcscgp =
288 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
289 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
290 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
291 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
292 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
293
294 // emit attributes
295 varyingHandler->emitAttributes(bcscgp);
296 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500297 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400298
299 fragBuilder->codeAppend("float4 dashParams;");
300 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500301 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400302 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
303 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
304 varyingHandler->addVarying("wrapDashes", &wrapDashes,
305 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
306 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
307 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
308 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500309 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400310 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
311 // x = length of on interval, y = length of on + off.
312 // There are two other parameters in dashParams.zw:
313 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
314 // Each interval has a "corresponding" dash which may be shifted partially or
315 // fully out of its interval by the phase. So there may be up to two "visual"
316 // dashes in an interval.
317 // When computing coverage in an interval we look at three dashes. These are the
318 // "corresponding" dashes from the current, previous, and next intervals. Any of these
319 // may be phase shifted into our interval or even when phase=0 they may be within half a
320 // pixel distance of a pixel center in the interval.
321 // When in the first interval we need to check the dash from the last interval. And
322 // similarly when in the last interval we need to check the dash from the first
323 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
324 // We compute the dash begin/end angles in the vertex shader and apply them in the
325 // fragment shader when we detect we're in the first/last interval.
326 vertBuilder->codeAppend(R"(
327 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
328 // to the fragment shader as a varying.
329 float4 wrapDashes;
330 half lastIntervalLength = mod(6.28318530718, dashParams.y);
331 // We can happen to be perfectly divisible.
332 if (0 == lastIntervalLength) {
333 lastIntervalLength = dashParams.y;
334 }
335 // Let 'l' be the last interval before reaching 2 pi.
336 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
337 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
338 // interval.
339 half offset = 0;
340 if (-dashParams.w >= lastIntervalLength) {
341 offset = -dashParams.y;
342 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
343 offset = dashParams.y;
344 }
345 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
346 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
347 // min.
348 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
349
350 // Based on the phase determine whether the -1st, 0th, or 1st interval's
351 // "corresponding" dash appears in the 0th interval and is closest to l.
352 offset = 0;
353 if (dashParams.w >= dashParams.x) {
354 offset = dashParams.y;
355 } else if (-dashParams.w > dashParams.y - dashParams.x) {
356 offset = -dashParams.y;
357 }
358 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
359 wrapDashes.w = wrapDashes.z + dashParams.x;
360 // The start of the dash we're considering may be clipped by the start of the
361 // circle.
362 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
363 )");
364 vertBuilder->codeAppendf("%s = wrapDashes;", wrapDashes.vsOut());
365 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
366 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
367 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
368
369 // setup pass through color
370 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500371 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400372 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
373
374 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500375 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400376
377 // emit transforms
378 this->emitTransforms(vertBuilder,
379 varyingHandler,
380 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500381 bcscgp.fInPosition.asShaderVar(),
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400382 bcscgp.fLocalMatrix,
383 args.fFPCoordTransformHandler);
384 GrShaderVar fnArgs[] = {
385 GrShaderVar("angleToEdge", kFloat_GrSLType),
386 GrShaderVar("diameter", kFloat_GrSLType),
387 };
388 SkString fnName;
389 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
390 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
391 float linearDist;
392 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
393 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400394 return saturate(linearDist + 0.5);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 )",
396 &fnName);
397 fragBuilder->codeAppend(R"(
398 float d = length(circleEdge.xy) * circleEdge.z;
399
400 // Compute coverage from outer/inner edges of the stroke.
401 half distanceToOuterEdge = circleEdge.z - d;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400402 half edgeAlpha = saturate(distanceToOuterEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400403 half distanceToInnerEdge = d - circleEdge.z * circleEdge.w;
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400404 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400405 edgeAlpha *= innerAlpha;
406
407 half angleFromStart = atan(circleEdge.y, circleEdge.x) - dashParams.z;
408 angleFromStart = mod(angleFromStart, 6.28318530718);
409 float x = mod(angleFromStart, dashParams.y);
410 // Convert the radial distance from center to pixel into a diameter.
411 d *= 2;
412 half2 currDash = half2(-dashParams.w, dashParams.x - dashParams.w);
413 half2 nextDash = half2(dashParams.y - dashParams.w,
414 dashParams.y + dashParams.x - dashParams.w);
415 half2 prevDash = half2(-dashParams.y - dashParams.w,
416 -dashParams.y + dashParams.x - dashParams.w);
417 half dashAlpha = 0;
418 )");
419 fragBuilder->codeAppendf(R"(
420 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
421 dashAlpha += %s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d);
422 currDash.y = min(currDash.y, lastIntervalLength);
423 if (nextDash.x >= lastIntervalLength) {
424 // The next dash is outside the 0..2pi range, throw it away
425 nextDash.xy = half2(1000);
426 } else {
427 // Clip the end of the next dash to the end of the circle
428 nextDash.y = min(nextDash.y, lastIntervalLength);
429 }
430 }
431 )", fnName.c_str(), fnName.c_str());
432 fragBuilder->codeAppendf(R"(
433 if (angleFromStart - x - dashParams.y < -0.01) {
434 dashAlpha += %s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d);
435 currDash.x = max(currDash.x, 0);
436 if (prevDash.y <= 0) {
437 // The previous dash is outside the 0..2pi range, throw it away
438 prevDash.xy = half2(1000);
439 } else {
440 // Clip the start previous dash to the start of the circle
441 prevDash.x = max(prevDash.x, 0);
442 }
443 }
444 )", fnName.c_str(), fnName.c_str());
445 fragBuilder->codeAppendf(R"(
446 dashAlpha += %s(x - currDash.x, d) * %s(currDash.y - x, d);
447 dashAlpha += %s(x - nextDash.x, d) * %s(nextDash.y - x, d);
448 dashAlpha += %s(x - prevDash.x, d) * %s(prevDash.y - x, d);
449 dashAlpha = min(dashAlpha, 1);
450 edgeAlpha *= dashAlpha;
451 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
452 fnName.c_str());
453 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
454 }
455
456 static void GenKey(const GrGeometryProcessor& gp,
457 const GrShaderCaps&,
458 GrProcessorKeyBuilder* b) {
459 const ButtCapDashedCircleGeometryProcessor& bcscgp =
460 gp.cast<ButtCapDashedCircleGeometryProcessor>();
461 b->add32(bcscgp.fLocalMatrix.hasPerspective());
462 }
463
464 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
465 FPCoordTransformIter&& transformIter) override {
466 this->setTransformDataHelper(
467 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
468 &transformIter);
469 }
470
471 private:
472 typedef GrGLSLGeometryProcessor INHERITED;
473 };
474
475 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500476 Attribute fInPosition;
477 Attribute fInColor;
478 Attribute fInCircleEdge;
479 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400480
481 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
482
483 typedef GrGeometryProcessor INHERITED;
484};
485
486#if GR_TEST_UTILS
487sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500488 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400489 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Brian Osmane3caf2d2018-11-21 13:48:36 -0500490 return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(wideColor, matrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400491}
492#endif
493
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000494///////////////////////////////////////////////////////////////////////////////
495
496/**
497 * 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 +0000498 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
499 * in both x and y directions.
500 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000501 * 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 +0000502 */
503
bsalomoncdaa97b2016-03-08 08:30:14 -0800504class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000505public:
Brian Osmane3caf2d2018-11-21 13:48:36 -0500506 EllipseGeometryProcessor(bool stroke, bool wideColor, const SkMatrix& localMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000507 : INHERITED(kEllipseGeometryProcessor_ClassID)
508 , fLocalMatrix(localMatrix) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500509 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500510 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500511 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
512 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
513 this->setVertexAttributes(&fInPosition, 4);
Brian Salomonea26d6b2018-01-23 20:33:21 +0000514 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000515 }
516
Brian Salomond3b65972017-03-22 12:05:03 -0400517 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000518
mtklein36352bf2015-03-25 18:17:31 -0700519 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800520
Brian Salomon94efbf52016-11-29 13:43:05 -0500521 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700522 GLSLProcessor::GenKey(*this, caps, b);
523 }
524
Brian Salomon94efbf52016-11-29 13:43:05 -0500525 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700526 return new GLSLProcessor();
527 }
528
529private:
egdaniel57d3b032015-11-13 11:57:27 -0800530 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000531 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800532 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000533
Brian Salomon289e3d82016-12-14 15:52:56 -0500534 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800535 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800536 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800537 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800538 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000539
joshualittabb52a12015-01-13 15:02:10 -0800540 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800541 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800542
Chris Dalton27372882017-12-08 13:34:21 -0700543 GrGLSLVarying ellipseOffsets(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800544 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800545 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500546 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000547
Chris Dalton27372882017-12-08 13:34:21 -0700548 GrGLSLVarying ellipseRadii(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800549 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500550 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800551
Chris Dalton60283612018-02-14 13:38:14 -0700552 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700553 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500554 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800555
joshualittabb52a12015-01-13 15:02:10 -0800556 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500557 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800558
559 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800560 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800561 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800562 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500563 egp.fInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700564 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700565 args.fFPCoordTransformHandler);
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400566 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
567 // to compute both the edges because we need two separate test equations for
568 // the single offset.
569 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
570 // the distance by the gradient, non-uniformly scaled by the inverse of the
571 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800572
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000573 // for outer curve
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400574 fragBuilder->codeAppendf("half2 offset = %s;", ellipseOffsets.fsIn());
575 if (egp.fStroke) {
576 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
577 }
578 fragBuilder->codeAppend("half test = dot(offset, offset) - 1.0;");
579 fragBuilder->codeAppendf("half2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400580 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700581
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000582 // avoid calling inversesqrt on zero.
Jim Van Verthd3420de2018-06-28 15:22:43 -0400583 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400584 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400585 fragBuilder->codeAppend("half edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000586
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000587 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800588 if (egp.fStroke) {
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400589 fragBuilder->codeAppendf("offset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800590 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400591 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
592 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800593 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400594 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000595 }
596
Brian Salomonea26d6b2018-01-23 20:33:21 +0000597 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000598 }
599
robertphillips46d36f02015-01-18 08:14:14 -0800600 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500601 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700602 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800603 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
604 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700605 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700606 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000607 }
608
bsalomona624bf32016-09-20 09:12:47 -0700609 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
610 FPCoordTransformIter&& transformIter) override {
611 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
612 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700613 }
614
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000615 private:
egdaniele659a582015-11-13 09:55:43 -0800616 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000617 };
618
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500619 Attribute fInPosition;
620 Attribute fInColor;
621 Attribute fInEllipseOffset;
622 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400623
joshualitte3ababe2015-05-15 07:56:07 -0700624 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000625 bool fStroke;
626
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400627 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000628
joshualitt249af152014-09-15 11:41:13 -0700629 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000630};
631
bsalomoncdaa97b2016-03-08 08:30:14 -0800632GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000633
Hal Canary6f6961e2017-01-31 13:50:44 -0500634#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700635sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000636 return sk_sp<GrGeometryProcessor>(
Brian Osmane3caf2d2018-11-21 13:48:36 -0500637 new EllipseGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
638 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000639}
Hal Canary6f6961e2017-01-31 13:50:44 -0500640#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000641
642///////////////////////////////////////////////////////////////////////////////
643
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000644/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000645 * 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 +0000646 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
647 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
648 * using differentials.
649 *
650 * The result is device-independent and can be used with any affine matrix.
651 */
652
bsalomoncdaa97b2016-03-08 08:30:14 -0800653enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000654
bsalomoncdaa97b2016-03-08 08:30:14 -0800655class DIEllipseGeometryProcessor : public GrGeometryProcessor {
656public:
Brian Osmane3caf2d2018-11-21 13:48:36 -0500657 DIEllipseGeometryProcessor(bool wideColor, const SkMatrix& viewMatrix, DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400658 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000659 , fViewMatrix(viewMatrix) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000660 fStyle = style;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500661 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500662 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500663 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
664 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
665 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000666 }
667
Brian Salomond3b65972017-03-22 12:05:03 -0400668 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000669
mtklein36352bf2015-03-25 18:17:31 -0700670 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000671
Brian Salomon94efbf52016-11-29 13:43:05 -0500672 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700673 GLSLProcessor::GenKey(*this, caps, b);
674 }
halcanary9d524f22016-03-29 09:03:52 -0700675
Brian Salomon94efbf52016-11-29 13:43:05 -0500676 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700677 return new GLSLProcessor();
678 }
679
680private:
egdaniel57d3b032015-11-13 11:57:27 -0800681 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000682 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500683 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000684
joshualitt465283c2015-09-11 08:19:35 -0700685 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800686 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800687 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800688 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800689 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000690
joshualittabb52a12015-01-13 15:02:10 -0800691 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800692 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800693
Chris Dalton27372882017-12-08 13:34:21 -0700694 GrGLSLVarying offsets0(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800695 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500696 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700697
Chris Dalton27372882017-12-08 13:34:21 -0700698 GrGLSLVarying offsets1(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800699 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500700 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800701
Chris Dalton60283612018-02-14 13:38:14 -0700702 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500703 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800704
joshualittabb52a12015-01-13 15:02:10 -0800705 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400706 this->writeOutputPosition(vertBuilder,
707 uniformHandler,
708 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500709 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400710 diegp.fViewMatrix,
711 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800712
713 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800714 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800715 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800716 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500717 diegp.fInPosition.asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700718 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800719
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000720 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400721 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
722 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
723 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
724 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500725 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400726 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
727 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500728 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000729
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400730 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000731 // avoid calling inversesqrt on zero.
Jim Van Verthd3420de2018-06-28 15:22:43 -0400732 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400733 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800734 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000735 // can probably do this with one step
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400736 fragBuilder->codeAppend("half edgeAlpha = saturate(1.0-test*invlen);");
737 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000738 } else {
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400739 fragBuilder->codeAppend("half edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000740 }
741
742 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800743 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800744 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
745 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
746 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
747 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500748 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400749 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
750 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500751 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800752 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400753 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000754 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000755
756 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000757 }
758
robertphillips46d36f02015-01-18 08:14:14 -0800759 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500760 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700761 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800762 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
763 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700764 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700765 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000766 }
767
bsalomona624bf32016-09-20 09:12:47 -0700768 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
769 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800770 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700771
bsalomon31df31c2016-08-17 09:00:24 -0700772 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
773 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700774 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800775 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700776 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
777 }
bsalomona624bf32016-09-20 09:12:47 -0700778 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000779 }
780
781 private:
joshualitt5559ca22015-05-21 15:50:36 -0700782 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700783 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800784
egdaniele659a582015-11-13 09:55:43 -0800785 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000786 };
787
Brian Salomon92be2f72018-06-19 14:33:47 -0400788
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500789 Attribute fInPosition;
790 Attribute fInColor;
791 Attribute fInEllipseOffsets0;
792 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400793
Brian Salomon289e3d82016-12-14 15:52:56 -0500794 SkMatrix fViewMatrix;
795 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000796
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400797 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000798
joshualitt249af152014-09-15 11:41:13 -0700799 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000800};
801
bsalomoncdaa97b2016-03-08 08:30:14 -0800802GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000803
Hal Canary6f6961e2017-01-31 13:50:44 -0500804#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700805sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500806 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
Brian Osmane3caf2d2018-11-21 13:48:36 -0500807 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
808 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000809}
Hal Canary6f6961e2017-01-31 13:50:44 -0500810#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000811
812///////////////////////////////////////////////////////////////////////////////
813
jvanverth6ca48822016-10-07 06:57:32 -0700814// We have two possible cases for geometry for a circle:
815
816// In the case of a normal fill, we draw geometry for the circle as an octagon.
817static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500818 // enter the octagon
819 // clang-format off
820 0, 1, 8, 1, 2, 8,
821 2, 3, 8, 3, 4, 8,
822 4, 5, 8, 5, 6, 8,
823 6, 7, 8, 7, 0, 8
824 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700825};
826
827// For stroked circles, we use two nested octagons.
828static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500829 // enter the octagon
830 // clang-format off
831 0, 1, 9, 0, 9, 8,
832 1, 2, 10, 1, 10, 9,
833 2, 3, 11, 2, 11, 10,
834 3, 4, 12, 3, 12, 11,
835 4, 5, 13, 4, 13, 12,
836 5, 6, 14, 5, 14, 13,
837 6, 7, 15, 6, 15, 14,
838 7, 0, 8, 7, 8, 15,
839 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700840};
841
Brian Osman9d958b52018-11-13 12:46:56 -0500842// Normalized geometry for octagons that circumscribe and lie on a circle:
843
844static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
845static constexpr SkPoint kOctagonOuter[] = {
846 SkPoint::Make(-kOctOffset, -1),
847 SkPoint::Make( kOctOffset, -1),
848 SkPoint::Make( 1, -kOctOffset),
849 SkPoint::Make( 1, kOctOffset),
850 SkPoint::Make( kOctOffset, 1),
851 SkPoint::Make(-kOctOffset, 1),
852 SkPoint::Make(-1, kOctOffset),
853 SkPoint::Make(-1, -kOctOffset),
854};
855
856// cosine and sine of pi/8
857static constexpr SkScalar kCosPi8 = 0.923579533f;
858static constexpr SkScalar kSinPi8 = 0.382683432f;
859static constexpr SkPoint kOctagonInner[] = {
860 SkPoint::Make(-kSinPi8, -kCosPi8),
861 SkPoint::Make( kSinPi8, -kCosPi8),
862 SkPoint::Make( kCosPi8, -kSinPi8),
863 SkPoint::Make( kCosPi8, kSinPi8),
864 SkPoint::Make( kSinPi8, kCosPi8),
865 SkPoint::Make(-kSinPi8, kCosPi8),
866 SkPoint::Make(-kCosPi8, kSinPi8),
867 SkPoint::Make(-kCosPi8, -kSinPi8),
868};
Brian Salomon289e3d82016-12-14 15:52:56 -0500869
jvanverth6ca48822016-10-07 06:57:32 -0700870static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
871static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
872static const int kVertsPerStrokeCircle = 16;
873static const int kVertsPerFillCircle = 9;
874
875static int circle_type_to_vert_count(bool stroked) {
876 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
877}
878
879static int circle_type_to_index_count(bool stroked) {
880 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
881}
882
883static const uint16_t* circle_type_to_indices(bool stroked) {
884 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
885}
886
887///////////////////////////////////////////////////////////////////////////////
888
Brian Salomon05441c42017-05-15 16:45:49 -0400889class CircleOp final : public GrMeshDrawOp {
890private:
891 using Helper = GrSimpleMeshDrawOpHelper;
892
joshualitt76e7fb62015-02-11 08:52:27 -0800893public:
Brian Salomon25a88092016-12-01 09:36:50 -0500894 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700895
bsalomon4f3a0ca2016-08-22 13:14:26 -0700896 /** Optional extra params to render a partial arc rather than a full circle. */
897 struct ArcParams {
898 SkScalar fStartAngleRadians;
899 SkScalar fSweepAngleRadians;
900 bool fUseCenter;
901 };
Brian Salomon05441c42017-05-15 16:45:49 -0400902
Robert Phillips7c525e62018-06-12 10:11:12 -0400903 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
904 GrPaint&& paint,
905 const SkMatrix& viewMatrix,
906 SkPoint center,
907 SkScalar radius,
908 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -0400909 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700910 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700911 if (style.hasPathEffect()) {
912 return nullptr;
913 }
Brian Salomon05441c42017-05-15 16:45:49 -0400914 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700915 SkStrokeRec::Style recStyle = stroke.getStyle();
916 if (arcParams) {
917 // Arc support depends on the style.
918 switch (recStyle) {
919 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500920 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700921 return nullptr;
922 case SkStrokeRec::kFill_Style:
923 // This supports all fills.
924 break;
Brian Salomon45c92202018-04-10 10:53:58 -0400925 case SkStrokeRec::kStroke_Style:
926 // Strokes that don't use the center point are supported with butt and round
927 // caps.
928 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
929 return nullptr;
930 }
931 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700932 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -0400933 // Hairline only supports butt cap. Round caps could be emulated by slightly
934 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700935 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
936 return nullptr;
937 }
938 break;
939 }
940 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400941 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
942 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -0400943 }
944
Brian Osmancf860852018-10-31 14:04:39 -0400945 CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
946 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
947 const ArcParams* arcParams)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000948 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -0400949 const SkStrokeRec& stroke = style.strokeRec();
950 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700951
Brian Salomon45c92202018-04-10 10:53:58 -0400952 fRoundCaps = false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500953 fWideColor = !SkPMColor4fFitsInBytes(color);
Brian Salomon45c92202018-04-10 10:53:58 -0400954
bsalomon4b4a7cc2016-07-08 04:42:54 -0700955 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700956 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700957 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800958
Brian Salomon289e3d82016-12-14 15:52:56 -0500959 bool isStrokeOnly =
960 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700961 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700962
jvanverth6ca48822016-10-07 06:57:32 -0700963 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700964 SkScalar outerRadius = radius;
965 SkScalar halfWidth = 0;
966 if (hasStroke) {
967 if (SkScalarNearlyZero(strokeWidth)) {
968 halfWidth = SK_ScalarHalf;
969 } else {
970 halfWidth = SkScalarHalf(strokeWidth);
971 }
972
973 outerRadius += halfWidth;
974 if (isStrokeOnly) {
975 innerRadius = radius - halfWidth;
976 }
977 }
978
979 // The radii are outset for two reasons. First, it allows the shader to simply perform
980 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
981 // Second, the outer radius is used to compute the verts of the bounding box that is
982 // rendered and the outset ensures the box will cover all partially covered by the circle.
983 outerRadius += SK_ScalarHalf;
984 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700985 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400986 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700987
bsalomon4f3a0ca2016-08-22 13:14:26 -0700988 // This makes every point fully inside the intersection plane.
989 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
990 // This makes every point fully outside the union plane.
991 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -0400992 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700993 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
994 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700995 if (arcParams) {
996 // The shader operates in a space where the circle is translated to be centered at the
997 // origin. Here we compute points on the unit circle at the starting and ending angles.
998 SkPoint startPoint, stopPoint;
999 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
1000 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
1001 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001002
1003 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1004 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1005 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1006 startPoint.normalize();
1007 stopPoint.normalize();
1008
1009 // If the matrix included scale (on one axis) we need to swap our start and end points
1010 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001011 using std::swap;
1012 swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001013 }
1014
Brian Salomon45c92202018-04-10 10:53:58 -04001015 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1016 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1017 SkPoint roundCaps[2];
1018 if (fRoundCaps) {
1019 // Compute the cap center points in the normalized space.
1020 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1021 roundCaps[0] = startPoint * midRadius;
1022 roundCaps[1] = stopPoint * midRadius;
1023 } else {
1024 roundCaps[0] = kUnusedRoundCaps[0];
1025 roundCaps[1] = kUnusedRoundCaps[1];
1026 }
1027
bsalomon4f3a0ca2016-08-22 13:14:26 -07001028 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001029 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1030 // center of the butts.
1031 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001032 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001033 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001034 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001035 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1036 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1037 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001038 if (useCenter) {
1039 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1040 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001041 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1042 if (arcParams->fSweepAngleRadians < 0) {
1043 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001044 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001045 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001046 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001047 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001048 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001049 color,
1050 innerRadius,
1051 outerRadius,
1052 {norm0.fX, norm0.fY, 0.5f},
1053 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1054 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001055 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001056 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001057 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001058 fClipPlaneIsect = false;
1059 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001060 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001061 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001062 color,
1063 innerRadius,
1064 outerRadius,
1065 {norm0.fX, norm0.fY, 0.5f},
1066 {norm1.fX, norm1.fY, 0.5f},
1067 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001068 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001069 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001070 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001071 fClipPlaneIsect = true;
1072 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073 }
1074 } else {
1075 // We clip to a secant of the original circle.
1076 startPoint.scale(radius);
1077 stopPoint.scale(radius);
1078 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1079 norm.normalize();
1080 if (arcParams->fSweepAngleRadians > 0) {
1081 norm.negate();
1082 }
1083 SkScalar d = -norm.dot(startPoint) + 0.5f;
1084
Brian Salomon05441c42017-05-15 16:45:49 -04001085 fCircles.emplace_back(
1086 Circle{color,
1087 innerRadius,
1088 outerRadius,
1089 {norm.fX, norm.fY, d},
1090 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1091 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001092 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001093 devBounds,
1094 stroked});
1095 fClipPlane = true;
1096 fClipPlaneIsect = false;
1097 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001098 }
1099 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001100 fCircles.emplace_back(
1101 Circle{color,
1102 innerRadius,
1103 outerRadius,
1104 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1105 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1106 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001107 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001108 devBounds,
1109 stroked});
1110 fClipPlane = false;
1111 fClipPlaneIsect = false;
1112 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001113 }
bsalomon88cf17d2016-07-08 06:40:56 -07001114 // Use the original radius and stroke radius for the bounds so that it does not include the
1115 // AA bloat.
1116 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001117 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001118 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1119 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001120 fVertCount = circle_type_to_vert_count(stroked);
1121 fIndexCount = circle_type_to_index_count(stroked);
1122 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001123 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001124
Brian Salomon289e3d82016-12-14 15:52:56 -05001125 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001126
Brian Salomon7d94bb52018-10-12 14:37:19 -04001127 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001128 fHelper.visitProxies(func);
1129 }
1130
Brian Osman9a390ac2018-11-12 09:47:48 -05001131#ifdef SK_DEBUG
robertphillipse004bfc2015-11-16 09:06:59 -08001132 SkString dumpInfo() const override {
1133 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001134 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001135 string.appendf(
1136 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1137 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001138 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001139 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1140 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1141 fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001142 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001143 string += fHelper.dumpInfo();
1144 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001145 return string;
1146 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001147#endif
robertphillipse004bfc2015-11-16 09:06:59 -08001148
Chris Dalton4b62aed2019-01-15 11:53:00 -07001149 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001150 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001151 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1152 color);
Brian Salomon05441c42017-05-15 16:45:49 -04001153 }
1154
1155 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1156
bsalomone46f9fe2015-08-18 06:05:14 -07001157private:
Brian Salomon91326c32017-08-09 16:02:19 -04001158 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001159 SkMatrix localMatrix;
1160 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001161 return;
1162 }
1163
1164 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001165 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
Brian Osmane3caf2d2018-11-21 13:48:36 -05001166 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, fWideColor,
1167 localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001168
jvanverth6ca48822016-10-07 06:57:32 -07001169 const GrBuffer* vertexBuffer;
1170 int firstVertex;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001171 GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
1172 &vertexBuffer, &firstVertex)};
1173 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001174 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001175 return;
1176 }
1177
jvanverth6ca48822016-10-07 06:57:32 -07001178 const GrBuffer* indexBuffer = nullptr;
1179 int firstIndex = 0;
1180 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1181 if (!indices) {
1182 SkDebugf("Could not allocate indices\n");
1183 return;
1184 }
1185
1186 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001187 for (const auto& circle : fCircles) {
1188 SkScalar innerRadius = circle.fInnerRadius;
1189 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001190 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001191 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001192
joshualitt76e7fb62015-02-11 08:52:27 -08001193 // The inner radius in the vertex data must be specified in normalized space.
1194 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001195 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001196
1197 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001198 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001199
Brian Osman9a24fee2018-08-03 09:48:42 -04001200 SkVector geoClipPlane = { 0, 0 };
1201 SkScalar offsetClipDist = SK_Scalar1;
1202 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1203 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1204 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1205 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1206 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1207 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1208 // the AA can extend just past the center of the circle.
1209 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1210 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1211 SkAssertResult(geoClipPlane.normalize());
1212 offsetClipDist = 0.5f / halfWidth;
1213 }
1214
Brian Osman7d8f82b2018-11-08 10:24:09 -05001215 for (int i = 0; i < 8; ++i) {
1216 // This clips the normalized offset to the half-plane we computed above. Then we
1217 // compute the vertex position from this.
Brian Osman9d958b52018-11-13 12:46:56 -05001218 SkScalar dist = SkTMin(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
1219 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001220 vertices.write(center + offset * halfWidth,
1221 color,
1222 offset,
1223 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001224 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001225 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001226 }
1227 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001228 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001229 }
1230 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001231 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001232 }
1233 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001234 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001235 }
Brian Salomon45c92202018-04-10 10:53:58 -04001236 }
jvanverth6ca48822016-10-07 06:57:32 -07001237
Brian Salomon05441c42017-05-15 16:45:49 -04001238 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001239 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001240
Brian Osman7d8f82b2018-11-08 10:24:09 -05001241 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001242 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1243 color,
1244 kOctagonInner[i] * innerRadius,
1245 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001246 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001247 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001248 }
1249 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001250 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001251 }
1252 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001253 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001254 }
1255 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001256 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001257 }
Brian Salomon45c92202018-04-10 10:53:58 -04001258 }
jvanverth6ca48822016-10-07 06:57:32 -07001259 } else {
1260 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001261 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001262 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001263 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001264 }
jvanverth6ca48822016-10-07 06:57:32 -07001265 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001266 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001267 }
1268 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001269 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001270 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001271 if (fRoundCaps) {
1272 vertices.write(circle.fRoundCapCenters);
1273 }
jvanverth6ca48822016-10-07 06:57:32 -07001274 }
1275
Brian Salomon05441c42017-05-15 16:45:49 -04001276 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1277 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001278 for (int i = 0; i < primIndexCount; ++i) {
1279 *indices++ = primIndices[i] + currStartVertex;
1280 }
1281
Brian Salomon05441c42017-05-15 16:45:49 -04001282 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001283 }
jvanverth6ca48822016-10-07 06:57:32 -07001284
Brian Salomon7eae3e02018-08-07 14:02:38 +00001285 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
1286 mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1287 GrPrimitiveRestart::kNo);
1288 mesh->setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001289 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00001290 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001291 }
1292
Brian Salomon7eae3e02018-08-07 14:02:38 +00001293 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001294 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001295
1296 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001297 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001298 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001299 }
1300
Brian Salomon05441c42017-05-15 16:45:49 -04001301 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001302 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001303 }
1304
Brian Salomon05441c42017-05-15 16:45:49 -04001305 if (fHelper.usesLocalCoords() &&
1306 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001307 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001308 }
1309
Brian Salomon289e3d82016-12-14 15:52:56 -05001310 // Because we've set up the ops that don't use the planes with noop values
1311 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001312 fClipPlane |= that->fClipPlane;
1313 fClipPlaneIsect |= that->fClipPlaneIsect;
1314 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001315 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001316 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001317
Brian Salomon05441c42017-05-15 16:45:49 -04001318 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001319 fVertCount += that->fVertCount;
1320 fIndexCount += that->fIndexCount;
1321 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001322 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001323 }
1324
Brian Salomon05441c42017-05-15 16:45:49 -04001325 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001326 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001327 SkScalar fInnerRadius;
1328 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001329 SkScalar fClipPlane[3];
1330 SkScalar fIsectPlane[3];
1331 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001332 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001333 SkRect fDevBounds;
1334 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001335 };
1336
Brian Salomon289e3d82016-12-14 15:52:56 -05001337 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001338 Helper fHelper;
1339 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001340 int fVertCount;
1341 int fIndexCount;
1342 bool fAllFill;
1343 bool fClipPlane;
1344 bool fClipPlaneIsect;
1345 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001346 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001347 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001348
Brian Salomon05441c42017-05-15 16:45:49 -04001349 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001350};
1351
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001352class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1353private:
1354 using Helper = GrSimpleMeshDrawOpHelper;
1355
1356public:
1357 DEFINE_OP_CLASS_ID
1358
Robert Phillips7c525e62018-06-12 10:11:12 -04001359 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1360 GrPaint&& paint,
1361 const SkMatrix& viewMatrix,
1362 SkPoint center,
1363 SkScalar radius,
1364 SkScalar strokeWidth,
1365 SkScalar startAngle,
1366 SkScalar onAngle,
1367 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001368 SkScalar phaseAngle) {
1369 SkASSERT(circle_stays_circle(viewMatrix));
1370 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001371 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1372 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001373 onAngle, offAngle, phaseAngle);
1374 }
1375
Brian Osmancf860852018-10-31 14:04:39 -04001376 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001377 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1378 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1379 SkScalar offAngle, SkScalar phaseAngle)
1380 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1381 SkASSERT(circle_stays_circle(viewMatrix));
1382 viewMatrix.mapPoints(&center, 1);
1383 radius = viewMatrix.mapRadius(radius);
1384 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1385
1386 // Determine the angle where the circle starts in device space and whether its orientation
1387 // has been reversed.
1388 SkVector start;
1389 bool reflection;
1390 if (!startAngle) {
1391 start = {1, 0};
1392 } else {
1393 start.fY = SkScalarSinCos(startAngle, &start.fX);
1394 }
1395 viewMatrix.mapVectors(&start, 1);
1396 startAngle = SkScalarATan2(start.fY, start.fX);
1397 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1398 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1399
1400 auto totalAngle = onAngle + offAngle;
1401 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1402
1403 SkScalar halfWidth = 0;
1404 if (SkScalarNearlyZero(strokeWidth)) {
1405 halfWidth = SK_ScalarHalf;
1406 } else {
1407 halfWidth = SkScalarHalf(strokeWidth);
1408 }
1409
1410 SkScalar outerRadius = radius + halfWidth;
1411 SkScalar innerRadius = radius - halfWidth;
1412
1413 // The radii are outset for two reasons. First, it allows the shader to simply perform
1414 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1415 // Second, the outer radius is used to compute the verts of the bounding box that is
1416 // rendered and the outset ensures the box will cover all partially covered by the circle.
1417 outerRadius += SK_ScalarHalf;
1418 innerRadius -= SK_ScalarHalf;
1419 fViewMatrixIfUsingLocalCoords = viewMatrix;
1420
1421 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1422 center.fX + outerRadius, center.fY + outerRadius);
1423
1424 // We store whether there is a reflection as a negative total angle.
1425 if (reflection) {
1426 totalAngle = -totalAngle;
1427 }
1428 fCircles.push_back(Circle{
1429 color,
1430 outerRadius,
1431 innerRadius,
1432 onAngle,
1433 totalAngle,
1434 startAngle,
1435 phaseAngle,
1436 devBounds
1437 });
1438 // Use the original radius and stroke radius for the bounds so that it does not include the
1439 // AA bloat.
1440 radius += halfWidth;
1441 this->setBounds(
1442 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1443 HasAABloat::kYes, IsZeroArea::kNo);
1444 fVertCount = circle_type_to_vert_count(true);
1445 fIndexCount = circle_type_to_index_count(true);
Brian Osmane3caf2d2018-11-21 13:48:36 -05001446 fWideColor = !SkPMColor4fFitsInBytes(color);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001447 }
1448
1449 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1450
Brian Salomon7d94bb52018-10-12 14:37:19 -04001451 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
1452 fHelper.visitProxies(func);
1453 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001454
Brian Osman9a390ac2018-11-12 09:47:48 -05001455#ifdef SK_DEBUG
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001456 SkString dumpInfo() const override {
1457 SkString string;
1458 for (int i = 0; i < fCircles.count(); ++i) {
1459 string.appendf(
1460 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1461 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1462 "Phase: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001463 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001464 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1465 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1466 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1467 fCircles[i].fPhaseAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001468 }
1469 string += fHelper.dumpInfo();
1470 string += INHERITED::dumpInfo();
1471 return string;
1472 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001473#endif
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001474
Chris Dalton4b62aed2019-01-15 11:53:00 -07001475 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001476 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001477 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1478 color);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001479 }
1480
1481 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1482
1483private:
1484 void onPrepareDraws(Target* target) override {
1485 SkMatrix localMatrix;
1486 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1487 return;
1488 }
1489
1490 // Setup geometry processor
Brian Osmane3caf2d2018-11-21 13:48:36 -05001491 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(fWideColor,
1492 localMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001493
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001494 const GrBuffer* vertexBuffer;
1495 int firstVertex;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001496 GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
1497 &vertexBuffer, &firstVertex)};
1498 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001499 SkDebugf("Could not allocate vertices\n");
1500 return;
1501 }
1502
1503 const GrBuffer* indexBuffer = nullptr;
1504 int firstIndex = 0;
1505 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1506 if (!indices) {
1507 SkDebugf("Could not allocate indices\n");
1508 return;
1509 }
1510
1511 int currStartVertex = 0;
1512 for (const auto& circle : fCircles) {
1513 // The inner radius in the vertex data must be specified in normalized space so that
1514 // length() can be called with smaller values to avoid precision issues with half
1515 // floats.
1516 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1517 const SkRect& bounds = circle.fDevBounds;
1518 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001519 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1520 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1521 };
1522 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001523 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001524 dashParams.totalAngle = -dashParams.totalAngle;
1525 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001526 }
1527
Brian Osmane3caf2d2018-11-21 13:48:36 -05001528 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001529
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001530 // The bounding geometry for the circle is composed of an outer bounding octagon and
1531 // an inner bounded octagon.
1532
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001533 // Compute the vertices of the outer octagon.
1534 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1535 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001536
1537 auto reflectY = [=](const SkPoint& p) {
1538 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001539 };
Brian Osman9d958b52018-11-13 12:46:56 -05001540
1541 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001542 vertices.write(center + kOctagonOuter[i] * halfWidth,
1543 color,
1544 reflectY(kOctagonOuter[i]),
1545 circle.fOuterRadius,
1546 normInnerRadius,
1547 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001548 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001549
1550 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001551 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001552 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1553 color,
1554 reflectY(kOctagonInner[i]) * normInnerRadius,
1555 circle.fOuterRadius,
1556 normInnerRadius,
1557 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001558 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001559
1560 const uint16_t* primIndices = circle_type_to_indices(true);
1561 const int primIndexCount = circle_type_to_index_count(true);
1562 for (int i = 0; i < primIndexCount; ++i) {
1563 *indices++ = primIndices[i] + currStartVertex;
1564 }
1565
1566 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001567 }
1568
Brian Salomon7eae3e02018-08-07 14:02:38 +00001569 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
1570 mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1571 GrPrimitiveRestart::kNo);
1572 mesh->setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001573 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00001574 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001575 }
1576
Brian Salomon7eae3e02018-08-07 14:02:38 +00001577 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001578 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1579
1580 // can only represent 65535 unique vertices with 16-bit indices
1581 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001582 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001583 }
1584
1585 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001586 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001587 }
1588
1589 if (fHelper.usesLocalCoords() &&
1590 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001591 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001592 }
1593
1594 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001595 fVertCount += that->fVertCount;
1596 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001597 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001598 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001599 }
1600
1601 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001602 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001603 SkScalar fOuterRadius;
1604 SkScalar fInnerRadius;
1605 SkScalar fOnAngle;
1606 SkScalar fTotalAngle;
1607 SkScalar fStartAngle;
1608 SkScalar fPhaseAngle;
1609 SkRect fDevBounds;
1610 };
1611
1612 SkMatrix fViewMatrixIfUsingLocalCoords;
1613 Helper fHelper;
1614 SkSTArray<1, Circle, true> fCircles;
1615 int fVertCount;
1616 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001617 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001618
1619 typedef GrMeshDrawOp INHERITED;
1620};
1621
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001622///////////////////////////////////////////////////////////////////////////////
1623
Brian Salomon05441c42017-05-15 16:45:49 -04001624class EllipseOp : public GrMeshDrawOp {
1625private:
1626 using Helper = GrSimpleMeshDrawOpHelper;
1627
1628 struct DeviceSpaceParams {
1629 SkPoint fCenter;
1630 SkScalar fXRadius;
1631 SkScalar fYRadius;
1632 SkScalar fInnerXRadius;
1633 SkScalar fInnerYRadius;
1634 };
1635
joshualitt76e7fb62015-02-11 08:52:27 -08001636public:
Brian Salomon25a88092016-12-01 09:36:50 -05001637 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001638
Robert Phillips7c525e62018-06-12 10:11:12 -04001639 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1640 GrPaint&& paint,
1641 const SkMatrix& viewMatrix,
1642 const SkRect& ellipse,
1643 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001644 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001645 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001646 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1647 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001648 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1649 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001650 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1651 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1652 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1653 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001654
bsalomon4b4a7cc2016-07-08 04:42:54 -07001655 // do (potentially) anisotropic mapping of stroke
1656 SkVector scaledStroke;
1657 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001658 scaledStroke.fX = SkScalarAbs(
1659 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1660 scaledStroke.fY = SkScalarAbs(
1661 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001662
1663 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001664 bool isStrokeOnly =
1665 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001666 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1667
Brian Salomon05441c42017-05-15 16:45:49 -04001668 params.fInnerXRadius = 0;
1669 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001670 if (hasStroke) {
1671 if (SkScalarNearlyZero(scaledStroke.length())) {
1672 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1673 } else {
1674 scaledStroke.scale(SK_ScalarHalf);
1675 }
1676
1677 // we only handle thick strokes for near-circular ellipses
1678 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001679 (0.5f * params.fXRadius > params.fYRadius ||
1680 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001681 return nullptr;
1682 }
1683
1684 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001685 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1686 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1687 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1688 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001689 return nullptr;
1690 }
1691
1692 // this is legit only if scale & translation (which should be the case at the moment)
1693 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001694 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1695 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001696 }
1697
Brian Salomon05441c42017-05-15 16:45:49 -04001698 params.fXRadius += scaledStroke.fX;
1699 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001700 }
Robert Phillips7c525e62018-06-12 10:11:12 -04001701 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
1702 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001703 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001704
Brian Osmancf860852018-10-31 14:04:39 -04001705 EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04001706 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
1707 const SkStrokeRec& stroke)
Brian Salomonea26d6b2018-01-23 20:33:21 +00001708 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001709 SkStrokeRec::Style style = stroke.getStyle();
1710 bool isStrokeOnly =
1711 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001712
Brian Salomon05441c42017-05-15 16:45:49 -04001713 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1714 params.fInnerXRadius, params.fInnerYRadius,
1715 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1716 params.fCenter.fY - params.fYRadius,
1717 params.fCenter.fX + params.fXRadius,
1718 params.fCenter.fY + params.fYRadius)});
1719
1720 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001721
bsalomon4b4a7cc2016-07-08 04:42:54 -07001722 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001723 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001724
Brian Salomon05441c42017-05-15 16:45:49 -04001725 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1726 fViewMatrixIfUsingLocalCoords = viewMatrix;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001727 fWideColor = !SkPMColor4fFitsInBytes(color);
bsalomoncdaa97b2016-03-08 08:30:14 -08001728 }
joshualitt76e7fb62015-02-11 08:52:27 -08001729
Brian Salomon289e3d82016-12-14 15:52:56 -05001730 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001731
Brian Salomon7d94bb52018-10-12 14:37:19 -04001732 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001733 fHelper.visitProxies(func);
1734 }
1735
Brian Osman9a390ac2018-11-12 09:47:48 -05001736#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001737 SkString dumpInfo() const override {
1738 SkString string;
1739 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001740 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001741 string.appendf(
1742 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1743 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001744 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001745 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
1746 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001747 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001748 string += fHelper.dumpInfo();
1749 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001750 return string;
1751 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001752#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001753
Chris Dalton4b62aed2019-01-15 11:53:00 -07001754 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001755 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001756 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1757 color);
Brian Salomon05441c42017-05-15 16:45:49 -04001758 }
1759
1760 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1761
bsalomone46f9fe2015-08-18 06:05:14 -07001762private:
Brian Salomon91326c32017-08-09 16:02:19 -04001763 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001764 SkMatrix localMatrix;
1765 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001766 return;
1767 }
1768
1769 // Setup geometry processor
Brian Osmane3caf2d2018-11-21 13:48:36 -05001770 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor,
1771 localMatrix));
Brian Osman9d958b52018-11-13 12:46:56 -05001772 QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001773 GrVertexWriter verts{helper.vertices()};
1774 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08001775 return;
1776 }
1777
Brian Salomon05441c42017-05-15 16:45:49 -04001778 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001779 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001780 SkScalar xRadius = ellipse.fXRadius;
1781 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001782
1783 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001784 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1785 SkScalarInvert(xRadius),
1786 SkScalarInvert(yRadius),
1787 SkScalarInvert(ellipse.fInnerXRadius),
1788 SkScalarInvert(ellipse.fInnerYRadius)
1789 };
vjiaoblack977996d2016-06-30 12:20:54 -07001790 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1791 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1792
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001793 if (!fStroked) {
1794 // For filled ellipses we map a unit circle in the vertex attributes rather than
1795 // computing an ellipse and modifying that distance, so we normalize to 1
1796 xMaxOffset /= xRadius;
1797 yMaxOffset /= yRadius;
1798 }
1799
joshualitt76e7fb62015-02-11 08:52:27 -08001800 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001801 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1802 color,
1803 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
1804 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08001805 }
Brian Salomon49348902018-06-26 09:12:38 -04001806 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00001807 helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08001808 }
1809
Brian Salomon7eae3e02018-08-07 14:02:38 +00001810 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001811 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001812
Brian Salomon05441c42017-05-15 16:45:49 -04001813 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001814 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001815 }
1816
bsalomoncdaa97b2016-03-08 08:30:14 -08001817 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001818 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001819 }
1820
Brian Salomon05441c42017-05-15 16:45:49 -04001821 if (fHelper.usesLocalCoords() &&
1822 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001823 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001824 }
1825
Brian Salomon05441c42017-05-15 16:45:49 -04001826 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05001827 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001828 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001829 }
1830
Brian Salomon05441c42017-05-15 16:45:49 -04001831 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04001832 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001833 SkScalar fXRadius;
1834 SkScalar fYRadius;
1835 SkScalar fInnerXRadius;
1836 SkScalar fInnerYRadius;
1837 SkRect fDevBounds;
1838 };
joshualitt76e7fb62015-02-11 08:52:27 -08001839
Brian Salomon289e3d82016-12-14 15:52:56 -05001840 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001841 Helper fHelper;
1842 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001843 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04001844 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001845
Brian Salomon05441c42017-05-15 16:45:49 -04001846 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001847};
1848
joshualitt76e7fb62015-02-11 08:52:27 -08001849/////////////////////////////////////////////////////////////////////////////////////////////////
1850
Brian Salomon05441c42017-05-15 16:45:49 -04001851class DIEllipseOp : public GrMeshDrawOp {
1852private:
1853 using Helper = GrSimpleMeshDrawOpHelper;
1854
1855 struct DeviceSpaceParams {
1856 SkPoint fCenter;
1857 SkScalar fXRadius;
1858 SkScalar fYRadius;
1859 SkScalar fInnerXRadius;
1860 SkScalar fInnerYRadius;
1861 DIEllipseStyle fStyle;
1862 };
1863
joshualitt76e7fb62015-02-11 08:52:27 -08001864public:
Brian Salomon25a88092016-12-01 09:36:50 -05001865 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001866
Robert Phillips7c525e62018-06-12 10:11:12 -04001867 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1868 GrPaint&& paint,
1869 const SkMatrix& viewMatrix,
1870 const SkRect& ellipse,
1871 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001872 DeviceSpaceParams params;
1873 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1874 params.fXRadius = SkScalarHalf(ellipse.width());
1875 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001876
bsalomon4b4a7cc2016-07-08 04:42:54 -07001877 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04001878 params.fStyle = (SkStrokeRec::kStroke_Style == style)
1879 ? DIEllipseStyle::kStroke
1880 : (SkStrokeRec::kHairline_Style == style)
1881 ? DIEllipseStyle::kHairline
1882 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001883
Brian Salomon05441c42017-05-15 16:45:49 -04001884 params.fInnerXRadius = 0;
1885 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001886 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1887 SkScalar strokeWidth = stroke.getWidth();
1888
1889 if (SkScalarNearlyZero(strokeWidth)) {
1890 strokeWidth = SK_ScalarHalf;
1891 } else {
1892 strokeWidth *= SK_ScalarHalf;
1893 }
1894
1895 // we only handle thick strokes for near-circular ellipses
1896 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001897 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1898 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001899 return nullptr;
1900 }
1901
1902 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001903 if (strokeWidth * (params.fYRadius * params.fYRadius) <
1904 (strokeWidth * strokeWidth) * params.fXRadius) {
1905 return nullptr;
1906 }
1907 if (strokeWidth * (params.fXRadius * params.fXRadius) <
1908 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001909 return nullptr;
1910 }
1911
1912 // set inner radius (if needed)
1913 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04001914 params.fInnerXRadius = params.fXRadius - strokeWidth;
1915 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001916 }
1917
Brian Salomon05441c42017-05-15 16:45:49 -04001918 params.fXRadius += strokeWidth;
1919 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001920 }
Brian Salomon05441c42017-05-15 16:45:49 -04001921 if (DIEllipseStyle::kStroke == params.fStyle &&
1922 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1923 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001924 }
Robert Phillips7c525e62018-06-12 10:11:12 -04001925 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04001926 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001927
Brian Osmancf860852018-10-31 14:04:39 -04001928 DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04001929 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00001930 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001931 // This expands the outer rect so that after CTM we end up with a half-pixel border
1932 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1933 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1934 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1935 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001936 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1937 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001938
Brian Salomon05441c42017-05-15 16:45:49 -04001939 fEllipses.emplace_back(
1940 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1941 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1942 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1943 params.fCenter.fY - params.fYRadius - geoDy,
1944 params.fCenter.fX + params.fXRadius + geoDx,
1945 params.fCenter.fY + params.fYRadius + geoDy)});
1946 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1947 IsZeroArea::kNo);
Brian Osmane3caf2d2018-11-21 13:48:36 -05001948 fWideColor = !SkPMColor4fFitsInBytes(color);
joshualitt76e7fb62015-02-11 08:52:27 -08001949 }
1950
Brian Salomon289e3d82016-12-14 15:52:56 -05001951 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001952
Brian Salomon7d94bb52018-10-12 14:37:19 -04001953 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001954 fHelper.visitProxies(func);
1955 }
1956
Brian Osman9a390ac2018-11-12 09:47:48 -05001957#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001958 SkString dumpInfo() const override {
1959 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001960 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001961 string.appendf(
1962 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1963 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1964 "GeoDY: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001965 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001966 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
1967 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001968 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001969 string += fHelper.dumpInfo();
1970 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001971 return string;
1972 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001973#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001974
Chris Dalton4b62aed2019-01-15 11:53:00 -07001975 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001976 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001977 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1978 color);
Brian Salomon05441c42017-05-15 16:45:49 -04001979 }
1980
1981 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1982
bsalomone46f9fe2015-08-18 06:05:14 -07001983private:
Brian Salomon91326c32017-08-09 16:02:19 -04001984 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001985 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001986 sk_sp<GrGeometryProcessor> gp(
Brian Osmane3caf2d2018-11-21 13:48:36 -05001987 new DIEllipseGeometryProcessor(fWideColor, this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001988
Brian Osman9d958b52018-11-13 12:46:56 -05001989 QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001990 GrVertexWriter verts{helper.vertices()};
1991 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08001992 return;
1993 }
1994
Brian Salomon05441c42017-05-15 16:45:49 -04001995 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001996 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001997 SkScalar xRadius = ellipse.fXRadius;
1998 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001999
joshualitt76e7fb62015-02-11 08:52:27 -08002000 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002001 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2002 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002003
Brian Osman9d958b52018-11-13 12:46:56 -05002004 // By default, constructed so that inner offset is (0, 0) for all points
2005 SkScalar innerRatioX = -offsetDx;
2006 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002007
Brian Osman9d958b52018-11-13 12:46:56 -05002008 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002009 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002010 innerRatioX = xRadius / ellipse.fInnerXRadius;
2011 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002012 }
joshualitt76e7fb62015-02-11 08:52:27 -08002013
Brian Osman2b6e3902018-11-21 15:29:43 -05002014 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2015 color,
2016 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
2017 origin_centered_tri_strip(innerRatioX + offsetDx,
2018 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002019 }
Brian Salomon49348902018-06-26 09:12:38 -04002020 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00002021 helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002022 }
halcanary9d524f22016-03-29 09:03:52 -07002023
Brian Salomon7eae3e02018-08-07 14:02:38 +00002024 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002025 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002026 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002027 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002028 }
2029
bsalomoncdaa97b2016-03-08 08:30:14 -08002030 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002031 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002032 }
2033
joshualittd96a67b2015-05-05 14:09:05 -07002034 // TODO rewrite to allow positioning on CPU
2035 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002036 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002037 }
2038
Brian Salomon05441c42017-05-15 16:45:49 -04002039 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002040 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002041 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002042 }
2043
Brian Salomon05441c42017-05-15 16:45:49 -04002044 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2045 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002046
Brian Salomon05441c42017-05-15 16:45:49 -04002047 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002048 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002049 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002050 SkScalar fXRadius;
2051 SkScalar fYRadius;
2052 SkScalar fInnerXRadius;
2053 SkScalar fInnerYRadius;
2054 SkScalar fGeoDx;
2055 SkScalar fGeoDy;
2056 DIEllipseStyle fStyle;
2057 SkRect fBounds;
2058 };
2059
Brian Salomon05441c42017-05-15 16:45:49 -04002060 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002061 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002062 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002063
Brian Salomon05441c42017-05-15 16:45:49 -04002064 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002065};
2066
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002067///////////////////////////////////////////////////////////////////////////////
2068
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002069// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002070//
2071// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2072// ____________
2073// |_|________|_|
2074// | | | |
2075// | | | |
2076// | | | |
2077// |_|________|_|
2078// |_|________|_|
2079//
2080// For strokes, we don't draw the center quad.
2081//
2082// For circular roundrects, in the case where the stroke width is greater than twice
2083// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002084// in the center. The shared vertices are duplicated so we can set a different outer radius
2085// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002086// ____________
2087// |_|________|_|
2088// | |\ ____ /| |
2089// | | | | | |
2090// | | |____| | |
2091// |_|/______\|_|
2092// |_|________|_|
2093//
2094// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002095//
2096// For filled rrects that need to provide a distance vector we resuse the overstroke
2097// geometry but make the inner rect degenerate (either a point or a horizontal or
2098// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002099
jvanverth84839f62016-08-29 10:16:40 -07002100static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002101 // clang-format off
2102 // overstroke quads
2103 // we place this at the beginning so that we can skip these indices when rendering normally
2104 16, 17, 19, 16, 19, 18,
2105 19, 17, 23, 19, 23, 21,
2106 21, 23, 22, 21, 22, 20,
2107 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002108
Brian Salomon289e3d82016-12-14 15:52:56 -05002109 // corners
2110 0, 1, 5, 0, 5, 4,
2111 2, 3, 7, 2, 7, 6,
2112 8, 9, 13, 8, 13, 12,
2113 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002114
Brian Salomon289e3d82016-12-14 15:52:56 -05002115 // edges
2116 1, 2, 6, 1, 6, 5,
2117 4, 5, 9, 4, 9, 8,
2118 6, 7, 11, 6, 11, 10,
2119 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002120
Brian Salomon289e3d82016-12-14 15:52:56 -05002121 // center
2122 // we place this at the end so that we can ignore these indices when not rendering as filled
2123 5, 6, 10, 5, 10, 9,
2124 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002125};
Brian Salomon289e3d82016-12-14 15:52:56 -05002126
jvanverth84839f62016-08-29 10:16:40 -07002127// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002128static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002129
jvanverth84839f62016-08-29 10:16:40 -07002130// overstroke count is arraysize minus the center indices
2131static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2132// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002133static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002134// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002135static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2136static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002137static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002138
jvanverthc3d0e422016-08-25 08:12:35 -07002139enum RRectType {
2140 kFill_RRectType,
2141 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002142 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002143};
2144
jvanverth84839f62016-08-29 10:16:40 -07002145static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002146 switch (type) {
2147 case kFill_RRectType:
2148 case kStroke_RRectType:
2149 return kVertsPerStandardRRect;
2150 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002151 return kVertsPerOverstrokeRRect;
2152 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002153 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002154 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002155}
2156
2157static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002158 switch (type) {
2159 case kFill_RRectType:
2160 return kIndicesPerFillRRect;
2161 case kStroke_RRectType:
2162 return kIndicesPerStrokeRRect;
2163 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002164 return kIndicesPerOverstrokeRRect;
2165 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002166 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002167 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002168}
2169
2170static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002171 switch (type) {
2172 case kFill_RRectType:
2173 case kStroke_RRectType:
2174 return gStandardRRectIndices;
2175 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002176 return gOverstrokeRRectIndices;
2177 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002178 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002179 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002180}
2181
joshualitt76e7fb62015-02-11 08:52:27 -08002182///////////////////////////////////////////////////////////////////////////////////////////////////
2183
Robert Phillips79839d42016-10-06 15:03:34 -04002184// For distance computations in the interior of filled rrects we:
2185//
2186// add a interior degenerate (point or line) rect
2187// each vertex of that rect gets -outerRad as its radius
2188// this makes the computation of the distance to the outer edge be negative
2189// negative values are caught and then handled differently in the GP's onEmitCode
2190// each vertex is also given the normalized x & y distance from the interior rect's edge
2191// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2192
Brian Salomon05441c42017-05-15 16:45:49 -04002193class CircularRRectOp : public GrMeshDrawOp {
2194private:
2195 using Helper = GrSimpleMeshDrawOpHelper;
2196
joshualitt76e7fb62015-02-11 08:52:27 -08002197public:
Brian Salomon25a88092016-12-01 09:36:50 -05002198 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002199
bsalomon4b4a7cc2016-07-08 04:42:54 -07002200 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2201 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002202 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2203 GrPaint&& paint,
2204 const SkMatrix& viewMatrix,
2205 const SkRect& devRect,
2206 float devRadius,
2207 float devStrokeWidth,
2208 bool strokeOnly) {
2209 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
2210 devRect, devRadius,
2211 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002212 }
Brian Osmancf860852018-10-31 14:04:39 -04002213 CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002214 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2215 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002216 : INHERITED(ClassID())
2217 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002218 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002219 SkRect bounds = devRect;
2220 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2221 SkScalar innerRadius = 0.0f;
2222 SkScalar outerRadius = devRadius;
2223 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002224 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002225 if (devStrokeWidth > 0) {
2226 if (SkScalarNearlyZero(devStrokeWidth)) {
2227 halfWidth = SK_ScalarHalf;
2228 } else {
2229 halfWidth = SkScalarHalf(devStrokeWidth);
2230 }
joshualitt76e7fb62015-02-11 08:52:27 -08002231
bsalomon4b4a7cc2016-07-08 04:42:54 -07002232 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002233 // Outset stroke by 1/4 pixel
2234 devStrokeWidth += 0.25f;
2235 // If stroke is greater than width or height, this is still a fill
2236 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002237 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002238 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002239 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002240 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002241 }
2242 outerRadius += halfWidth;
2243 bounds.outset(halfWidth, halfWidth);
2244 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002245
bsalomon4b4a7cc2016-07-08 04:42:54 -07002246 // The radii are outset for two reasons. First, it allows the shader to simply perform
2247 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2248 // Second, the outer radius is used to compute the verts of the bounding box that is
2249 // rendered and the outset ensures the box will cover all partially covered by the rrect
2250 // corners.
2251 outerRadius += SK_ScalarHalf;
2252 innerRadius -= SK_ScalarHalf;
2253
bsalomon88cf17d2016-07-08 06:40:56 -07002254 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2255
2256 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07002257 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2258
Brian Salomon05441c42017-05-15 16:45:49 -04002259 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002260 fVertCount = rrect_type_to_vert_count(type);
2261 fIndexCount = rrect_type_to_index_count(type);
2262 fAllFill = (kFill_RRectType == type);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002263 fWideColor = !SkPMColor4fFitsInBytes(color);
joshualitt76e7fb62015-02-11 08:52:27 -08002264 }
2265
Brian Salomon289e3d82016-12-14 15:52:56 -05002266 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002267
Brian Salomon7d94bb52018-10-12 14:37:19 -04002268 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002269 fHelper.visitProxies(func);
2270 }
2271
Brian Osman9a390ac2018-11-12 09:47:48 -05002272#ifdef SK_DEBUG
jvanverthc3d0e422016-08-25 08:12:35 -07002273 SkString dumpInfo() const override {
2274 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002275 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002276 string.appendf(
2277 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2278 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002279 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002280 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2281 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2282 fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002283 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002284 string += fHelper.dumpInfo();
2285 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002286 return string;
2287 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002288#endif
jvanverthc3d0e422016-08-25 08:12:35 -07002289
Chris Dalton4b62aed2019-01-15 11:53:00 -07002290 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04002291 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07002292 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2293 color);
Brian Salomon05441c42017-05-15 16:45:49 -04002294 }
2295
2296 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2297
Brian Salomon92aee3d2016-12-21 09:20:25 -05002298private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002299 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002300 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002301 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002302 SkASSERT(smInset < bigInset);
2303
2304 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002305 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2306 color,
2307 xOffset, 0.0f,
2308 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002309
2310 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002311 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2312 color,
2313 xOffset, 0.0f,
2314 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002315
Brian Osmana1d4eb92018-12-06 16:33:10 -05002316 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2317 color,
2318 0.0f, 0.0f,
2319 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002320
Brian Osmana1d4eb92018-12-06 16:33:10 -05002321 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2322 color,
2323 0.0f, 0.0f,
2324 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002325
Brian Osmana1d4eb92018-12-06 16:33:10 -05002326 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2327 color,
2328 0.0f, 0.0f,
2329 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002330
Brian Osmana1d4eb92018-12-06 16:33:10 -05002331 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2332 color,
2333 0.0f, 0.0f,
2334 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002335
2336 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002337 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2338 color,
2339 xOffset, 0.0f,
2340 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002341
2342 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002343 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2344 color,
2345 xOffset, 0.0f,
2346 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002347 }
2348
Brian Salomon91326c32017-08-09 16:02:19 -04002349 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002350 // Invert the view matrix as a local matrix (if any other processors require coords).
2351 SkMatrix localMatrix;
2352 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002353 return;
2354 }
2355
2356 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002357 sk_sp<GrGeometryProcessor> gp(
Brian Osmana1d4eb92018-12-06 16:33:10 -05002358 new CircleGeometryProcessor(!fAllFill, false, false, false, false, fWideColor,
Brian Osmane3caf2d2018-11-21 13:48:36 -05002359 localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002360
jvanverth84839f62016-08-29 10:16:40 -07002361 const GrBuffer* vertexBuffer;
2362 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002363
Brian Osmana1d4eb92018-12-06 16:33:10 -05002364 GrVertexWriter verts{target->makeVertexSpace(gp->vertexStride(), fVertCount,
2365 &vertexBuffer, &firstVertex)};
2366 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002367 SkDebugf("Could not allocate vertices\n");
2368 return;
2369 }
2370
jvanverth84839f62016-08-29 10:16:40 -07002371 const GrBuffer* indexBuffer = nullptr;
2372 int firstIndex = 0;
2373 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2374 if (!indices) {
2375 SkDebugf("Could not allocate indices\n");
2376 return;
2377 }
2378
2379 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002380 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002381 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002382 SkScalar outerRadius = rrect.fOuterRadius;
2383 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002384
Brian Salomon289e3d82016-12-14 15:52:56 -05002385 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2386 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002387
Brian Salomon289e3d82016-12-14 15:52:56 -05002388 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002389 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002390 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002391 SkScalar innerRadius = rrect.fType != kFill_RRectType
2392 ? rrect.fInnerRadius / rrect.fOuterRadius
2393 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002394 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002395 verts.write(bounds.fLeft, yCoords[i],
2396 color,
2397 -1.0f, yOuterRadii[i],
2398 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002399
Brian Osmana1d4eb92018-12-06 16:33:10 -05002400 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2401 color,
2402 0.0f, yOuterRadii[i],
2403 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002404
Brian Osmana1d4eb92018-12-06 16:33:10 -05002405 verts.write(bounds.fRight - outerRadius, yCoords[i],
2406 color,
2407 0.0f, yOuterRadii[i],
2408 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002409
Brian Osmana1d4eb92018-12-06 16:33:10 -05002410 verts.write(bounds.fRight, yCoords[i],
2411 color,
2412 1.0f, yOuterRadii[i],
2413 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002414 }
jvanverthc3d0e422016-08-25 08:12:35 -07002415 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002416 // Effectively this is an additional stroked rrect, with its
2417 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2418 // This will give us correct AA in the center and the correct
2419 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002420 //
jvanvertha4f1af82016-08-29 07:17:47 -07002421 // Also, the outer offset is a constant vector pointing to the right, which
2422 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002423 if (kOverstroke_RRectType == rrect.fType) {
2424 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002425
Brian Salomon05441c42017-05-15 16:45:49 -04002426 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002427 // this is the normalized distance from the outer rectangle of this
2428 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002429 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002430
Brian Osmana1d4eb92018-12-06 16:33:10 -05002431 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002432 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002433 }
jvanverth6a397612016-08-26 08:15:33 -07002434
Brian Salomon05441c42017-05-15 16:45:49 -04002435 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2436 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002437 for (int i = 0; i < primIndexCount; ++i) {
2438 *indices++ = primIndices[i] + currStartVertex;
2439 }
2440
Brian Salomon05441c42017-05-15 16:45:49 -04002441 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002442 }
2443
Brian Salomon7eae3e02018-08-07 14:02:38 +00002444 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
2445 mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
2446 GrPrimitiveRestart::kNo);
2447 mesh->setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04002448 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00002449 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002450 }
2451
Brian Salomon7eae3e02018-08-07 14:02:38 +00002452 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002453 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002454
2455 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002456 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002457 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002458 }
2459
Brian Salomon05441c42017-05-15 16:45:49 -04002460 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002461 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002462 }
2463
Brian Salomon05441c42017-05-15 16:45:49 -04002464 if (fHelper.usesLocalCoords() &&
2465 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002466 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002467 }
2468
Brian Salomon05441c42017-05-15 16:45:49 -04002469 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002470 fVertCount += that->fVertCount;
2471 fIndexCount += that->fIndexCount;
2472 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002473 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002474 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002475 }
2476
Brian Salomon05441c42017-05-15 16:45:49 -04002477 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002478 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002479 SkScalar fInnerRadius;
2480 SkScalar fOuterRadius;
2481 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002482 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002483 };
2484
Brian Salomon289e3d82016-12-14 15:52:56 -05002485 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002486 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002487 int fVertCount;
2488 int fIndexCount;
2489 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002490 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002491 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002492
Brian Salomon05441c42017-05-15 16:45:49 -04002493 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002494};
2495
jvanverth84839f62016-08-29 10:16:40 -07002496static const int kNumRRectsInIndexBuffer = 256;
2497
2498GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2499GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002500static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2501 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002502 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2503 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2504 switch (type) {
2505 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002506 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002507 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2508 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002509 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002510 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002511 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2512 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002513 default:
2514 SkASSERT(false);
2515 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002516 }
jvanverth84839f62016-08-29 10:16:40 -07002517}
2518
Brian Salomon05441c42017-05-15 16:45:49 -04002519class EllipticalRRectOp : public GrMeshDrawOp {
2520private:
2521 using Helper = GrSimpleMeshDrawOpHelper;
2522
joshualitt76e7fb62015-02-11 08:52:27 -08002523public:
Brian Salomon25a88092016-12-01 09:36:50 -05002524 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002525
bsalomon4b4a7cc2016-07-08 04:42:54 -07002526 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2527 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002528 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2529 GrPaint&& paint,
2530 const SkMatrix& viewMatrix,
2531 const SkRect& devRect,
2532 float devXRadius,
2533 float devYRadius,
2534 SkVector devStrokeWidths,
2535 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002536 SkASSERT(devXRadius > 0.5);
2537 SkASSERT(devYRadius > 0.5);
2538 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2539 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002540 if (devStrokeWidths.fX > 0) {
2541 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2542 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2543 } else {
2544 devStrokeWidths.scale(SK_ScalarHalf);
2545 }
joshualitt76e7fb62015-02-11 08:52:27 -08002546
bsalomon4b4a7cc2016-07-08 04:42:54 -07002547 // we only handle thick strokes for near-circular ellipses
2548 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002549 (SK_ScalarHalf * devXRadius > devYRadius ||
2550 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002551 return nullptr;
2552 }
2553
2554 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002555 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2556 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002557 return nullptr;
2558 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002559 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2560 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002561 return nullptr;
2562 }
Brian Salomon05441c42017-05-15 16:45:49 -04002563 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002564 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
2565 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002566 devXRadius, devYRadius, devStrokeWidths,
2567 strokeOnly);
2568 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002569
Brian Osmancf860852018-10-31 14:04:39 -04002570 EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002571 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2572 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002573 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04002574 SkScalar innerXRadius = 0.0f;
2575 SkScalar innerYRadius = 0.0f;
2576 SkRect bounds = devRect;
2577 bool stroked = false;
2578 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002579 // this is legit only if scale & translation (which should be the case at the moment)
2580 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002581 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2582 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002583 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2584 }
2585
Brian Salomon05441c42017-05-15 16:45:49 -04002586 devXRadius += devStrokeHalfWidths.fX;
2587 devYRadius += devStrokeHalfWidths.fY;
2588 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002589 }
2590
Brian Salomon05441c42017-05-15 16:45:49 -04002591 fStroked = stroked;
2592 fViewMatrixIfUsingLocalCoords = viewMatrix;
2593 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002594 // Expand the rect for aa in order to generate the correct vertices.
2595 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002596 fWideColor = !SkPMColor4fFitsInBytes(color);
Brian Salomon05441c42017-05-15 16:45:49 -04002597 fRRects.emplace_back(
2598 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002599 }
2600
Brian Salomon289e3d82016-12-14 15:52:56 -05002601 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002602
Brian Salomon7d94bb52018-10-12 14:37:19 -04002603 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002604 fHelper.visitProxies(func);
2605 }
2606
Brian Osman9a390ac2018-11-12 09:47:48 -05002607#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002608 SkString dumpInfo() const override {
2609 SkString string;
2610 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002611 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002612 string.appendf(
2613 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2614 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002615 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002616 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2617 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002618 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002619 string += fHelper.dumpInfo();
2620 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002621 return string;
2622 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002623#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002624
Chris Dalton4b62aed2019-01-15 11:53:00 -07002625 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04002626 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07002627 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2628 color);
Brian Salomon05441c42017-05-15 16:45:49 -04002629 }
2630
2631 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2632
bsalomone46f9fe2015-08-18 06:05:14 -07002633private:
Brian Salomon91326c32017-08-09 16:02:19 -04002634 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002635 SkMatrix localMatrix;
2636 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002637 return;
2638 }
2639
2640 // Setup geometry processor
Brian Osmana1d4eb92018-12-06 16:33:10 -05002641 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor,
2642 localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002643
bsalomonb5238a72015-05-05 07:49:49 -07002644 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002645 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002646 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2647 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002648
Brian Osmana1d4eb92018-12-06 16:33:10 -05002649 PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
Brian Salomon7eae3e02018-08-07 14:02:38 +00002650 indexBuffer.get(), kVertsPerStandardRRect, indicesPerInstance,
2651 fRRects.count());
Brian Osmana1d4eb92018-12-06 16:33:10 -05002652 GrVertexWriter verts{helper.vertices()};
2653 if (!verts.fPtr || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002654 SkDebugf("Could not allocate vertices\n");
2655 return;
2656 }
2657
Brian Salomon05441c42017-05-15 16:45:49 -04002658 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002659 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002660 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002661 float reciprocalRadii[4] = {
2662 SkScalarInvert(rrect.fXRadius),
2663 SkScalarInvert(rrect.fYRadius),
2664 SkScalarInvert(rrect.fInnerXRadius),
2665 SkScalarInvert(rrect.fInnerYRadius)
2666 };
joshualitt76e7fb62015-02-11 08:52:27 -08002667
2668 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002669 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2670 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002671
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002672 SkScalar xMaxOffset = xOuterRadius;
2673 SkScalar yMaxOffset = yOuterRadius;
2674 if (!fStroked) {
2675 // For filled rrects we map a unit circle in the vertex attributes rather than
2676 // computing an ellipse and modifying that distance, so we normalize to 1.
2677 xMaxOffset /= rrect.fXRadius;
2678 yMaxOffset /= rrect.fYRadius;
2679 }
2680
Brian Salomon05441c42017-05-15 16:45:49 -04002681 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002682
Brian Salomon289e3d82016-12-14 15:52:56 -05002683 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2684 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002685 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002686 SK_ScalarNearlyZero, // we're using inversesqrt() in
2687 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002688 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08002689
2690 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002691 verts.write(bounds.fLeft, yCoords[i],
2692 color,
2693 xMaxOffset, yOuterOffsets[i],
2694 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002695
Brian Osmana1d4eb92018-12-06 16:33:10 -05002696 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
2697 color,
2698 SK_ScalarNearlyZero, yOuterOffsets[i],
2699 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002700
Brian Osmana1d4eb92018-12-06 16:33:10 -05002701 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
2702 color,
2703 SK_ScalarNearlyZero, yOuterOffsets[i],
2704 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002705
Brian Osmana1d4eb92018-12-06 16:33:10 -05002706 verts.write(bounds.fRight, yCoords[i],
2707 color,
2708 xMaxOffset, yOuterOffsets[i],
2709 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002710 }
2711 }
Brian Salomon49348902018-06-26 09:12:38 -04002712 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00002713 helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002714 }
2715
Brian Salomon7eae3e02018-08-07 14:02:38 +00002716 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002717 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002718
Brian Salomon05441c42017-05-15 16:45:49 -04002719 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002720 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002721 }
2722
bsalomoncdaa97b2016-03-08 08:30:14 -08002723 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002724 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002725 }
2726
Brian Salomon05441c42017-05-15 16:45:49 -04002727 if (fHelper.usesLocalCoords() &&
2728 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002729 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002730 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002731
Brian Salomon05441c42017-05-15 16:45:49 -04002732 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05002733 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002734 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002735 }
2736
Brian Salomon05441c42017-05-15 16:45:49 -04002737 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002738 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002739 SkScalar fXRadius;
2740 SkScalar fYRadius;
2741 SkScalar fInnerXRadius;
2742 SkScalar fInnerYRadius;
2743 SkRect fDevBounds;
2744 };
2745
Brian Salomon289e3d82016-12-14 15:52:56 -05002746 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002747 Helper fHelper;
2748 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002749 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002750 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002751
Brian Salomon05441c42017-05-15 16:45:49 -04002752 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002753};
2754
Robert Phillips7c525e62018-06-12 10:11:12 -04002755static std::unique_ptr<GrDrawOp> make_rrect_op(GrContext* context,
2756 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002757 const SkMatrix& viewMatrix,
2758 const SkRRect& rrect,
2759 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002760 SkASSERT(viewMatrix.rectStaysRect());
2761 SkASSERT(rrect.isSimple());
2762 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002763
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002764 // RRect ops only handle simple, but not too simple, rrects.
2765 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002766 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002767 SkRect bounds;
2768 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002769
Mike Reed242135a2018-02-22 13:41:39 -05002770 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05002771 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2772 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2773 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2774 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002775
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002776 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002777
bsalomon4b4a7cc2016-07-08 04:42:54 -07002778 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2779 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002780 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002781
Brian Salomon289e3d82016-12-14 15:52:56 -05002782 bool isStrokeOnly =
2783 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002784 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2785
jvanverthc3d0e422016-08-25 08:12:35 -07002786 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002787 if (hasStroke) {
2788 if (SkStrokeRec::kHairline_Style == style) {
2789 scaledStroke.set(1, 1);
2790 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002791 scaledStroke.fX = SkScalarAbs(
2792 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2793 scaledStroke.fY = SkScalarAbs(
2794 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002795 }
2796
jvanverthc3d0e422016-08-25 08:12:35 -07002797 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2798 // for non-circular rrects, if half of strokewidth is greater than radius,
2799 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002800 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2801 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002802 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002803 }
2804 }
2805
2806 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2807 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2808 // patch will have fractional coverage. This only matters when the interior is actually filled.
2809 // We could consider falling back to rect rendering here, since a tiny radius is
2810 // indistinguishable from a square corner.
2811 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002812 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002813 }
2814
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002815 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002816 if (isCircular) {
Robert Phillips7c525e62018-06-12 10:11:12 -04002817 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, xRadius,
2818 scaledStroke.fX, isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05002819 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002820 } else {
Robert Phillips7c525e62018-06-12 10:11:12 -04002821 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
2822 xRadius, yRadius, scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002823 }
joshualitt3e708c52015-04-30 13:49:27 -07002824}
2825
Robert Phillips7c525e62018-06-12 10:11:12 -04002826std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrContext* context,
2827 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002828 const SkMatrix& viewMatrix,
2829 const SkRRect& rrect,
2830 const SkStrokeRec& stroke,
2831 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002832 if (rrect.isOval()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04002833 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
2834 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002835 }
2836
2837 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002838 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002839 }
2840
Robert Phillips7c525e62018-06-12 10:11:12 -04002841 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002842}
joshualitt3e708c52015-04-30 13:49:27 -07002843
bsalomon4b4a7cc2016-07-08 04:42:54 -07002844///////////////////////////////////////////////////////////////////////////////
2845
Robert Phillips7c525e62018-06-12 10:11:12 -04002846std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrContext* context,
2847 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002848 const SkMatrix& viewMatrix,
2849 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002850 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002851 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002852 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002853 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04002854 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2855 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002856 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002857 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002858 if (style.hasNonDashPathEffect()) {
2859 return nullptr;
2860 } else if (style.isDashed()) {
2861 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
2862 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
2863 return nullptr;
2864 }
2865 auto onInterval = style.dashIntervals()[0];
2866 auto offInterval = style.dashIntervals()[1];
2867 if (offInterval == 0) {
2868 GrStyle strokeStyle(style.strokeRec(), nullptr);
Robert Phillips7c525e62018-06-12 10:11:12 -04002869 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
2870 strokeStyle, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002871 } else if (onInterval == 0) {
2872 // There is nothing to draw but we have no way to indicate that here.
2873 return nullptr;
2874 }
2875 auto angularOnInterval = onInterval / r;
2876 auto angularOffInterval = offInterval / r;
2877 auto phaseAngle = style.dashPhase() / r;
2878 // Currently this function doesn't accept ovals with different start angles, though
2879 // it could.
2880 static const SkScalar kStartAngle = 0.f;
Robert Phillips7c525e62018-06-12 10:11:12 -04002881 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002882 style.strokeRec().getWidth(), kStartAngle,
2883 angularOnInterval, angularOffInterval, phaseAngle);
2884 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002885 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002886 }
2887
2888 if (style.pathEffect()) {
2889 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002890 }
2891
Stan Ilieveb868aa2017-02-21 11:06:16 -05002892 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002893 if (viewMatrix.rectStaysRect()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04002894 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07002895 }
2896
Stan Ilieveb868aa2017-02-21 11:06:16 -05002897 // Otherwise, if we have shader derivative support, render as device-independent
2898 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04002899 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2900 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2901 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2902 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2903 // Check for near-degenerate matrix
2904 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
2905 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
2906 style.strokeRec());
2907 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05002908 }
2909
bsalomon4b4a7cc2016-07-08 04:42:54 -07002910 return nullptr;
2911}
2912
2913///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002914
Robert Phillips7c525e62018-06-12 10:11:12 -04002915std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrContext* context,
2916 GrPaint&& paint,
2917 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002918 const SkRect& oval, SkScalar startAngle,
2919 SkScalar sweepAngle, bool useCenter,
2920 const GrStyle& style,
2921 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002922 SkASSERT(!oval.isEmpty());
2923 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002924 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002925 if (SkScalarAbs(sweepAngle) >= 360.f) {
2926 return nullptr;
2927 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002928 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2929 return nullptr;
2930 }
2931 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002932 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2933 useCenter};
Robert Phillips7c525e62018-06-12 10:11:12 -04002934 return CircleOp::Make(context, std::move(paint), viewMatrix,
2935 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002936}
2937
2938///////////////////////////////////////////////////////////////////////////////
2939
Hal Canary6f6961e2017-01-31 13:50:44 -05002940#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002941
Brian Salomon05441c42017-05-15 16:45:49 -04002942GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002943 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002944 SkScalar rotate = random->nextSScalar1() * 360.f;
2945 SkScalar translateX = random->nextSScalar1() * 1000.f;
2946 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04002947 SkScalar scale;
2948 do {
2949 scale = random->nextSScalar1() * 100.f;
2950 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07002951 SkMatrix viewMatrix;
2952 viewMatrix.setRotate(rotate);
2953 viewMatrix.postTranslate(translateX, translateY);
2954 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002955 SkRect circle = GrTest::TestSquare(random);
2956 SkPoint center = {circle.centerX(), circle.centerY()};
2957 SkScalar radius = circle.width() / 2.f;
2958 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002959 CircleOp::ArcParams arcParamsTmp;
2960 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002961 if (random->nextBool()) {
2962 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002963 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2964 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002965 arcParams = &arcParamsTmp;
2966 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002967 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
2968 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002969 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002970 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002971 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002972 }
Mike Klein16885072018-12-11 09:54:31 -05002973 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002974 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002975}
2976
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002977GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
2978 SkScalar rotate = random->nextSScalar1() * 360.f;
2979 SkScalar translateX = random->nextSScalar1() * 1000.f;
2980 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04002981 SkScalar scale;
2982 do {
2983 scale = random->nextSScalar1() * 100.f;
2984 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002985 SkMatrix viewMatrix;
2986 viewMatrix.setRotate(rotate);
2987 viewMatrix.postTranslate(translateX, translateY);
2988 viewMatrix.postScale(scale, scale);
2989 SkRect circle = GrTest::TestSquare(random);
2990 SkPoint center = {circle.centerX(), circle.centerY()};
2991 SkScalar radius = circle.width() / 2.f;
2992 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
2993 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
2994 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
2995 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
2996 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04002997 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
2998 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002999 startAngle, onAngle, offAngle, phase);
3000}
3001
Brian Salomon05441c42017-05-15 16:45:49 -04003002GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003003 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003004 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003005 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3006 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003007}
3008
Brian Salomon05441c42017-05-15 16:45:49 -04003009GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003010 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003011 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003012 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3013 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003014}
3015
Brian Salomon05441c42017-05-15 16:45:49 -04003016GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003017 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003018 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003019 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
3020 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003021}
3022
3023#endif