blob: 53e776b44f2cfb88c1e6c7db5c9e726b0ef86aaa [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 Nicholase1f55022019-02-05 17:17:40 -0500160 fragBuilder->codeAppend("half distanceToOuterEdge = half(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 Nicholase1f55022019-02-05 17:17:40 -0500164 "half distanceToInnerEdge = half(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 Nicholase1f55022019-02-05 17:17:40 -0500171 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
172 "clipPlane.xy) + 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 Nicholase1f55022019-02-05 17:17:40 -0500175 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
176 "isectPlane.xy) + 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 Nicholase1f55022019-02-05 17:17:40 -0500180 "clip = saturate(clip + half(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(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500189 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
190 " roundCapCenters.xy)));"
191 "half dcap2 = half(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;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500330 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400331 // We can happen to be perfectly divisible.
332 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500333 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400334 }
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) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500341 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400342 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500343 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400344 }
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) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500354 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400355 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500356 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400357 }
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 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500364 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400365 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.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500401 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400402 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500403 half distanceToInnerEdge = half(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
Ethan Nicholase1f55022019-02-05 17:17:40 -0500407 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408 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;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500412 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
413 half(dashParams.w));
414 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
415 half(dashParams.y) + half(dashParams.x) -
416 half(dashParams.w));
417 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
418 half(-dashParams.y) + half(dashParams.x) -
419 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400420 half dashAlpha = 0;
421 )");
422 fragBuilder->codeAppendf(R"(
423 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500424 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400425 currDash.y = min(currDash.y, lastIntervalLength);
426 if (nextDash.x >= lastIntervalLength) {
427 // The next dash is outside the 0..2pi range, throw it away
428 nextDash.xy = half2(1000);
429 } else {
430 // Clip the end of the next dash to the end of the circle
431 nextDash.y = min(nextDash.y, lastIntervalLength);
432 }
433 }
434 )", fnName.c_str(), fnName.c_str());
435 fragBuilder->codeAppendf(R"(
436 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500437 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400438 currDash.x = max(currDash.x, 0);
439 if (prevDash.y <= 0) {
440 // The previous dash is outside the 0..2pi range, throw it away
441 prevDash.xy = half2(1000);
442 } else {
443 // Clip the start previous dash to the start of the circle
444 prevDash.x = max(prevDash.x, 0);
445 }
446 }
447 )", fnName.c_str(), fnName.c_str());
448 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500449 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
450 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
451 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400452 dashAlpha = min(dashAlpha, 1);
453 edgeAlpha *= dashAlpha;
454 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
455 fnName.c_str());
456 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
457 }
458
459 static void GenKey(const GrGeometryProcessor& gp,
460 const GrShaderCaps&,
461 GrProcessorKeyBuilder* b) {
462 const ButtCapDashedCircleGeometryProcessor& bcscgp =
463 gp.cast<ButtCapDashedCircleGeometryProcessor>();
464 b->add32(bcscgp.fLocalMatrix.hasPerspective());
465 }
466
467 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
468 FPCoordTransformIter&& transformIter) override {
469 this->setTransformDataHelper(
470 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
471 &transformIter);
472 }
473
474 private:
475 typedef GrGLSLGeometryProcessor INHERITED;
476 };
477
478 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500479 Attribute fInPosition;
480 Attribute fInColor;
481 Attribute fInCircleEdge;
482 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400483
484 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
485
486 typedef GrGeometryProcessor INHERITED;
487};
488
489#if GR_TEST_UTILS
490sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500491 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400492 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Brian Osmane3caf2d2018-11-21 13:48:36 -0500493 return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(wideColor, matrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400494}
495#endif
496
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000497///////////////////////////////////////////////////////////////////////////////
498
499/**
500 * 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 +0000501 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
502 * in both x and y directions.
503 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000504 * 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 +0000505 */
506
bsalomoncdaa97b2016-03-08 08:30:14 -0800507class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000508public:
Brian Osmane3caf2d2018-11-21 13:48:36 -0500509 EllipseGeometryProcessor(bool stroke, bool wideColor, const SkMatrix& localMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000510 : INHERITED(kEllipseGeometryProcessor_ClassID)
511 , fLocalMatrix(localMatrix) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500512 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500513 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500514 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
515 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
516 this->setVertexAttributes(&fInPosition, 4);
Brian Salomonea26d6b2018-01-23 20:33:21 +0000517 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000518 }
519
Brian Salomond3b65972017-03-22 12:05:03 -0400520 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000521
mtklein36352bf2015-03-25 18:17:31 -0700522 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800523
Brian Salomon94efbf52016-11-29 13:43:05 -0500524 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700525 GLSLProcessor::GenKey(*this, caps, b);
526 }
527
Brian Salomon94efbf52016-11-29 13:43:05 -0500528 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700529 return new GLSLProcessor();
530 }
531
532private:
egdaniel57d3b032015-11-13 11:57:27 -0800533 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000534 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800535 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000536
Brian Salomon289e3d82016-12-14 15:52:56 -0500537 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800538 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800539 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800540 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800541 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000542
joshualittabb52a12015-01-13 15:02:10 -0800543 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800544 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800545
Chris Dalton27372882017-12-08 13:34:21 -0700546 GrGLSLVarying ellipseOffsets(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800547 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800548 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500549 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000550
Chris Dalton27372882017-12-08 13:34:21 -0700551 GrGLSLVarying ellipseRadii(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800552 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500553 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800554
Chris Dalton60283612018-02-14 13:38:14 -0700555 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700556 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500557 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800558
joshualittabb52a12015-01-13 15:02:10 -0800559 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500560 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800561
562 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800563 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800564 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800565 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500566 egp.fInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700567 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700568 args.fFPCoordTransformHandler);
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400569 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
570 // to compute both the edges because we need two separate test equations for
571 // the single offset.
572 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
573 // the distance by the gradient, non-uniformly scaled by the inverse of the
574 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800575
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000576 // for outer curve
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400577 fragBuilder->codeAppendf("half2 offset = %s;", ellipseOffsets.fsIn());
578 if (egp.fStroke) {
579 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
580 }
581 fragBuilder->codeAppend("half test = dot(offset, offset) - 1.0;");
582 fragBuilder->codeAppendf("half2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400583 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700584
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000585 // avoid calling inversesqrt on zero.
Jim Van Verthd3420de2018-06-28 15:22:43 -0400586 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500587 fragBuilder->codeAppend("half invlen = half(inversesqrt(grad_dot));");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400588 fragBuilder->codeAppend("half edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000589
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000590 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800591 if (egp.fStroke) {
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400592 fragBuilder->codeAppendf("offset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800593 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400594 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
595 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
Ethan Nicholase1f55022019-02-05 17:17:40 -0500596 fragBuilder->codeAppend("invlen = half(inversesqrt(dot(grad, grad)));");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400597 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000598 }
599
Brian Salomonea26d6b2018-01-23 20:33:21 +0000600 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000601 }
602
robertphillips46d36f02015-01-18 08:14:14 -0800603 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500604 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700605 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800606 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
607 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700608 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700609 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000610 }
611
bsalomona624bf32016-09-20 09:12:47 -0700612 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
613 FPCoordTransformIter&& transformIter) override {
614 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
615 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700616 }
617
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000618 private:
egdaniele659a582015-11-13 09:55:43 -0800619 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000620 };
621
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500622 Attribute fInPosition;
623 Attribute fInColor;
624 Attribute fInEllipseOffset;
625 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400626
joshualitte3ababe2015-05-15 07:56:07 -0700627 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000628 bool fStroke;
629
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400630 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000631
joshualitt249af152014-09-15 11:41:13 -0700632 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000633};
634
bsalomoncdaa97b2016-03-08 08:30:14 -0800635GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000636
Hal Canary6f6961e2017-01-31 13:50:44 -0500637#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700638sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000639 return sk_sp<GrGeometryProcessor>(
Brian Osmane3caf2d2018-11-21 13:48:36 -0500640 new EllipseGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
641 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000642}
Hal Canary6f6961e2017-01-31 13:50:44 -0500643#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000644
645///////////////////////////////////////////////////////////////////////////////
646
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000647/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000648 * 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 +0000649 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
650 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
651 * using differentials.
652 *
653 * The result is device-independent and can be used with any affine matrix.
654 */
655
bsalomoncdaa97b2016-03-08 08:30:14 -0800656enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000657
bsalomoncdaa97b2016-03-08 08:30:14 -0800658class DIEllipseGeometryProcessor : public GrGeometryProcessor {
659public:
Brian Osmane3caf2d2018-11-21 13:48:36 -0500660 DIEllipseGeometryProcessor(bool wideColor, const SkMatrix& viewMatrix, DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400661 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000662 , fViewMatrix(viewMatrix) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000663 fStyle = style;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500664 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500665 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500666 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
667 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
668 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000669 }
670
Brian Salomond3b65972017-03-22 12:05:03 -0400671 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000672
mtklein36352bf2015-03-25 18:17:31 -0700673 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000674
Brian Salomon94efbf52016-11-29 13:43:05 -0500675 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700676 GLSLProcessor::GenKey(*this, caps, b);
677 }
halcanary9d524f22016-03-29 09:03:52 -0700678
Brian Salomon94efbf52016-11-29 13:43:05 -0500679 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700680 return new GLSLProcessor();
681 }
682
683private:
egdaniel57d3b032015-11-13 11:57:27 -0800684 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000685 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500686 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000687
joshualitt465283c2015-09-11 08:19:35 -0700688 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800689 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800690 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800691 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800692 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000693
joshualittabb52a12015-01-13 15:02:10 -0800694 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800695 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800696
Chris Dalton27372882017-12-08 13:34:21 -0700697 GrGLSLVarying offsets0(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800698 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500699 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700700
Chris Dalton27372882017-12-08 13:34:21 -0700701 GrGLSLVarying offsets1(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800702 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500703 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800704
Chris Dalton60283612018-02-14 13:38:14 -0700705 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500706 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800707
joshualittabb52a12015-01-13 15:02:10 -0800708 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400709 this->writeOutputPosition(vertBuilder,
710 uniformHandler,
711 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500712 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400713 diegp.fViewMatrix,
714 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800715
716 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800717 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800718 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800719 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500720 diegp.fInPosition.asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700721 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800722
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000723 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400724 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
725 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500726 fragBuilder->codeAppendf("half2 duvdx = half2(dFdx(%s));", offsets0.fsIn());
727 fragBuilder->codeAppendf("half2 duvdy = half2(dFdy(%s));", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500728 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400729 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
730 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500731 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000732
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400733 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000734 // avoid calling inversesqrt on zero.
Jim Van Verthd3420de2018-06-28 15:22:43 -0400735 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500736 fragBuilder->codeAppend("half invlen = half(inversesqrt(grad_dot));");
bsalomoncdaa97b2016-03-08 08:30:14 -0800737 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000738 // can probably do this with one step
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400739 fragBuilder->codeAppend("half edgeAlpha = saturate(1.0-test*invlen);");
740 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000741 } else {
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400742 fragBuilder->codeAppend("half edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000743 }
744
745 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800746 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800747 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
748 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500749 fragBuilder->codeAppendf("duvdx = half2(dFdx(%s));", offsets1.fsIn());
750 fragBuilder->codeAppendf("duvdy = half2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500751 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400752 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
753 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500754 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Ethan Nicholase1f55022019-02-05 17:17:40 -0500755 fragBuilder->codeAppend("invlen = half(inversesqrt(dot(grad, grad)));");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400756 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000757 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000758
759 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000760 }
761
robertphillips46d36f02015-01-18 08:14:14 -0800762 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500763 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700764 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800765 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
766 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700767 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700768 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000769 }
770
bsalomona624bf32016-09-20 09:12:47 -0700771 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
772 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800773 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700774
bsalomon31df31c2016-08-17 09:00:24 -0700775 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
776 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700777 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800778 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700779 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
780 }
bsalomona624bf32016-09-20 09:12:47 -0700781 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000782 }
783
784 private:
joshualitt5559ca22015-05-21 15:50:36 -0700785 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700786 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800787
egdaniele659a582015-11-13 09:55:43 -0800788 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000789 };
790
Brian Salomon92be2f72018-06-19 14:33:47 -0400791
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500792 Attribute fInPosition;
793 Attribute fInColor;
794 Attribute fInEllipseOffsets0;
795 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400796
Brian Salomon289e3d82016-12-14 15:52:56 -0500797 SkMatrix fViewMatrix;
798 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000799
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400800 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000801
joshualitt249af152014-09-15 11:41:13 -0700802 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000803};
804
bsalomoncdaa97b2016-03-08 08:30:14 -0800805GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000806
Hal Canary6f6961e2017-01-31 13:50:44 -0500807#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700808sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500809 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
Brian Osmane3caf2d2018-11-21 13:48:36 -0500810 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
811 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000812}
Hal Canary6f6961e2017-01-31 13:50:44 -0500813#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000814
815///////////////////////////////////////////////////////////////////////////////
816
jvanverth6ca48822016-10-07 06:57:32 -0700817// We have two possible cases for geometry for a circle:
818
819// In the case of a normal fill, we draw geometry for the circle as an octagon.
820static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500821 // enter the octagon
822 // clang-format off
823 0, 1, 8, 1, 2, 8,
824 2, 3, 8, 3, 4, 8,
825 4, 5, 8, 5, 6, 8,
826 6, 7, 8, 7, 0, 8
827 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700828};
829
830// For stroked circles, we use two nested octagons.
831static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500832 // enter the octagon
833 // clang-format off
834 0, 1, 9, 0, 9, 8,
835 1, 2, 10, 1, 10, 9,
836 2, 3, 11, 2, 11, 10,
837 3, 4, 12, 3, 12, 11,
838 4, 5, 13, 4, 13, 12,
839 5, 6, 14, 5, 14, 13,
840 6, 7, 15, 6, 15, 14,
841 7, 0, 8, 7, 8, 15,
842 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700843};
844
Brian Osman9d958b52018-11-13 12:46:56 -0500845// Normalized geometry for octagons that circumscribe and lie on a circle:
846
847static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
848static constexpr SkPoint kOctagonOuter[] = {
849 SkPoint::Make(-kOctOffset, -1),
850 SkPoint::Make( kOctOffset, -1),
851 SkPoint::Make( 1, -kOctOffset),
852 SkPoint::Make( 1, kOctOffset),
853 SkPoint::Make( kOctOffset, 1),
854 SkPoint::Make(-kOctOffset, 1),
855 SkPoint::Make(-1, kOctOffset),
856 SkPoint::Make(-1, -kOctOffset),
857};
858
859// cosine and sine of pi/8
860static constexpr SkScalar kCosPi8 = 0.923579533f;
861static constexpr SkScalar kSinPi8 = 0.382683432f;
862static constexpr SkPoint kOctagonInner[] = {
863 SkPoint::Make(-kSinPi8, -kCosPi8),
864 SkPoint::Make( kSinPi8, -kCosPi8),
865 SkPoint::Make( kCosPi8, -kSinPi8),
866 SkPoint::Make( kCosPi8, kSinPi8),
867 SkPoint::Make( kSinPi8, kCosPi8),
868 SkPoint::Make(-kSinPi8, kCosPi8),
869 SkPoint::Make(-kCosPi8, kSinPi8),
870 SkPoint::Make(-kCosPi8, -kSinPi8),
871};
Brian Salomon289e3d82016-12-14 15:52:56 -0500872
jvanverth6ca48822016-10-07 06:57:32 -0700873static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
874static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
875static const int kVertsPerStrokeCircle = 16;
876static const int kVertsPerFillCircle = 9;
877
878static int circle_type_to_vert_count(bool stroked) {
879 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
880}
881
882static int circle_type_to_index_count(bool stroked) {
883 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
884}
885
886static const uint16_t* circle_type_to_indices(bool stroked) {
887 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
888}
889
890///////////////////////////////////////////////////////////////////////////////
891
Brian Salomon05441c42017-05-15 16:45:49 -0400892class CircleOp final : public GrMeshDrawOp {
893private:
894 using Helper = GrSimpleMeshDrawOpHelper;
895
joshualitt76e7fb62015-02-11 08:52:27 -0800896public:
Brian Salomon25a88092016-12-01 09:36:50 -0500897 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700898
bsalomon4f3a0ca2016-08-22 13:14:26 -0700899 /** Optional extra params to render a partial arc rather than a full circle. */
900 struct ArcParams {
901 SkScalar fStartAngleRadians;
902 SkScalar fSweepAngleRadians;
903 bool fUseCenter;
904 };
Brian Salomon05441c42017-05-15 16:45:49 -0400905
Robert Phillips7c525e62018-06-12 10:11:12 -0400906 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
907 GrPaint&& paint,
908 const SkMatrix& viewMatrix,
909 SkPoint center,
910 SkScalar radius,
911 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -0400912 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700913 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700914 if (style.hasPathEffect()) {
915 return nullptr;
916 }
Brian Salomon05441c42017-05-15 16:45:49 -0400917 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700918 SkStrokeRec::Style recStyle = stroke.getStyle();
919 if (arcParams) {
920 // Arc support depends on the style.
921 switch (recStyle) {
922 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500923 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700924 return nullptr;
925 case SkStrokeRec::kFill_Style:
926 // This supports all fills.
927 break;
Brian Salomon45c92202018-04-10 10:53:58 -0400928 case SkStrokeRec::kStroke_Style:
929 // Strokes that don't use the center point are supported with butt and round
930 // caps.
931 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
932 return nullptr;
933 }
934 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700935 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -0400936 // Hairline only supports butt cap. Round caps could be emulated by slightly
937 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700938 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
939 return nullptr;
940 }
941 break;
942 }
943 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400944 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
945 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -0400946 }
947
Brian Osmancf860852018-10-31 14:04:39 -0400948 CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
949 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
950 const ArcParams* arcParams)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000951 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -0400952 const SkStrokeRec& stroke = style.strokeRec();
953 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700954
Brian Salomon45c92202018-04-10 10:53:58 -0400955 fRoundCaps = false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500956 fWideColor = !SkPMColor4fFitsInBytes(color);
Brian Salomon45c92202018-04-10 10:53:58 -0400957
bsalomon4b4a7cc2016-07-08 04:42:54 -0700958 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700959 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700960 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800961
Brian Salomon289e3d82016-12-14 15:52:56 -0500962 bool isStrokeOnly =
963 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700964 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700965
jvanverth6ca48822016-10-07 06:57:32 -0700966 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700967 SkScalar outerRadius = radius;
968 SkScalar halfWidth = 0;
969 if (hasStroke) {
970 if (SkScalarNearlyZero(strokeWidth)) {
971 halfWidth = SK_ScalarHalf;
972 } else {
973 halfWidth = SkScalarHalf(strokeWidth);
974 }
975
976 outerRadius += halfWidth;
977 if (isStrokeOnly) {
978 innerRadius = radius - halfWidth;
979 }
980 }
981
982 // The radii are outset for two reasons. First, it allows the shader to simply perform
983 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
984 // Second, the outer radius is used to compute the verts of the bounding box that is
985 // rendered and the outset ensures the box will cover all partially covered by the circle.
986 outerRadius += SK_ScalarHalf;
987 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700988 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400989 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700990
bsalomon4f3a0ca2016-08-22 13:14:26 -0700991 // This makes every point fully inside the intersection plane.
992 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
993 // This makes every point fully outside the union plane.
994 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -0400995 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700996 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
997 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700998 if (arcParams) {
999 // The shader operates in a space where the circle is translated to be centered at the
1000 // origin. Here we compute points on the unit circle at the starting and ending angles.
1001 SkPoint startPoint, stopPoint;
1002 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
1003 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
1004 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001005
1006 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1007 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1008 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1009 startPoint.normalize();
1010 stopPoint.normalize();
1011
1012 // If the matrix included scale (on one axis) we need to swap our start and end points
1013 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001014 using std::swap;
1015 swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001016 }
1017
Brian Salomon45c92202018-04-10 10:53:58 -04001018 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1019 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1020 SkPoint roundCaps[2];
1021 if (fRoundCaps) {
1022 // Compute the cap center points in the normalized space.
1023 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1024 roundCaps[0] = startPoint * midRadius;
1025 roundCaps[1] = stopPoint * midRadius;
1026 } else {
1027 roundCaps[0] = kUnusedRoundCaps[0];
1028 roundCaps[1] = kUnusedRoundCaps[1];
1029 }
1030
bsalomon4f3a0ca2016-08-22 13:14:26 -07001031 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001032 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1033 // center of the butts.
1034 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001035 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001036 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001037 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001038 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1039 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1040 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001041 if (useCenter) {
1042 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1043 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001044 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1045 if (arcParams->fSweepAngleRadians < 0) {
1046 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001047 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001048 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001049 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001050 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001051 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001052 color,
1053 innerRadius,
1054 outerRadius,
1055 {norm0.fX, norm0.fY, 0.5f},
1056 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1057 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001058 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001059 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001060 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001061 fClipPlaneIsect = false;
1062 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001063 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001064 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001065 color,
1066 innerRadius,
1067 outerRadius,
1068 {norm0.fX, norm0.fY, 0.5f},
1069 {norm1.fX, norm1.fY, 0.5f},
1070 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001071 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001072 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001073 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001074 fClipPlaneIsect = true;
1075 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001076 }
1077 } else {
1078 // We clip to a secant of the original circle.
1079 startPoint.scale(radius);
1080 stopPoint.scale(radius);
1081 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1082 norm.normalize();
1083 if (arcParams->fSweepAngleRadians > 0) {
1084 norm.negate();
1085 }
1086 SkScalar d = -norm.dot(startPoint) + 0.5f;
1087
Brian Salomon05441c42017-05-15 16:45:49 -04001088 fCircles.emplace_back(
1089 Circle{color,
1090 innerRadius,
1091 outerRadius,
1092 {norm.fX, norm.fY, d},
1093 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1094 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001095 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001096 devBounds,
1097 stroked});
1098 fClipPlane = true;
1099 fClipPlaneIsect = false;
1100 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001101 }
1102 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001103 fCircles.emplace_back(
1104 Circle{color,
1105 innerRadius,
1106 outerRadius,
1107 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1108 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1109 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001110 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001111 devBounds,
1112 stroked});
1113 fClipPlane = false;
1114 fClipPlaneIsect = false;
1115 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001116 }
bsalomon88cf17d2016-07-08 06:40:56 -07001117 // Use the original radius and stroke radius for the bounds so that it does not include the
1118 // AA bloat.
1119 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001120 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001121 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1122 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001123 fVertCount = circle_type_to_vert_count(stroked);
1124 fIndexCount = circle_type_to_index_count(stroked);
1125 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001126 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001127
Brian Salomon289e3d82016-12-14 15:52:56 -05001128 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001129
Brian Salomon7d94bb52018-10-12 14:37:19 -04001130 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001131 fHelper.visitProxies(func);
1132 }
1133
Brian Osman9a390ac2018-11-12 09:47:48 -05001134#ifdef SK_DEBUG
robertphillipse004bfc2015-11-16 09:06:59 -08001135 SkString dumpInfo() const override {
1136 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001137 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001138 string.appendf(
1139 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1140 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001141 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001142 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1143 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1144 fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001145 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001146 string += fHelper.dumpInfo();
1147 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001148 return string;
1149 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001150#endif
robertphillipse004bfc2015-11-16 09:06:59 -08001151
Chris Dalton4b62aed2019-01-15 11:53:00 -07001152 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001153 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001154 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1155 color);
Brian Salomon05441c42017-05-15 16:45:49 -04001156 }
1157
1158 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1159
bsalomone46f9fe2015-08-18 06:05:14 -07001160private:
Brian Salomon91326c32017-08-09 16:02:19 -04001161 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001162 SkMatrix localMatrix;
1163 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001164 return;
1165 }
1166
1167 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001168 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
Brian Osmane3caf2d2018-11-21 13:48:36 -05001169 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, fWideColor,
1170 localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001171
Brian Salomon12d22642019-01-29 14:38:50 -05001172 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001173 int firstVertex;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001174 GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
1175 &vertexBuffer, &firstVertex)};
1176 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001177 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001178 return;
1179 }
1180
Brian Salomon12d22642019-01-29 14:38:50 -05001181 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001182 int firstIndex = 0;
1183 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1184 if (!indices) {
1185 SkDebugf("Could not allocate indices\n");
1186 return;
1187 }
1188
1189 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001190 for (const auto& circle : fCircles) {
1191 SkScalar innerRadius = circle.fInnerRadius;
1192 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001193 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001194 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001195
joshualitt76e7fb62015-02-11 08:52:27 -08001196 // The inner radius in the vertex data must be specified in normalized space.
1197 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001198 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001199
1200 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001201 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001202
Brian Osman9a24fee2018-08-03 09:48:42 -04001203 SkVector geoClipPlane = { 0, 0 };
1204 SkScalar offsetClipDist = SK_Scalar1;
1205 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1206 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1207 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1208 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1209 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1210 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1211 // the AA can extend just past the center of the circle.
1212 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1213 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1214 SkAssertResult(geoClipPlane.normalize());
1215 offsetClipDist = 0.5f / halfWidth;
1216 }
1217
Brian Osman7d8f82b2018-11-08 10:24:09 -05001218 for (int i = 0; i < 8; ++i) {
1219 // This clips the normalized offset to the half-plane we computed above. Then we
1220 // compute the vertex position from this.
Brian Osman9d958b52018-11-13 12:46:56 -05001221 SkScalar dist = SkTMin(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
1222 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001223 vertices.write(center + offset * halfWidth,
1224 color,
1225 offset,
1226 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001227 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001228 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001229 }
1230 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001231 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001232 }
1233 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001234 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001235 }
1236 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001237 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001238 }
Brian Salomon45c92202018-04-10 10:53:58 -04001239 }
jvanverth6ca48822016-10-07 06:57:32 -07001240
Brian Salomon05441c42017-05-15 16:45:49 -04001241 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001242 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001243
Brian Osman7d8f82b2018-11-08 10:24:09 -05001244 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001245 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1246 color,
1247 kOctagonInner[i] * innerRadius,
1248 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001249 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001250 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001251 }
1252 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001253 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001254 }
1255 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001256 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001257 }
1258 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001259 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001260 }
Brian Salomon45c92202018-04-10 10:53:58 -04001261 }
jvanverth6ca48822016-10-07 06:57:32 -07001262 } else {
1263 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001264 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001265 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001266 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001267 }
jvanverth6ca48822016-10-07 06:57:32 -07001268 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001269 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001270 }
1271 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001272 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001273 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001274 if (fRoundCaps) {
1275 vertices.write(circle.fRoundCapCenters);
1276 }
jvanverth6ca48822016-10-07 06:57:32 -07001277 }
1278
Brian Salomon05441c42017-05-15 16:45:49 -04001279 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1280 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001281 for (int i = 0; i < primIndexCount; ++i) {
1282 *indices++ = primIndices[i] + currStartVertex;
1283 }
1284
Brian Salomon05441c42017-05-15 16:45:49 -04001285 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001286 }
jvanverth6ca48822016-10-07 06:57:32 -07001287
Brian Salomon7eae3e02018-08-07 14:02:38 +00001288 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -05001289 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +00001290 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -05001291 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001292 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00001293 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001294 }
1295
Brian Salomon7eae3e02018-08-07 14:02:38 +00001296 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001297 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001298
1299 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001300 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001301 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001302 }
1303
Brian Salomon05441c42017-05-15 16:45:49 -04001304 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001305 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001306 }
1307
Brian Salomon05441c42017-05-15 16:45:49 -04001308 if (fHelper.usesLocalCoords() &&
1309 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001310 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001311 }
1312
Brian Salomon289e3d82016-12-14 15:52:56 -05001313 // Because we've set up the ops that don't use the planes with noop values
1314 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001315 fClipPlane |= that->fClipPlane;
1316 fClipPlaneIsect |= that->fClipPlaneIsect;
1317 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001318 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001319 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001320
Brian Salomon05441c42017-05-15 16:45:49 -04001321 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001322 fVertCount += that->fVertCount;
1323 fIndexCount += that->fIndexCount;
1324 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001325 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001326 }
1327
Brian Salomon05441c42017-05-15 16:45:49 -04001328 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001329 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001330 SkScalar fInnerRadius;
1331 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001332 SkScalar fClipPlane[3];
1333 SkScalar fIsectPlane[3];
1334 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001335 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001336 SkRect fDevBounds;
1337 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001338 };
1339
Brian Salomon289e3d82016-12-14 15:52:56 -05001340 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001341 Helper fHelper;
1342 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001343 int fVertCount;
1344 int fIndexCount;
1345 bool fAllFill;
1346 bool fClipPlane;
1347 bool fClipPlaneIsect;
1348 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001349 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001350 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001351
Brian Salomon05441c42017-05-15 16:45:49 -04001352 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001353};
1354
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001355class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1356private:
1357 using Helper = GrSimpleMeshDrawOpHelper;
1358
1359public:
1360 DEFINE_OP_CLASS_ID
1361
Robert Phillips7c525e62018-06-12 10:11:12 -04001362 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1363 GrPaint&& paint,
1364 const SkMatrix& viewMatrix,
1365 SkPoint center,
1366 SkScalar radius,
1367 SkScalar strokeWidth,
1368 SkScalar startAngle,
1369 SkScalar onAngle,
1370 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001371 SkScalar phaseAngle) {
1372 SkASSERT(circle_stays_circle(viewMatrix));
1373 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001374 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1375 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001376 onAngle, offAngle, phaseAngle);
1377 }
1378
Brian Osmancf860852018-10-31 14:04:39 -04001379 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001380 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1381 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1382 SkScalar offAngle, SkScalar phaseAngle)
1383 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1384 SkASSERT(circle_stays_circle(viewMatrix));
1385 viewMatrix.mapPoints(&center, 1);
1386 radius = viewMatrix.mapRadius(radius);
1387 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1388
1389 // Determine the angle where the circle starts in device space and whether its orientation
1390 // has been reversed.
1391 SkVector start;
1392 bool reflection;
1393 if (!startAngle) {
1394 start = {1, 0};
1395 } else {
1396 start.fY = SkScalarSinCos(startAngle, &start.fX);
1397 }
1398 viewMatrix.mapVectors(&start, 1);
1399 startAngle = SkScalarATan2(start.fY, start.fX);
1400 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1401 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1402
1403 auto totalAngle = onAngle + offAngle;
1404 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1405
1406 SkScalar halfWidth = 0;
1407 if (SkScalarNearlyZero(strokeWidth)) {
1408 halfWidth = SK_ScalarHalf;
1409 } else {
1410 halfWidth = SkScalarHalf(strokeWidth);
1411 }
1412
1413 SkScalar outerRadius = radius + halfWidth;
1414 SkScalar innerRadius = radius - halfWidth;
1415
1416 // The radii are outset for two reasons. First, it allows the shader to simply perform
1417 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1418 // Second, the outer radius is used to compute the verts of the bounding box that is
1419 // rendered and the outset ensures the box will cover all partially covered by the circle.
1420 outerRadius += SK_ScalarHalf;
1421 innerRadius -= SK_ScalarHalf;
1422 fViewMatrixIfUsingLocalCoords = viewMatrix;
1423
1424 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1425 center.fX + outerRadius, center.fY + outerRadius);
1426
1427 // We store whether there is a reflection as a negative total angle.
1428 if (reflection) {
1429 totalAngle = -totalAngle;
1430 }
1431 fCircles.push_back(Circle{
1432 color,
1433 outerRadius,
1434 innerRadius,
1435 onAngle,
1436 totalAngle,
1437 startAngle,
1438 phaseAngle,
1439 devBounds
1440 });
1441 // Use the original radius and stroke radius for the bounds so that it does not include the
1442 // AA bloat.
1443 radius += halfWidth;
1444 this->setBounds(
1445 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1446 HasAABloat::kYes, IsZeroArea::kNo);
1447 fVertCount = circle_type_to_vert_count(true);
1448 fIndexCount = circle_type_to_index_count(true);
Brian Osmane3caf2d2018-11-21 13:48:36 -05001449 fWideColor = !SkPMColor4fFitsInBytes(color);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001450 }
1451
1452 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1453
Brian Salomon7d94bb52018-10-12 14:37:19 -04001454 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
1455 fHelper.visitProxies(func);
1456 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001457
Brian Osman9a390ac2018-11-12 09:47:48 -05001458#ifdef SK_DEBUG
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001459 SkString dumpInfo() const override {
1460 SkString string;
1461 for (int i = 0; i < fCircles.count(); ++i) {
1462 string.appendf(
1463 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1464 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1465 "Phase: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001466 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001467 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1468 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1469 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1470 fCircles[i].fPhaseAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001471 }
1472 string += fHelper.dumpInfo();
1473 string += INHERITED::dumpInfo();
1474 return string;
1475 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001476#endif
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001477
Chris Dalton4b62aed2019-01-15 11:53:00 -07001478 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001479 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001480 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1481 color);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001482 }
1483
1484 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1485
1486private:
1487 void onPrepareDraws(Target* target) override {
1488 SkMatrix localMatrix;
1489 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1490 return;
1491 }
1492
1493 // Setup geometry processor
Brian Osmane3caf2d2018-11-21 13:48:36 -05001494 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(fWideColor,
1495 localMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001496
Brian Salomon12d22642019-01-29 14:38:50 -05001497 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001498 int firstVertex;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001499 GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
1500 &vertexBuffer, &firstVertex)};
1501 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001502 SkDebugf("Could not allocate vertices\n");
1503 return;
1504 }
1505
Brian Salomon12d22642019-01-29 14:38:50 -05001506 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001507 int firstIndex = 0;
1508 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1509 if (!indices) {
1510 SkDebugf("Could not allocate indices\n");
1511 return;
1512 }
1513
1514 int currStartVertex = 0;
1515 for (const auto& circle : fCircles) {
1516 // The inner radius in the vertex data must be specified in normalized space so that
1517 // length() can be called with smaller values to avoid precision issues with half
1518 // floats.
1519 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1520 const SkRect& bounds = circle.fDevBounds;
1521 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001522 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1523 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1524 };
1525 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001526 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001527 dashParams.totalAngle = -dashParams.totalAngle;
1528 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001529 }
1530
Brian Osmane3caf2d2018-11-21 13:48:36 -05001531 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001532
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001533 // The bounding geometry for the circle is composed of an outer bounding octagon and
1534 // an inner bounded octagon.
1535
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001536 // Compute the vertices of the outer octagon.
1537 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1538 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001539
1540 auto reflectY = [=](const SkPoint& p) {
1541 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001542 };
Brian Osman9d958b52018-11-13 12:46:56 -05001543
1544 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001545 vertices.write(center + kOctagonOuter[i] * halfWidth,
1546 color,
1547 reflectY(kOctagonOuter[i]),
1548 circle.fOuterRadius,
1549 normInnerRadius,
1550 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001551 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001552
1553 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001554 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001555 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1556 color,
1557 reflectY(kOctagonInner[i]) * normInnerRadius,
1558 circle.fOuterRadius,
1559 normInnerRadius,
1560 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001561 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001562
1563 const uint16_t* primIndices = circle_type_to_indices(true);
1564 const int primIndexCount = circle_type_to_index_count(true);
1565 for (int i = 0; i < primIndexCount; ++i) {
1566 *indices++ = primIndices[i] + currStartVertex;
1567 }
1568
1569 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001570 }
1571
Brian Salomon7eae3e02018-08-07 14:02:38 +00001572 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -05001573 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +00001574 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -05001575 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001576 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00001577 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001578 }
1579
Brian Salomon7eae3e02018-08-07 14:02:38 +00001580 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001581 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1582
1583 // can only represent 65535 unique vertices with 16-bit indices
1584 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001585 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001586 }
1587
1588 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001589 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001590 }
1591
1592 if (fHelper.usesLocalCoords() &&
1593 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001594 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001595 }
1596
1597 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001598 fVertCount += that->fVertCount;
1599 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001600 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001601 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001602 }
1603
1604 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001605 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001606 SkScalar fOuterRadius;
1607 SkScalar fInnerRadius;
1608 SkScalar fOnAngle;
1609 SkScalar fTotalAngle;
1610 SkScalar fStartAngle;
1611 SkScalar fPhaseAngle;
1612 SkRect fDevBounds;
1613 };
1614
1615 SkMatrix fViewMatrixIfUsingLocalCoords;
1616 Helper fHelper;
1617 SkSTArray<1, Circle, true> fCircles;
1618 int fVertCount;
1619 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001620 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001621
1622 typedef GrMeshDrawOp INHERITED;
1623};
1624
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001625///////////////////////////////////////////////////////////////////////////////
1626
Brian Salomon05441c42017-05-15 16:45:49 -04001627class EllipseOp : public GrMeshDrawOp {
1628private:
1629 using Helper = GrSimpleMeshDrawOpHelper;
1630
1631 struct DeviceSpaceParams {
1632 SkPoint fCenter;
1633 SkScalar fXRadius;
1634 SkScalar fYRadius;
1635 SkScalar fInnerXRadius;
1636 SkScalar fInnerYRadius;
1637 };
1638
joshualitt76e7fb62015-02-11 08:52:27 -08001639public:
Brian Salomon25a88092016-12-01 09:36:50 -05001640 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001641
Robert Phillips7c525e62018-06-12 10:11:12 -04001642 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1643 GrPaint&& paint,
1644 const SkMatrix& viewMatrix,
1645 const SkRect& ellipse,
1646 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001647 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001648 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001649 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1650 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001651 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1652 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001653 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1654 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1655 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1656 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001657
bsalomon4b4a7cc2016-07-08 04:42:54 -07001658 // do (potentially) anisotropic mapping of stroke
1659 SkVector scaledStroke;
1660 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001661 scaledStroke.fX = SkScalarAbs(
1662 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1663 scaledStroke.fY = SkScalarAbs(
1664 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001665
1666 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001667 bool isStrokeOnly =
1668 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001669 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1670
Brian Salomon05441c42017-05-15 16:45:49 -04001671 params.fInnerXRadius = 0;
1672 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001673 if (hasStroke) {
1674 if (SkScalarNearlyZero(scaledStroke.length())) {
1675 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1676 } else {
1677 scaledStroke.scale(SK_ScalarHalf);
1678 }
1679
1680 // we only handle thick strokes for near-circular ellipses
1681 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001682 (0.5f * params.fXRadius > params.fYRadius ||
1683 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001684 return nullptr;
1685 }
1686
1687 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001688 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1689 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1690 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1691 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001692 return nullptr;
1693 }
1694
1695 // this is legit only if scale & translation (which should be the case at the moment)
1696 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001697 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1698 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001699 }
1700
Brian Salomon05441c42017-05-15 16:45:49 -04001701 params.fXRadius += scaledStroke.fX;
1702 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001703 }
Robert Phillips7c525e62018-06-12 10:11:12 -04001704 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
1705 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001706 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001707
Brian Osmancf860852018-10-31 14:04:39 -04001708 EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04001709 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
1710 const SkStrokeRec& stroke)
Brian Salomonea26d6b2018-01-23 20:33:21 +00001711 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001712 SkStrokeRec::Style style = stroke.getStyle();
1713 bool isStrokeOnly =
1714 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001715
Brian Salomon05441c42017-05-15 16:45:49 -04001716 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1717 params.fInnerXRadius, params.fInnerYRadius,
1718 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1719 params.fCenter.fY - params.fYRadius,
1720 params.fCenter.fX + params.fXRadius,
1721 params.fCenter.fY + params.fYRadius)});
1722
1723 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001724
bsalomon4b4a7cc2016-07-08 04:42:54 -07001725 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001726 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001727
Brian Salomon05441c42017-05-15 16:45:49 -04001728 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1729 fViewMatrixIfUsingLocalCoords = viewMatrix;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001730 fWideColor = !SkPMColor4fFitsInBytes(color);
bsalomoncdaa97b2016-03-08 08:30:14 -08001731 }
joshualitt76e7fb62015-02-11 08:52:27 -08001732
Brian Salomon289e3d82016-12-14 15:52:56 -05001733 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001734
Brian Salomon7d94bb52018-10-12 14:37:19 -04001735 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001736 fHelper.visitProxies(func);
1737 }
1738
Brian Osman9a390ac2018-11-12 09:47:48 -05001739#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001740 SkString dumpInfo() const override {
1741 SkString string;
1742 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001743 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001744 string.appendf(
1745 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1746 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001747 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001748 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
1749 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001750 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001751 string += fHelper.dumpInfo();
1752 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001753 return string;
1754 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001755#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001756
Chris Dalton4b62aed2019-01-15 11:53:00 -07001757 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001758 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001759 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1760 color);
Brian Salomon05441c42017-05-15 16:45:49 -04001761 }
1762
1763 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1764
bsalomone46f9fe2015-08-18 06:05:14 -07001765private:
Brian Salomon91326c32017-08-09 16:02:19 -04001766 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001767 SkMatrix localMatrix;
1768 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001769 return;
1770 }
1771
1772 // Setup geometry processor
Brian Osmane3caf2d2018-11-21 13:48:36 -05001773 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor,
1774 localMatrix));
Brian Osman9d958b52018-11-13 12:46:56 -05001775 QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001776 GrVertexWriter verts{helper.vertices()};
1777 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08001778 return;
1779 }
1780
Brian Salomon05441c42017-05-15 16:45:49 -04001781 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001782 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001783 SkScalar xRadius = ellipse.fXRadius;
1784 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001785
1786 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001787 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1788 SkScalarInvert(xRadius),
1789 SkScalarInvert(yRadius),
1790 SkScalarInvert(ellipse.fInnerXRadius),
1791 SkScalarInvert(ellipse.fInnerYRadius)
1792 };
vjiaoblack977996d2016-06-30 12:20:54 -07001793 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1794 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1795
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001796 if (!fStroked) {
1797 // For filled ellipses we map a unit circle in the vertex attributes rather than
1798 // computing an ellipse and modifying that distance, so we normalize to 1
1799 xMaxOffset /= xRadius;
1800 yMaxOffset /= yRadius;
1801 }
1802
joshualitt76e7fb62015-02-11 08:52:27 -08001803 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001804 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1805 color,
1806 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
1807 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08001808 }
Brian Salomon49348902018-06-26 09:12:38 -04001809 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00001810 helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08001811 }
1812
Brian Salomon7eae3e02018-08-07 14:02:38 +00001813 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001814 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001815
Brian Salomon05441c42017-05-15 16:45:49 -04001816 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001817 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001818 }
1819
bsalomoncdaa97b2016-03-08 08:30:14 -08001820 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001821 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001822 }
1823
Brian Salomon05441c42017-05-15 16:45:49 -04001824 if (fHelper.usesLocalCoords() &&
1825 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001826 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001827 }
1828
Brian Salomon05441c42017-05-15 16:45:49 -04001829 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05001830 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001831 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001832 }
1833
Brian Salomon05441c42017-05-15 16:45:49 -04001834 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04001835 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001836 SkScalar fXRadius;
1837 SkScalar fYRadius;
1838 SkScalar fInnerXRadius;
1839 SkScalar fInnerYRadius;
1840 SkRect fDevBounds;
1841 };
joshualitt76e7fb62015-02-11 08:52:27 -08001842
Brian Salomon289e3d82016-12-14 15:52:56 -05001843 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001844 Helper fHelper;
1845 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001846 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04001847 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001848
Brian Salomon05441c42017-05-15 16:45:49 -04001849 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001850};
1851
joshualitt76e7fb62015-02-11 08:52:27 -08001852/////////////////////////////////////////////////////////////////////////////////////////////////
1853
Brian Salomon05441c42017-05-15 16:45:49 -04001854class DIEllipseOp : public GrMeshDrawOp {
1855private:
1856 using Helper = GrSimpleMeshDrawOpHelper;
1857
1858 struct DeviceSpaceParams {
1859 SkPoint fCenter;
1860 SkScalar fXRadius;
1861 SkScalar fYRadius;
1862 SkScalar fInnerXRadius;
1863 SkScalar fInnerYRadius;
1864 DIEllipseStyle fStyle;
1865 };
1866
joshualitt76e7fb62015-02-11 08:52:27 -08001867public:
Brian Salomon25a88092016-12-01 09:36:50 -05001868 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001869
Robert Phillips7c525e62018-06-12 10:11:12 -04001870 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1871 GrPaint&& paint,
1872 const SkMatrix& viewMatrix,
1873 const SkRect& ellipse,
1874 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001875 DeviceSpaceParams params;
1876 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1877 params.fXRadius = SkScalarHalf(ellipse.width());
1878 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001879
bsalomon4b4a7cc2016-07-08 04:42:54 -07001880 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04001881 params.fStyle = (SkStrokeRec::kStroke_Style == style)
1882 ? DIEllipseStyle::kStroke
1883 : (SkStrokeRec::kHairline_Style == style)
1884 ? DIEllipseStyle::kHairline
1885 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001886
Brian Salomon05441c42017-05-15 16:45:49 -04001887 params.fInnerXRadius = 0;
1888 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001889 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1890 SkScalar strokeWidth = stroke.getWidth();
1891
1892 if (SkScalarNearlyZero(strokeWidth)) {
1893 strokeWidth = SK_ScalarHalf;
1894 } else {
1895 strokeWidth *= SK_ScalarHalf;
1896 }
1897
1898 // we only handle thick strokes for near-circular ellipses
1899 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001900 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1901 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001902 return nullptr;
1903 }
1904
1905 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001906 if (strokeWidth * (params.fYRadius * params.fYRadius) <
1907 (strokeWidth * strokeWidth) * params.fXRadius) {
1908 return nullptr;
1909 }
1910 if (strokeWidth * (params.fXRadius * params.fXRadius) <
1911 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001912 return nullptr;
1913 }
1914
1915 // set inner radius (if needed)
1916 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04001917 params.fInnerXRadius = params.fXRadius - strokeWidth;
1918 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001919 }
1920
Brian Salomon05441c42017-05-15 16:45:49 -04001921 params.fXRadius += strokeWidth;
1922 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001923 }
Brian Salomon05441c42017-05-15 16:45:49 -04001924 if (DIEllipseStyle::kStroke == params.fStyle &&
1925 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1926 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001927 }
Robert Phillips7c525e62018-06-12 10:11:12 -04001928 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04001929 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001930
Brian Osmancf860852018-10-31 14:04:39 -04001931 DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04001932 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00001933 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001934 // This expands the outer rect so that after CTM we end up with a half-pixel border
1935 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1936 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1937 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1938 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001939 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1940 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001941
Brian Salomon05441c42017-05-15 16:45:49 -04001942 fEllipses.emplace_back(
1943 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1944 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1945 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1946 params.fCenter.fY - params.fYRadius - geoDy,
1947 params.fCenter.fX + params.fXRadius + geoDx,
1948 params.fCenter.fY + params.fYRadius + geoDy)});
1949 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1950 IsZeroArea::kNo);
Brian Osmane3caf2d2018-11-21 13:48:36 -05001951 fWideColor = !SkPMColor4fFitsInBytes(color);
joshualitt76e7fb62015-02-11 08:52:27 -08001952 }
1953
Brian Salomon289e3d82016-12-14 15:52:56 -05001954 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001955
Brian Salomon7d94bb52018-10-12 14:37:19 -04001956 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001957 fHelper.visitProxies(func);
1958 }
1959
Brian Osman9a390ac2018-11-12 09:47:48 -05001960#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001961 SkString dumpInfo() const override {
1962 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001963 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001964 string.appendf(
1965 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1966 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1967 "GeoDY: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001968 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001969 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
1970 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001971 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001972 string += fHelper.dumpInfo();
1973 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001974 return string;
1975 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001976#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001977
Chris Dalton4b62aed2019-01-15 11:53:00 -07001978 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04001979 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07001980 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1981 color);
Brian Salomon05441c42017-05-15 16:45:49 -04001982 }
1983
1984 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1985
bsalomone46f9fe2015-08-18 06:05:14 -07001986private:
Brian Salomon91326c32017-08-09 16:02:19 -04001987 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001988 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001989 sk_sp<GrGeometryProcessor> gp(
Brian Osmane3caf2d2018-11-21 13:48:36 -05001990 new DIEllipseGeometryProcessor(fWideColor, this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001991
Brian Osman9d958b52018-11-13 12:46:56 -05001992 QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001993 GrVertexWriter verts{helper.vertices()};
1994 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08001995 return;
1996 }
1997
Brian Salomon05441c42017-05-15 16:45:49 -04001998 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001999 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002000 SkScalar xRadius = ellipse.fXRadius;
2001 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002002
joshualitt76e7fb62015-02-11 08:52:27 -08002003 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002004 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2005 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002006
Brian Osman9d958b52018-11-13 12:46:56 -05002007 // By default, constructed so that inner offset is (0, 0) for all points
2008 SkScalar innerRatioX = -offsetDx;
2009 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002010
Brian Osman9d958b52018-11-13 12:46:56 -05002011 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002012 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002013 innerRatioX = xRadius / ellipse.fInnerXRadius;
2014 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002015 }
joshualitt76e7fb62015-02-11 08:52:27 -08002016
Brian Osman2b6e3902018-11-21 15:29:43 -05002017 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2018 color,
2019 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
2020 origin_centered_tri_strip(innerRatioX + offsetDx,
2021 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002022 }
Brian Salomon49348902018-06-26 09:12:38 -04002023 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00002024 helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002025 }
halcanary9d524f22016-03-29 09:03:52 -07002026
Brian Salomon7eae3e02018-08-07 14:02:38 +00002027 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002028 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002029 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002030 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002031 }
2032
bsalomoncdaa97b2016-03-08 08:30:14 -08002033 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002034 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002035 }
2036
joshualittd96a67b2015-05-05 14:09:05 -07002037 // TODO rewrite to allow positioning on CPU
2038 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002039 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002040 }
2041
Brian Salomon05441c42017-05-15 16:45:49 -04002042 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002043 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002044 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002045 }
2046
Brian Salomon05441c42017-05-15 16:45:49 -04002047 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2048 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002049
Brian Salomon05441c42017-05-15 16:45:49 -04002050 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002051 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002052 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002053 SkScalar fXRadius;
2054 SkScalar fYRadius;
2055 SkScalar fInnerXRadius;
2056 SkScalar fInnerYRadius;
2057 SkScalar fGeoDx;
2058 SkScalar fGeoDy;
2059 DIEllipseStyle fStyle;
2060 SkRect fBounds;
2061 };
2062
Brian Salomon05441c42017-05-15 16:45:49 -04002063 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002064 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002065 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002066
Brian Salomon05441c42017-05-15 16:45:49 -04002067 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002068};
2069
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002070///////////////////////////////////////////////////////////////////////////////
2071
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002072// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002073//
2074// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2075// ____________
2076// |_|________|_|
2077// | | | |
2078// | | | |
2079// | | | |
2080// |_|________|_|
2081// |_|________|_|
2082//
2083// For strokes, we don't draw the center quad.
2084//
2085// For circular roundrects, in the case where the stroke width is greater than twice
2086// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002087// in the center. The shared vertices are duplicated so we can set a different outer radius
2088// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002089// ____________
2090// |_|________|_|
2091// | |\ ____ /| |
2092// | | | | | |
2093// | | |____| | |
2094// |_|/______\|_|
2095// |_|________|_|
2096//
2097// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002098//
2099// For filled rrects that need to provide a distance vector we resuse the overstroke
2100// geometry but make the inner rect degenerate (either a point or a horizontal or
2101// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002102
jvanverth84839f62016-08-29 10:16:40 -07002103static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002104 // clang-format off
2105 // overstroke quads
2106 // we place this at the beginning so that we can skip these indices when rendering normally
2107 16, 17, 19, 16, 19, 18,
2108 19, 17, 23, 19, 23, 21,
2109 21, 23, 22, 21, 22, 20,
2110 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002111
Brian Salomon289e3d82016-12-14 15:52:56 -05002112 // corners
2113 0, 1, 5, 0, 5, 4,
2114 2, 3, 7, 2, 7, 6,
2115 8, 9, 13, 8, 13, 12,
2116 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002117
Brian Salomon289e3d82016-12-14 15:52:56 -05002118 // edges
2119 1, 2, 6, 1, 6, 5,
2120 4, 5, 9, 4, 9, 8,
2121 6, 7, 11, 6, 11, 10,
2122 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002123
Brian Salomon289e3d82016-12-14 15:52:56 -05002124 // center
2125 // we place this at the end so that we can ignore these indices when not rendering as filled
2126 5, 6, 10, 5, 10, 9,
2127 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002128};
Brian Salomon289e3d82016-12-14 15:52:56 -05002129
jvanverth84839f62016-08-29 10:16:40 -07002130// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002131static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002132
jvanverth84839f62016-08-29 10:16:40 -07002133// overstroke count is arraysize minus the center indices
2134static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2135// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002136static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002137// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002138static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2139static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002140static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002141
jvanverthc3d0e422016-08-25 08:12:35 -07002142enum RRectType {
2143 kFill_RRectType,
2144 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002145 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002146};
2147
jvanverth84839f62016-08-29 10:16:40 -07002148static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002149 switch (type) {
2150 case kFill_RRectType:
2151 case kStroke_RRectType:
2152 return kVertsPerStandardRRect;
2153 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002154 return kVertsPerOverstrokeRRect;
2155 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002156 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002157 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002158}
2159
2160static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002161 switch (type) {
2162 case kFill_RRectType:
2163 return kIndicesPerFillRRect;
2164 case kStroke_RRectType:
2165 return kIndicesPerStrokeRRect;
2166 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002167 return kIndicesPerOverstrokeRRect;
2168 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002169 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002170 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002171}
2172
2173static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002174 switch (type) {
2175 case kFill_RRectType:
2176 case kStroke_RRectType:
2177 return gStandardRRectIndices;
2178 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002179 return gOverstrokeRRectIndices;
2180 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002181 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002182 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002183}
2184
joshualitt76e7fb62015-02-11 08:52:27 -08002185///////////////////////////////////////////////////////////////////////////////////////////////////
2186
Robert Phillips79839d42016-10-06 15:03:34 -04002187// For distance computations in the interior of filled rrects we:
2188//
2189// add a interior degenerate (point or line) rect
2190// each vertex of that rect gets -outerRad as its radius
2191// this makes the computation of the distance to the outer edge be negative
2192// negative values are caught and then handled differently in the GP's onEmitCode
2193// each vertex is also given the normalized x & y distance from the interior rect's edge
2194// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2195
Brian Salomon05441c42017-05-15 16:45:49 -04002196class CircularRRectOp : public GrMeshDrawOp {
2197private:
2198 using Helper = GrSimpleMeshDrawOpHelper;
2199
joshualitt76e7fb62015-02-11 08:52:27 -08002200public:
Brian Salomon25a88092016-12-01 09:36:50 -05002201 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002202
bsalomon4b4a7cc2016-07-08 04:42:54 -07002203 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2204 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002205 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2206 GrPaint&& paint,
2207 const SkMatrix& viewMatrix,
2208 const SkRect& devRect,
2209 float devRadius,
2210 float devStrokeWidth,
2211 bool strokeOnly) {
2212 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
2213 devRect, devRadius,
2214 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002215 }
Brian Osmancf860852018-10-31 14:04:39 -04002216 CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002217 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2218 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002219 : INHERITED(ClassID())
2220 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002221 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002222 SkRect bounds = devRect;
2223 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2224 SkScalar innerRadius = 0.0f;
2225 SkScalar outerRadius = devRadius;
2226 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002227 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002228 if (devStrokeWidth > 0) {
2229 if (SkScalarNearlyZero(devStrokeWidth)) {
2230 halfWidth = SK_ScalarHalf;
2231 } else {
2232 halfWidth = SkScalarHalf(devStrokeWidth);
2233 }
joshualitt76e7fb62015-02-11 08:52:27 -08002234
bsalomon4b4a7cc2016-07-08 04:42:54 -07002235 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002236 // Outset stroke by 1/4 pixel
2237 devStrokeWidth += 0.25f;
2238 // If stroke is greater than width or height, this is still a fill
2239 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002240 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002241 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002242 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002243 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002244 }
2245 outerRadius += halfWidth;
2246 bounds.outset(halfWidth, halfWidth);
2247 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002248
bsalomon4b4a7cc2016-07-08 04:42:54 -07002249 // The radii are outset for two reasons. First, it allows the shader to simply perform
2250 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2251 // Second, the outer radius is used to compute the verts of the bounding box that is
2252 // rendered and the outset ensures the box will cover all partially covered by the rrect
2253 // corners.
2254 outerRadius += SK_ScalarHalf;
2255 innerRadius -= SK_ScalarHalf;
2256
bsalomon88cf17d2016-07-08 06:40:56 -07002257 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2258
2259 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07002260 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2261
Brian Salomon05441c42017-05-15 16:45:49 -04002262 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002263 fVertCount = rrect_type_to_vert_count(type);
2264 fIndexCount = rrect_type_to_index_count(type);
2265 fAllFill = (kFill_RRectType == type);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002266 fWideColor = !SkPMColor4fFitsInBytes(color);
joshualitt76e7fb62015-02-11 08:52:27 -08002267 }
2268
Brian Salomon289e3d82016-12-14 15:52:56 -05002269 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002270
Brian Salomon7d94bb52018-10-12 14:37:19 -04002271 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002272 fHelper.visitProxies(func);
2273 }
2274
Brian Osman9a390ac2018-11-12 09:47:48 -05002275#ifdef SK_DEBUG
jvanverthc3d0e422016-08-25 08:12:35 -07002276 SkString dumpInfo() const override {
2277 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002278 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002279 string.appendf(
2280 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2281 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002282 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002283 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2284 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2285 fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002286 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002287 string += fHelper.dumpInfo();
2288 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002289 return string;
2290 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002291#endif
jvanverthc3d0e422016-08-25 08:12:35 -07002292
Chris Dalton4b62aed2019-01-15 11:53:00 -07002293 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04002294 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07002295 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2296 color);
Brian Salomon05441c42017-05-15 16:45:49 -04002297 }
2298
2299 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2300
Brian Salomon92aee3d2016-12-21 09:20:25 -05002301private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002302 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002303 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002304 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002305 SkASSERT(smInset < bigInset);
2306
2307 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002308 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2309 color,
2310 xOffset, 0.0f,
2311 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002312
2313 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002314 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2315 color,
2316 xOffset, 0.0f,
2317 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002318
Brian Osmana1d4eb92018-12-06 16:33:10 -05002319 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2320 color,
2321 0.0f, 0.0f,
2322 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002323
Brian Osmana1d4eb92018-12-06 16:33:10 -05002324 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2325 color,
2326 0.0f, 0.0f,
2327 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002328
Brian Osmana1d4eb92018-12-06 16:33:10 -05002329 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2330 color,
2331 0.0f, 0.0f,
2332 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002333
Brian Osmana1d4eb92018-12-06 16:33:10 -05002334 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2335 color,
2336 0.0f, 0.0f,
2337 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002338
2339 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002340 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2341 color,
2342 xOffset, 0.0f,
2343 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002344
2345 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002346 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2347 color,
2348 xOffset, 0.0f,
2349 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002350 }
2351
Brian Salomon91326c32017-08-09 16:02:19 -04002352 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002353 // Invert the view matrix as a local matrix (if any other processors require coords).
2354 SkMatrix localMatrix;
2355 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002356 return;
2357 }
2358
2359 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002360 sk_sp<GrGeometryProcessor> gp(
Brian Osmana1d4eb92018-12-06 16:33:10 -05002361 new CircleGeometryProcessor(!fAllFill, false, false, false, false, fWideColor,
Brian Osmane3caf2d2018-11-21 13:48:36 -05002362 localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002363
Brian Salomon12d22642019-01-29 14:38:50 -05002364 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002365 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002366
Brian Osmana1d4eb92018-12-06 16:33:10 -05002367 GrVertexWriter verts{target->makeVertexSpace(gp->vertexStride(), fVertCount,
2368 &vertexBuffer, &firstVertex)};
2369 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002370 SkDebugf("Could not allocate vertices\n");
2371 return;
2372 }
2373
Brian Salomon12d22642019-01-29 14:38:50 -05002374 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002375 int firstIndex = 0;
2376 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2377 if (!indices) {
2378 SkDebugf("Could not allocate indices\n");
2379 return;
2380 }
2381
2382 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002383 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002384 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002385 SkScalar outerRadius = rrect.fOuterRadius;
2386 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002387
Brian Salomon289e3d82016-12-14 15:52:56 -05002388 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2389 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002390
Brian Salomon289e3d82016-12-14 15:52:56 -05002391 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002392 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002393 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002394 SkScalar innerRadius = rrect.fType != kFill_RRectType
2395 ? rrect.fInnerRadius / rrect.fOuterRadius
2396 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002397 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002398 verts.write(bounds.fLeft, yCoords[i],
2399 color,
2400 -1.0f, yOuterRadii[i],
2401 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002402
Brian Osmana1d4eb92018-12-06 16:33:10 -05002403 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2404 color,
2405 0.0f, yOuterRadii[i],
2406 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002407
Brian Osmana1d4eb92018-12-06 16:33:10 -05002408 verts.write(bounds.fRight - outerRadius, yCoords[i],
2409 color,
2410 0.0f, yOuterRadii[i],
2411 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002412
Brian Osmana1d4eb92018-12-06 16:33:10 -05002413 verts.write(bounds.fRight, yCoords[i],
2414 color,
2415 1.0f, yOuterRadii[i],
2416 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002417 }
jvanverthc3d0e422016-08-25 08:12:35 -07002418 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002419 // Effectively this is an additional stroked rrect, with its
2420 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2421 // This will give us correct AA in the center and the correct
2422 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002423 //
jvanvertha4f1af82016-08-29 07:17:47 -07002424 // Also, the outer offset is a constant vector pointing to the right, which
2425 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002426 if (kOverstroke_RRectType == rrect.fType) {
2427 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002428
Brian Salomon05441c42017-05-15 16:45:49 -04002429 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002430 // this is the normalized distance from the outer rectangle of this
2431 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002432 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002433
Brian Osmana1d4eb92018-12-06 16:33:10 -05002434 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002435 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002436 }
jvanverth6a397612016-08-26 08:15:33 -07002437
Brian Salomon05441c42017-05-15 16:45:49 -04002438 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2439 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002440 for (int i = 0; i < primIndexCount; ++i) {
2441 *indices++ = primIndices[i] + currStartVertex;
2442 }
2443
Brian Salomon05441c42017-05-15 16:45:49 -04002444 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002445 }
2446
Brian Salomon7eae3e02018-08-07 14:02:38 +00002447 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -05002448 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +00002449 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -05002450 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04002451 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00002452 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002453 }
2454
Brian Salomon7eae3e02018-08-07 14:02:38 +00002455 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002456 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002457
2458 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002459 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002460 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002461 }
2462
Brian Salomon05441c42017-05-15 16:45:49 -04002463 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002464 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002465 }
2466
Brian Salomon05441c42017-05-15 16:45:49 -04002467 if (fHelper.usesLocalCoords() &&
2468 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002469 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002470 }
2471
Brian Salomon05441c42017-05-15 16:45:49 -04002472 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002473 fVertCount += that->fVertCount;
2474 fIndexCount += that->fIndexCount;
2475 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002476 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002477 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002478 }
2479
Brian Salomon05441c42017-05-15 16:45:49 -04002480 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002481 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002482 SkScalar fInnerRadius;
2483 SkScalar fOuterRadius;
2484 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002485 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002486 };
2487
Brian Salomon289e3d82016-12-14 15:52:56 -05002488 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002489 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002490 int fVertCount;
2491 int fIndexCount;
2492 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002493 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002494 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002495
Brian Salomon05441c42017-05-15 16:45:49 -04002496 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002497};
2498
jvanverth84839f62016-08-29 10:16:40 -07002499static const int kNumRRectsInIndexBuffer = 256;
2500
2501GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2502GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002503static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2504 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002505 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2506 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2507 switch (type) {
2508 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002509 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002510 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2511 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002512 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002513 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002514 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2515 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002516 default:
2517 SkASSERT(false);
2518 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002519 }
jvanverth84839f62016-08-29 10:16:40 -07002520}
2521
Brian Salomon05441c42017-05-15 16:45:49 -04002522class EllipticalRRectOp : public GrMeshDrawOp {
2523private:
2524 using Helper = GrSimpleMeshDrawOpHelper;
2525
joshualitt76e7fb62015-02-11 08:52:27 -08002526public:
Brian Salomon25a88092016-12-01 09:36:50 -05002527 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002528
bsalomon4b4a7cc2016-07-08 04:42:54 -07002529 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2530 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002531 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2532 GrPaint&& paint,
2533 const SkMatrix& viewMatrix,
2534 const SkRect& devRect,
2535 float devXRadius,
2536 float devYRadius,
2537 SkVector devStrokeWidths,
2538 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002539 SkASSERT(devXRadius > 0.5);
2540 SkASSERT(devYRadius > 0.5);
2541 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2542 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002543 if (devStrokeWidths.fX > 0) {
2544 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2545 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2546 } else {
2547 devStrokeWidths.scale(SK_ScalarHalf);
2548 }
joshualitt76e7fb62015-02-11 08:52:27 -08002549
bsalomon4b4a7cc2016-07-08 04:42:54 -07002550 // we only handle thick strokes for near-circular ellipses
2551 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002552 (SK_ScalarHalf * devXRadius > devYRadius ||
2553 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002554 return nullptr;
2555 }
2556
2557 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002558 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2559 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002560 return nullptr;
2561 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002562 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2563 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002564 return nullptr;
2565 }
Brian Salomon05441c42017-05-15 16:45:49 -04002566 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002567 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
2568 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002569 devXRadius, devYRadius, devStrokeWidths,
2570 strokeOnly);
2571 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002572
Brian Osmancf860852018-10-31 14:04:39 -04002573 EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002574 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2575 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002576 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04002577 SkScalar innerXRadius = 0.0f;
2578 SkScalar innerYRadius = 0.0f;
2579 SkRect bounds = devRect;
2580 bool stroked = false;
2581 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002582 // this is legit only if scale & translation (which should be the case at the moment)
2583 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002584 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2585 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002586 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2587 }
2588
Brian Salomon05441c42017-05-15 16:45:49 -04002589 devXRadius += devStrokeHalfWidths.fX;
2590 devYRadius += devStrokeHalfWidths.fY;
2591 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002592 }
2593
Brian Salomon05441c42017-05-15 16:45:49 -04002594 fStroked = stroked;
2595 fViewMatrixIfUsingLocalCoords = viewMatrix;
2596 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002597 // Expand the rect for aa in order to generate the correct vertices.
2598 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002599 fWideColor = !SkPMColor4fFitsInBytes(color);
Brian Salomon05441c42017-05-15 16:45:49 -04002600 fRRects.emplace_back(
2601 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002602 }
2603
Brian Salomon289e3d82016-12-14 15:52:56 -05002604 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002605
Brian Salomon7d94bb52018-10-12 14:37:19 -04002606 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002607 fHelper.visitProxies(func);
2608 }
2609
Brian Osman9a390ac2018-11-12 09:47:48 -05002610#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002611 SkString dumpInfo() const override {
2612 SkString string;
2613 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002614 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002615 string.appendf(
2616 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2617 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002618 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002619 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2620 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002621 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002622 string += fHelper.dumpInfo();
2623 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002624 return string;
2625 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002626#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002627
Chris Dalton4b62aed2019-01-15 11:53:00 -07002628 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
Brian Osmancf860852018-10-31 14:04:39 -04002629 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton4b62aed2019-01-15 11:53:00 -07002630 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2631 color);
Brian Salomon05441c42017-05-15 16:45:49 -04002632 }
2633
2634 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2635
bsalomone46f9fe2015-08-18 06:05:14 -07002636private:
Brian Salomon91326c32017-08-09 16:02:19 -04002637 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002638 SkMatrix localMatrix;
2639 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002640 return;
2641 }
2642
2643 // Setup geometry processor
Brian Osmana1d4eb92018-12-06 16:33:10 -05002644 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor,
2645 localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002646
bsalomonb5238a72015-05-05 07:49:49 -07002647 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002648 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002649 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2650 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002651
Brian Salomon12d22642019-01-29 14:38:50 -05002652 if (!indexBuffer) {
2653 SkDebugf("Could not allocate indices\n");
2654 return;
2655 }
Brian Osmana1d4eb92018-12-06 16:33:10 -05002656 PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002657 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Brian Salomon7eae3e02018-08-07 14:02:38 +00002658 fRRects.count());
Brian Osmana1d4eb92018-12-06 16:33:10 -05002659 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002660 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002661 SkDebugf("Could not allocate vertices\n");
2662 return;
2663 }
2664
Brian Salomon05441c42017-05-15 16:45:49 -04002665 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002666 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002667 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002668 float reciprocalRadii[4] = {
2669 SkScalarInvert(rrect.fXRadius),
2670 SkScalarInvert(rrect.fYRadius),
2671 SkScalarInvert(rrect.fInnerXRadius),
2672 SkScalarInvert(rrect.fInnerYRadius)
2673 };
joshualitt76e7fb62015-02-11 08:52:27 -08002674
2675 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002676 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2677 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002678
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002679 SkScalar xMaxOffset = xOuterRadius;
2680 SkScalar yMaxOffset = yOuterRadius;
2681 if (!fStroked) {
2682 // For filled rrects we map a unit circle in the vertex attributes rather than
2683 // computing an ellipse and modifying that distance, so we normalize to 1.
2684 xMaxOffset /= rrect.fXRadius;
2685 yMaxOffset /= rrect.fYRadius;
2686 }
2687
Brian Salomon05441c42017-05-15 16:45:49 -04002688 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002689
Brian Salomon289e3d82016-12-14 15:52:56 -05002690 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2691 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002692 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002693 SK_ScalarNearlyZero, // we're using inversesqrt() in
2694 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002695 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08002696
2697 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002698 verts.write(bounds.fLeft, yCoords[i],
2699 color,
2700 xMaxOffset, yOuterOffsets[i],
2701 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002702
Brian Osmana1d4eb92018-12-06 16:33:10 -05002703 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
2704 color,
2705 SK_ScalarNearlyZero, yOuterOffsets[i],
2706 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002707
Brian Osmana1d4eb92018-12-06 16:33:10 -05002708 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
2709 color,
2710 SK_ScalarNearlyZero, yOuterOffsets[i],
2711 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002712
Brian Osmana1d4eb92018-12-06 16:33:10 -05002713 verts.write(bounds.fRight, yCoords[i],
2714 color,
2715 xMaxOffset, yOuterOffsets[i],
2716 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002717 }
2718 }
Brian Salomon49348902018-06-26 09:12:38 -04002719 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +00002720 helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002721 }
2722
Brian Salomon7eae3e02018-08-07 14:02:38 +00002723 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002724 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002725
Brian Salomon05441c42017-05-15 16:45:49 -04002726 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002727 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002728 }
2729
bsalomoncdaa97b2016-03-08 08:30:14 -08002730 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002731 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002732 }
2733
Brian Salomon05441c42017-05-15 16:45:49 -04002734 if (fHelper.usesLocalCoords() &&
2735 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002736 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002737 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002738
Brian Salomon05441c42017-05-15 16:45:49 -04002739 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05002740 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002741 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002742 }
2743
Brian Salomon05441c42017-05-15 16:45:49 -04002744 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002745 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002746 SkScalar fXRadius;
2747 SkScalar fYRadius;
2748 SkScalar fInnerXRadius;
2749 SkScalar fInnerYRadius;
2750 SkRect fDevBounds;
2751 };
2752
Brian Salomon289e3d82016-12-14 15:52:56 -05002753 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002754 Helper fHelper;
2755 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002756 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002757 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002758
Brian Salomon05441c42017-05-15 16:45:49 -04002759 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002760};
2761
Robert Phillips7c525e62018-06-12 10:11:12 -04002762static std::unique_ptr<GrDrawOp> make_rrect_op(GrContext* context,
2763 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002764 const SkMatrix& viewMatrix,
2765 const SkRRect& rrect,
2766 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002767 SkASSERT(viewMatrix.rectStaysRect());
2768 SkASSERT(rrect.isSimple());
2769 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002770
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002771 // RRect ops only handle simple, but not too simple, rrects.
2772 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002773 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002774 SkRect bounds;
2775 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002776
Mike Reed242135a2018-02-22 13:41:39 -05002777 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05002778 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2779 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2780 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2781 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002782
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002783 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002784
bsalomon4b4a7cc2016-07-08 04:42:54 -07002785 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2786 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002787 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002788
Brian Salomon289e3d82016-12-14 15:52:56 -05002789 bool isStrokeOnly =
2790 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002791 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2792
jvanverthc3d0e422016-08-25 08:12:35 -07002793 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002794 if (hasStroke) {
2795 if (SkStrokeRec::kHairline_Style == style) {
2796 scaledStroke.set(1, 1);
2797 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002798 scaledStroke.fX = SkScalarAbs(
2799 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2800 scaledStroke.fY = SkScalarAbs(
2801 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002802 }
2803
jvanverthc3d0e422016-08-25 08:12:35 -07002804 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2805 // for non-circular rrects, if half of strokewidth is greater than radius,
2806 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002807 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2808 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002809 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002810 }
2811 }
2812
2813 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2814 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2815 // patch will have fractional coverage. This only matters when the interior is actually filled.
2816 // We could consider falling back to rect rendering here, since a tiny radius is
2817 // indistinguishable from a square corner.
2818 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002819 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002820 }
2821
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002822 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002823 if (isCircular) {
Robert Phillips7c525e62018-06-12 10:11:12 -04002824 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, xRadius,
2825 scaledStroke.fX, isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05002826 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002827 } else {
Robert Phillips7c525e62018-06-12 10:11:12 -04002828 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
2829 xRadius, yRadius, scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002830 }
joshualitt3e708c52015-04-30 13:49:27 -07002831}
2832
Robert Phillips7c525e62018-06-12 10:11:12 -04002833std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrContext* context,
2834 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002835 const SkMatrix& viewMatrix,
2836 const SkRRect& rrect,
2837 const SkStrokeRec& stroke,
2838 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002839 if (rrect.isOval()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04002840 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
2841 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002842 }
2843
2844 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002845 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002846 }
2847
Robert Phillips7c525e62018-06-12 10:11:12 -04002848 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002849}
joshualitt3e708c52015-04-30 13:49:27 -07002850
bsalomon4b4a7cc2016-07-08 04:42:54 -07002851///////////////////////////////////////////////////////////////////////////////
2852
Robert Phillips7c525e62018-06-12 10:11:12 -04002853std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrContext* context,
2854 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002855 const SkMatrix& viewMatrix,
2856 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002857 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002858 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002859 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002860 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04002861 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2862 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002863 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002864 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002865 if (style.hasNonDashPathEffect()) {
2866 return nullptr;
2867 } else if (style.isDashed()) {
2868 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
2869 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
2870 return nullptr;
2871 }
2872 auto onInterval = style.dashIntervals()[0];
2873 auto offInterval = style.dashIntervals()[1];
2874 if (offInterval == 0) {
2875 GrStyle strokeStyle(style.strokeRec(), nullptr);
Robert Phillips7c525e62018-06-12 10:11:12 -04002876 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
2877 strokeStyle, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002878 } else if (onInterval == 0) {
2879 // There is nothing to draw but we have no way to indicate that here.
2880 return nullptr;
2881 }
2882 auto angularOnInterval = onInterval / r;
2883 auto angularOffInterval = offInterval / r;
2884 auto phaseAngle = style.dashPhase() / r;
2885 // Currently this function doesn't accept ovals with different start angles, though
2886 // it could.
2887 static const SkScalar kStartAngle = 0.f;
Robert Phillips7c525e62018-06-12 10:11:12 -04002888 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002889 style.strokeRec().getWidth(), kStartAngle,
2890 angularOnInterval, angularOffInterval, phaseAngle);
2891 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002892 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002893 }
2894
2895 if (style.pathEffect()) {
2896 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002897 }
2898
Stan Ilieveb868aa2017-02-21 11:06:16 -05002899 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002900 if (viewMatrix.rectStaysRect()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04002901 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07002902 }
2903
Stan Ilieveb868aa2017-02-21 11:06:16 -05002904 // Otherwise, if we have shader derivative support, render as device-independent
2905 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04002906 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2907 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2908 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2909 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2910 // Check for near-degenerate matrix
2911 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
2912 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
2913 style.strokeRec());
2914 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05002915 }
2916
bsalomon4b4a7cc2016-07-08 04:42:54 -07002917 return nullptr;
2918}
2919
2920///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002921
Robert Phillips7c525e62018-06-12 10:11:12 -04002922std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrContext* context,
2923 GrPaint&& paint,
2924 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002925 const SkRect& oval, SkScalar startAngle,
2926 SkScalar sweepAngle, bool useCenter,
2927 const GrStyle& style,
2928 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002929 SkASSERT(!oval.isEmpty());
2930 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002931 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002932 if (SkScalarAbs(sweepAngle) >= 360.f) {
2933 return nullptr;
2934 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002935 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2936 return nullptr;
2937 }
2938 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002939 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2940 useCenter};
Robert Phillips7c525e62018-06-12 10:11:12 -04002941 return CircleOp::Make(context, std::move(paint), viewMatrix,
2942 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002943}
2944
2945///////////////////////////////////////////////////////////////////////////////
2946
Hal Canary6f6961e2017-01-31 13:50:44 -05002947#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002948
Brian Salomon05441c42017-05-15 16:45:49 -04002949GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002950 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002951 SkScalar rotate = random->nextSScalar1() * 360.f;
2952 SkScalar translateX = random->nextSScalar1() * 1000.f;
2953 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04002954 SkScalar scale;
2955 do {
2956 scale = random->nextSScalar1() * 100.f;
2957 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07002958 SkMatrix viewMatrix;
2959 viewMatrix.setRotate(rotate);
2960 viewMatrix.postTranslate(translateX, translateY);
2961 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002962 SkRect circle = GrTest::TestSquare(random);
2963 SkPoint center = {circle.centerX(), circle.centerY()};
2964 SkScalar radius = circle.width() / 2.f;
2965 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002966 CircleOp::ArcParams arcParamsTmp;
2967 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002968 if (random->nextBool()) {
2969 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002970 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2971 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002972 arcParams = &arcParamsTmp;
2973 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002974 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
2975 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00002976 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002977 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002978 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002979 }
Mike Klein16885072018-12-11 09:54:31 -05002980 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002981 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002982}
2983
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002984GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
2985 SkScalar rotate = random->nextSScalar1() * 360.f;
2986 SkScalar translateX = random->nextSScalar1() * 1000.f;
2987 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04002988 SkScalar scale;
2989 do {
2990 scale = random->nextSScalar1() * 100.f;
2991 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04002992 SkMatrix viewMatrix;
2993 viewMatrix.setRotate(rotate);
2994 viewMatrix.postTranslate(translateX, translateY);
2995 viewMatrix.postScale(scale, scale);
2996 SkRect circle = GrTest::TestSquare(random);
2997 SkPoint center = {circle.centerX(), circle.centerY()};
2998 SkScalar radius = circle.width() / 2.f;
2999 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3000 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3001 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3002 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3003 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003004 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3005 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003006 startAngle, onAngle, offAngle, phase);
3007}
3008
Brian Salomon05441c42017-05-15 16:45:49 -04003009GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003010 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003011 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003012 return EllipseOp::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(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003017 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003018 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003019 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3020 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003021}
3022
Brian Salomon05441c42017-05-15 16:45:49 -04003023GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003024 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003025 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003026 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
3027 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003028}
3029
3030#endif