blob: 19495aedf0972908a8c84eabbe0d1f7824818b5f [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
commit-bot@chromium.org81312832013-03-22 18:34:09 +000042}
43
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
70 return arena->make<CircleGeometryProcessor>(stroke, clipPlane, isectPlane, unionPlane,
71 roundCaps, wideColor, localMatrix);
72 }
73
74 const char* name() const override { return "CircleGeometryProcessor"; }
75
76 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
77 GLSLProcessor::GenKey(*this, caps, b);
78 }
79
80 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
81 return new GLSLProcessor();
82 }
83
84private:
85 friend class ::SkArenaAlloc; // for access to ctor
86
Greg Daniel2655ede2019-04-10 00:49:28 +000087 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050088 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040089 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040090 , fLocalMatrix(localMatrix)
91 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050092 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050093 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050094 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
95
bsalomon4f3a0ca2016-08-22 13:14:26 -070096 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040097 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070098 }
99 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400100 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700101 }
102 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400103 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700104 }
Brian Salomon45c92202018-04-10 10:53:58 -0400105 if (roundCaps) {
106 SkASSERT(stroke);
107 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400108 fInRoundCapCenters =
109 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400110 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500111 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 }
113
egdaniel57d3b032015-11-13 11:57:27 -0800114 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000115 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800116 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117
Brian Salomon289e3d82016-12-14 15:52:56 -0500118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800119 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800120 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800121 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800122 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700123 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800124
joshualittabb52a12015-01-13 15:02:10 -0800125 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400127 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500128 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400129 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400130 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
132 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400133 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400134 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
136 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400137 if (cgp.fInUnionPlane.isInitialized()) {
138 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400139 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700140 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
141 }
Brian Salomon45c92202018-04-10 10:53:58 -0400142 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400143 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400144 fragBuilder->codeAppend("float4 roundCapCenters;");
145 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
146 varyingHandler->addVarying("capRadius", &capRadius,
147 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
148 // This is the cap radius in normalized space where the outer radius is 1 and
149 // circledEdge.w is the normalized inner radius.
150 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500151 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400152 }
joshualittabb52a12015-01-13 15:02:10 -0800153
joshualittb8c241a2015-05-19 08:23:30 -0700154 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500155 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800156
joshualittabb52a12015-01-13 15:02:10 -0800157 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500158 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400159 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
160 cgp.fInPosition.asShaderVar(), cgp.fLocalMatrix,
161 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800162
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400163 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500164 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000165 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800166 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500167 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500168 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000169 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800170 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000172
Brian Salomon92be2f72018-06-19 14:33:47 -0400173 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500174 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000175 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
176 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400177 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500178 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000179 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
180 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700181 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400182 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500183 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000184 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
185 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 }
187 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400188 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400189 // We compute coverage of the round caps as circles at the butt caps produced
190 // by the clip planes. The inverse of the clip planes is applied so that there
191 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400192 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500193 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
194 " roundCapCenters.xy)));"
195 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
196 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400197 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400198 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
199 capRadius.fsIn(), capRadius.fsIn());
200 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700201 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000202 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203 }
204
robertphillips46d36f02015-01-18 08:14:14 -0800205 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500206 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700207 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800208 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400209 uint32_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500210 key = cgp.fStroke ? 0x01 : 0x0;
Michael Ludwig553db622020-06-19 10:47:30 -0400211 key |= cgp.fInClipPlane.isInitialized() ? 0x02 : 0x0;
212 key |= cgp.fInIsectPlane.isInitialized() ? 0x04 : 0x0;
213 key |= cgp.fInUnionPlane.isInitialized() ? 0x08 : 0x0;
214 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x10 : 0x0;
215 key |= (ComputeMatrixKey(cgp.fLocalMatrix) << 16);
joshualittb8c241a2015-05-19 08:23:30 -0700216 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217 }
218
bsalomona624bf32016-09-20 09:12:47 -0700219 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500220 const CoordTransformRange& transformRange) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400221 this->setTransformDataHelper(pdman, transformRange);
222 this->setTransform(pdman, fLocalMatrixUniform,
223 primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
224 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700225 }
226
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227 private:
egdaniele659a582015-11-13 09:55:43 -0800228 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400229
230 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
231 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232 };
233
Brian Salomon289e3d82016-12-14 15:52:56 -0500234 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400235
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500236 Attribute fInPosition;
237 Attribute fInColor;
238 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400239 // Optional attributes.
240 Attribute fInClipPlane;
241 Attribute fInIsectPlane;
242 Attribute fInUnionPlane;
243 Attribute fInRoundCapCenters;
244
Brian Salomon289e3d82016-12-14 15:52:56 -0500245 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400246 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247
joshualitt249af152014-09-15 11:41:13 -0700248 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249};
250
bsalomoncdaa97b2016-03-08 08:30:14 -0800251GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000252
Hal Canary6f6961e2017-01-31 13:50:44 -0500253#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500254GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400255 bool stroke = d->fRandom->nextBool();
256 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500257 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400258 bool clipPlane = d->fRandom->nextBool();
259 bool isectPlane = d->fRandom->nextBool();
260 bool unionPlane = d->fRandom->nextBool();
261 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500262 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
263 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264}
Hal Canary6f6961e2017-01-31 13:50:44 -0500265#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400267class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
268public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500269 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
270 const SkMatrix& localMatrix) {
271 return arena->make<ButtCapDashedCircleGeometryProcessor>(wideColor, localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400272 }
273
274 ~ButtCapDashedCircleGeometryProcessor() override {}
275
276 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
277
278 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
279 GLSLProcessor::GenKey(*this, caps, b);
280 }
281
282 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
283 return new GLSLProcessor();
284 }
285
286private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500287 friend class ::SkArenaAlloc; // for access to ctor
288
289 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
290 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
291 , fLocalMatrix(localMatrix) {
292 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
293 fInColor = MakeColorAttribute("inColor", wideColor);
294 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
295 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
296 this->setVertexAttributes(&fInPosition, 4);
297 }
298
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400299 class GLSLProcessor : public GrGLSLGeometryProcessor {
300 public:
301 GLSLProcessor() {}
302
303 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
304 const ButtCapDashedCircleGeometryProcessor& bcscgp =
305 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
306 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
307 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
308 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
309 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
310
311 // emit attributes
312 varyingHandler->emitAttributes(bcscgp);
313 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500314 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400315
316 fragBuilder->codeAppend("float4 dashParams;");
317 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500318 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400319 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
320 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
321 varyingHandler->addVarying("wrapDashes", &wrapDashes,
322 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
323 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
324 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
325 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500326 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400327 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
328 // x = length of on interval, y = length of on + off.
329 // There are two other parameters in dashParams.zw:
330 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
331 // Each interval has a "corresponding" dash which may be shifted partially or
332 // fully out of its interval by the phase. So there may be up to two "visual"
333 // dashes in an interval.
334 // When computing coverage in an interval we look at three dashes. These are the
335 // "corresponding" dashes from the current, previous, and next intervals. Any of these
336 // may be phase shifted into our interval or even when phase=0 they may be within half a
337 // pixel distance of a pixel center in the interval.
338 // When in the first interval we need to check the dash from the last interval. And
339 // similarly when in the last interval we need to check the dash from the first
340 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
341 // We compute the dash begin/end angles in the vertex shader and apply them in the
342 // fragment shader when we detect we're in the first/last interval.
343 vertBuilder->codeAppend(R"(
344 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
345 // to the fragment shader as a varying.
346 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500347 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400348 // We can happen to be perfectly divisible.
349 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500350 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400351 }
352 // Let 'l' be the last interval before reaching 2 pi.
353 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
354 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
355 // interval.
356 half offset = 0;
357 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500358 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400359 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500360 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400361 }
362 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
363 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
364 // min.
365 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
366
367 // Based on the phase determine whether the -1st, 0th, or 1st interval's
368 // "corresponding" dash appears in the 0th interval and is closest to l.
369 offset = 0;
370 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500371 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400372 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500373 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400374 }
375 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
376 wrapDashes.w = wrapDashes.z + dashParams.x;
377 // The start of the dash we're considering may be clipped by the start of the
378 // circle.
379 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
380 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500381 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400382 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
383 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
384 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
385
386 // setup pass through color
387 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500388 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400389 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
390
391 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500392 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400393 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
394 bcscgp.fInPosition.asShaderVar(), bcscgp.fLocalMatrix,
395 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400396
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400397 GrShaderVar fnArgs[] = {
398 GrShaderVar("angleToEdge", kFloat_GrSLType),
399 GrShaderVar("diameter", kFloat_GrSLType),
400 };
401 SkString fnName;
402 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
403 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
404 float linearDist;
405 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
406 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400407 return saturate(linearDist + 0.5);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408 )",
409 &fnName);
410 fragBuilder->codeAppend(R"(
411 float d = length(circleEdge.xy) * circleEdge.z;
412
413 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500414 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400415 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500416 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400417 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400418 edgeAlpha *= innerAlpha;
419
Ethan Nicholase1f55022019-02-05 17:17:40 -0500420 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400421 angleFromStart = mod(angleFromStart, 6.28318530718);
422 float x = mod(angleFromStart, dashParams.y);
423 // Convert the radial distance from center to pixel into a diameter.
424 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500425 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
426 half(dashParams.w));
427 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
428 half(dashParams.y) + half(dashParams.x) -
429 half(dashParams.w));
430 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
431 half(-dashParams.y) + half(dashParams.x) -
432 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400433 half dashAlpha = 0;
434 )");
435 fragBuilder->codeAppendf(R"(
436 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500437 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400438 currDash.y = min(currDash.y, lastIntervalLength);
439 if (nextDash.x >= lastIntervalLength) {
440 // The next dash is outside the 0..2pi range, throw it away
441 nextDash.xy = half2(1000);
442 } else {
443 // Clip the end of the next dash to the end of the circle
444 nextDash.y = min(nextDash.y, lastIntervalLength);
445 }
446 }
447 )", fnName.c_str(), fnName.c_str());
448 fragBuilder->codeAppendf(R"(
449 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500450 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400451 currDash.x = max(currDash.x, 0);
452 if (prevDash.y <= 0) {
453 // The previous dash is outside the 0..2pi range, throw it away
454 prevDash.xy = half2(1000);
455 } else {
456 // Clip the start previous dash to the start of the circle
457 prevDash.x = max(prevDash.x, 0);
458 }
459 }
460 )", fnName.c_str(), fnName.c_str());
461 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500462 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
463 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
464 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400465 dashAlpha = min(dashAlpha, 1);
466 edgeAlpha *= dashAlpha;
467 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
468 fnName.c_str());
469 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
470 }
471
472 static void GenKey(const GrGeometryProcessor& gp,
473 const GrShaderCaps&,
474 GrProcessorKeyBuilder* b) {
475 const ButtCapDashedCircleGeometryProcessor& bcscgp =
476 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400477 b->add32(ComputeMatrixKey(bcscgp.fLocalMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400478 }
479
480 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500481 const CoordTransformRange& transformRange) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400482 this->setTransformDataHelper(pdman, transformRange);
483 this->setTransform(pdman, fLocalMatrixUniform,
484 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
485 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400486 }
487
488 private:
489 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400490
491 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
492 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400493 };
494
495 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500496 Attribute fInPosition;
497 Attribute fInColor;
498 Attribute fInCircleEdge;
499 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400500
501 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
502
503 typedef GrGeometryProcessor INHERITED;
504};
505
506#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500507GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500508 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400509 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500510 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400511}
512#endif
513
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000514///////////////////////////////////////////////////////////////////////////////
515
516/**
517 * 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 +0000518 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
519 * in both x and y directions.
520 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000521 * 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 +0000522 */
523
bsalomoncdaa97b2016-03-08 08:30:14 -0800524class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000525public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500526 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
527 bool useScale, const SkMatrix& localMatrix) {
528 return arena->make<EllipseGeometryProcessor>(stroke, wideColor, useScale, localMatrix);
529 }
530
531 ~EllipseGeometryProcessor() override {}
532
533 const char* name() const override { return "EllipseGeometryProcessor"; }
534
535 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
536 GLSLProcessor::GenKey(*this, caps, b);
537 }
538
539 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
540 return new GLSLProcessor();
541 }
542
543private:
544 friend class ::SkArenaAlloc; // for access to ctor
545
Greg Daniel2655ede2019-04-10 00:49:28 +0000546 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400547 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500548 : INHERITED(kEllipseGeometryProcessor_ClassID)
549 , fLocalMatrix(localMatrix)
550 , fStroke(stroke)
551 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500552 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500553 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400554 if (useScale) {
555 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
556 } else {
557 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
558 }
559 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500560 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000561 }
562
egdaniel57d3b032015-11-13 11:57:27 -0800563 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000564 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800565 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000566
Brian Salomon289e3d82016-12-14 15:52:56 -0500567 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800568 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800569 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800570 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800571 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000572
joshualittabb52a12015-01-13 15:02:10 -0800573 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800574 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800575
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400576 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
577 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800578 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800579 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500580 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000581
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400582 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800583 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500584 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800585
Chris Dalton60283612018-02-14 13:38:14 -0700586 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700587 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500588 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800589
joshualittabb52a12015-01-13 15:02:10 -0800590 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500591 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400592 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
593 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
594 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800595
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400596 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
597 // to compute both the edges because we need two separate test equations for
598 // the single offset.
599 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
600 // the distance by the gradient, non-uniformly scaled by the inverse of the
601 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800602
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400603 // On medium precision devices, we scale the denominator of the distance equation
604 // before taking the inverse square root to minimize the chance that we're dividing
605 // by zero, then we scale the result back.
606
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000607 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400608 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400609 if (egp.fStroke) {
610 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
611 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400612 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
613 if (egp.fUseScale) {
614 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
615 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
616 } else {
617 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
618 }
619 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700620
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000621 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400622 if (args.fShaderCaps->floatIs32Bits()) {
623 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
624 } else {
625 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
626 }
627 if (egp.fUseScale) {
628 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
629 ellipseOffsets.fsIn());
630 } else {
631 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
632 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000633 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000634
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000635 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800636 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400637 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800638 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400639 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400640 if (egp.fUseScale) {
641 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
642 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
643 } else {
644 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
645 }
646 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
647 if (!args.fShaderCaps->floatIs32Bits()) {
648 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
649 }
650 if (egp.fUseScale) {
651 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
652 ellipseOffsets.fsIn());
653 } else {
654 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
655 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000656 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000657 }
658
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400659 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000660 }
661
robertphillips46d36f02015-01-18 08:14:14 -0800662 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500663 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700664 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800665 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400666 uint32_t key = egp.fStroke ? 0x1 : 0x0;
667 key |= ComputeMatrixKey(egp.fLocalMatrix) << 1;
joshualittb8c241a2015-05-19 08:23:30 -0700668 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000669 }
670
bsalomona624bf32016-09-20 09:12:47 -0700671 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500672 const CoordTransformRange& transformRange) override {
bsalomona624bf32016-09-20 09:12:47 -0700673 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400674 this->setTransformDataHelper(pdman, transformRange);
675 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700676 }
677
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000678 private:
egdaniele659a582015-11-13 09:55:43 -0800679 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400680
681 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
682 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000683 };
684
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500685 Attribute fInPosition;
686 Attribute fInColor;
687 Attribute fInEllipseOffset;
688 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400689
joshualitte3ababe2015-05-15 07:56:07 -0700690 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000691 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400692 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000693
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400694 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000695
joshualitt249af152014-09-15 11:41:13 -0700696 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000697};
698
bsalomoncdaa97b2016-03-08 08:30:14 -0800699GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000700
Hal Canary6f6961e2017-01-31 13:50:44 -0500701#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500702GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
703 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
704 d->fRandom->nextBool(), d->fRandom->nextBool(),
705 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000706}
Hal Canary6f6961e2017-01-31 13:50:44 -0500707#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000708
709///////////////////////////////////////////////////////////////////////////////
710
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000711/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000712 * 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 +0000713 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
714 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
715 * using differentials.
716 *
717 * The result is device-independent and can be used with any affine matrix.
718 */
719
bsalomoncdaa97b2016-03-08 08:30:14 -0800720enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000721
bsalomoncdaa97b2016-03-08 08:30:14 -0800722class DIEllipseGeometryProcessor : public GrGeometryProcessor {
723public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500724 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
725 const SkMatrix& viewMatrix, DIEllipseStyle style) {
726 return arena->make<DIEllipseGeometryProcessor>(wideColor, useScale, viewMatrix, style);
727 }
728
729 ~DIEllipseGeometryProcessor() override {}
730
731 const char* name() const override { return "DIEllipseGeometryProcessor"; }
732
733 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
734 GLSLProcessor::GenKey(*this, caps, b);
735 }
736
737 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
738 return new GLSLProcessor();
739 }
740
741private:
742 friend class ::SkArenaAlloc; // for access to ctor
743
Greg Daniel2655ede2019-04-10 00:49:28 +0000744 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400745 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400746 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400747 , fViewMatrix(viewMatrix)
748 , fUseScale(useScale)
749 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500750 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500751 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400752 if (useScale) {
753 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
754 kFloat3_GrSLType};
755 } else {
756 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
757 kFloat2_GrSLType};
758 }
759 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500760 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000761 }
762
egdaniel57d3b032015-11-13 11:57:27 -0800763 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000764 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500765 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000766
joshualitt465283c2015-09-11 08:19:35 -0700767 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800768 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800769 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800770 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800771 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000772
joshualittabb52a12015-01-13 15:02:10 -0800773 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800774 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800775
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400776 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
777 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800778 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500779 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700780
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400781 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800782 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500783 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800784
Chris Dalton60283612018-02-14 13:38:14 -0700785 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500786 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800787
joshualittabb52a12015-01-13 15:02:10 -0800788 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400789 this->writeOutputPosition(vertBuilder,
790 uniformHandler,
791 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500792 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400793 diegp.fViewMatrix,
794 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400795 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800796
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000797 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400798 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
799 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
800 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
801 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500802 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400803 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
804 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500805 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400806 if (diegp.fUseScale) {
807 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
808 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000809
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400810 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000811 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400812 if (args.fShaderCaps->floatIs32Bits()) {
813 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
814 } else {
815 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
816 }
817 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
818 if (diegp.fUseScale) {
819 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
820 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800821 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000822 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000823 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
824 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000825 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000826 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000827 }
828
829 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800830 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800831 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
832 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400833 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
834 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500835 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400836 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
837 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500838 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400839 if (diegp.fUseScale) {
840 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
841 }
842 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
843 if (!args.fShaderCaps->floatIs32Bits()) {
844 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
845 }
846 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
847 if (diegp.fUseScale) {
848 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
849 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000850 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000851 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000852
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400853 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000854 }
855
robertphillips46d36f02015-01-18 08:14:14 -0800856 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500857 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700858 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800859 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400860 uint32_t key = static_cast<uint32_t>(diegp.fStyle);
861 key |= ComputeMatrixKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700862 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000863 }
864
bsalomona624bf32016-09-20 09:12:47 -0700865 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
Brian Salomonc241b582019-11-27 08:57:17 -0500866 const CoordTransformRange& transformRange) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800867 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700868
Michael Ludwig553db622020-06-19 10:47:30 -0400869 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
870 this->setTransformDataHelper(pdman, transformRange);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000871 }
872
873 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400874 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700875 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800876
egdaniele659a582015-11-13 09:55:43 -0800877 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000878 };
879
Brian Salomon92be2f72018-06-19 14:33:47 -0400880
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500881 Attribute fInPosition;
882 Attribute fInColor;
883 Attribute fInEllipseOffsets0;
884 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400885
Brian Salomon289e3d82016-12-14 15:52:56 -0500886 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400887 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500888 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000889
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400890 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000891
joshualitt249af152014-09-15 11:41:13 -0700892 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000893};
894
bsalomoncdaa97b2016-03-08 08:30:14 -0800895GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000896
Hal Canary6f6961e2017-01-31 13:50:44 -0500897#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500898GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
899 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
900 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
901 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000902}
Hal Canary6f6961e2017-01-31 13:50:44 -0500903#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000904
905///////////////////////////////////////////////////////////////////////////////
906
jvanverth6ca48822016-10-07 06:57:32 -0700907// We have two possible cases for geometry for a circle:
908
909// In the case of a normal fill, we draw geometry for the circle as an octagon.
910static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500911 // enter the octagon
912 // clang-format off
913 0, 1, 8, 1, 2, 8,
914 2, 3, 8, 3, 4, 8,
915 4, 5, 8, 5, 6, 8,
916 6, 7, 8, 7, 0, 8
917 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700918};
919
920// For stroked circles, we use two nested octagons.
921static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500922 // enter the octagon
923 // clang-format off
924 0, 1, 9, 0, 9, 8,
925 1, 2, 10, 1, 10, 9,
926 2, 3, 11, 2, 11, 10,
927 3, 4, 12, 3, 12, 11,
928 4, 5, 13, 4, 13, 12,
929 5, 6, 14, 5, 14, 13,
930 6, 7, 15, 6, 15, 14,
931 7, 0, 8, 7, 8, 15,
932 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700933};
934
Brian Osman9d958b52018-11-13 12:46:56 -0500935// Normalized geometry for octagons that circumscribe and lie on a circle:
936
937static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
938static constexpr SkPoint kOctagonOuter[] = {
939 SkPoint::Make(-kOctOffset, -1),
940 SkPoint::Make( kOctOffset, -1),
941 SkPoint::Make( 1, -kOctOffset),
942 SkPoint::Make( 1, kOctOffset),
943 SkPoint::Make( kOctOffset, 1),
944 SkPoint::Make(-kOctOffset, 1),
945 SkPoint::Make(-1, kOctOffset),
946 SkPoint::Make(-1, -kOctOffset),
947};
948
949// cosine and sine of pi/8
950static constexpr SkScalar kCosPi8 = 0.923579533f;
951static constexpr SkScalar kSinPi8 = 0.382683432f;
952static constexpr SkPoint kOctagonInner[] = {
953 SkPoint::Make(-kSinPi8, -kCosPi8),
954 SkPoint::Make( kSinPi8, -kCosPi8),
955 SkPoint::Make( kCosPi8, -kSinPi8),
956 SkPoint::Make( kCosPi8, kSinPi8),
957 SkPoint::Make( kSinPi8, kCosPi8),
958 SkPoint::Make(-kSinPi8, kCosPi8),
959 SkPoint::Make(-kCosPi8, kSinPi8),
960 SkPoint::Make(-kCosPi8, -kSinPi8),
961};
Brian Salomon289e3d82016-12-14 15:52:56 -0500962
jvanverth6ca48822016-10-07 06:57:32 -0700963static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
964static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
965static const int kVertsPerStrokeCircle = 16;
966static const int kVertsPerFillCircle = 9;
967
968static int circle_type_to_vert_count(bool stroked) {
969 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
970}
971
972static int circle_type_to_index_count(bool stroked) {
973 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
974}
975
976static const uint16_t* circle_type_to_indices(bool stroked) {
977 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
978}
979
980///////////////////////////////////////////////////////////////////////////////
981
Brian Salomon05441c42017-05-15 16:45:49 -0400982class CircleOp final : public GrMeshDrawOp {
983private:
984 using Helper = GrSimpleMeshDrawOpHelper;
985
joshualitt76e7fb62015-02-11 08:52:27 -0800986public:
Brian Salomon25a88092016-12-01 09:36:50 -0500987 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700988
bsalomon4f3a0ca2016-08-22 13:14:26 -0700989 /** Optional extra params to render a partial arc rather than a full circle. */
990 struct ArcParams {
991 SkScalar fStartAngleRadians;
992 SkScalar fSweepAngleRadians;
993 bool fUseCenter;
994 };
Brian Salomon05441c42017-05-15 16:45:49 -0400995
Robert Phillipsb97da532019-02-12 15:24:12 -0500996 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400997 GrPaint&& paint,
998 const SkMatrix& viewMatrix,
999 SkPoint center,
1000 SkScalar radius,
1001 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -04001002 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001003 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001004 if (style.hasPathEffect()) {
1005 return nullptr;
1006 }
Brian Salomon05441c42017-05-15 16:45:49 -04001007 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001008 SkStrokeRec::Style recStyle = stroke.getStyle();
1009 if (arcParams) {
1010 // Arc support depends on the style.
1011 switch (recStyle) {
1012 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001013 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001014 return nullptr;
1015 case SkStrokeRec::kFill_Style:
1016 // This supports all fills.
1017 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001018 case SkStrokeRec::kStroke_Style:
1019 // Strokes that don't use the center point are supported with butt and round
1020 // caps.
1021 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1022 return nullptr;
1023 }
1024 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001025 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001026 // Hairline only supports butt cap. Round caps could be emulated by slightly
1027 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001028 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1029 return nullptr;
1030 }
1031 break;
1032 }
1033 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001034 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1035 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001036 }
1037
Greg Daniel2655ede2019-04-10 00:49:28 +00001038 CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001039 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1040 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001041 : GrMeshDrawOp(ClassID())
1042 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001043 const SkStrokeRec& stroke = style.strokeRec();
1044 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001045
Brian Salomon45c92202018-04-10 10:53:58 -04001046 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001047
bsalomon4b4a7cc2016-07-08 04:42:54 -07001048 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001049 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001050 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001051
Brian Salomon289e3d82016-12-14 15:52:56 -05001052 bool isStrokeOnly =
1053 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001054 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001055
jvanverth6ca48822016-10-07 06:57:32 -07001056 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001057 SkScalar outerRadius = radius;
1058 SkScalar halfWidth = 0;
1059 if (hasStroke) {
1060 if (SkScalarNearlyZero(strokeWidth)) {
1061 halfWidth = SK_ScalarHalf;
1062 } else {
1063 halfWidth = SkScalarHalf(strokeWidth);
1064 }
1065
1066 outerRadius += halfWidth;
1067 if (isStrokeOnly) {
1068 innerRadius = radius - halfWidth;
1069 }
1070 }
1071
1072 // The radii are outset for two reasons. First, it allows the shader to simply perform
1073 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1074 // Second, the outer radius is used to compute the verts of the bounding box that is
1075 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001076 outerRadius += SK_ScalarHalf;
1077 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001078 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001079 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001080
bsalomon4f3a0ca2016-08-22 13:14:26 -07001081 // This makes every point fully inside the intersection plane.
1082 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1083 // This makes every point fully outside the union plane.
1084 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001085 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001086 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1087 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001088 if (arcParams) {
1089 // The shader operates in a space where the circle is translated to be centered at the
1090 // origin. Here we compute points on the unit circle at the starting and ending angles.
1091 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001092 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1093 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001094 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001095 stopPoint.fY = SkScalarSin(endAngle);
1096 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001097
1098 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1099 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1100 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1101 startPoint.normalize();
1102 stopPoint.normalize();
1103
Brian Salomon3517aa72019-12-11 08:16:22 -05001104 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1105 // should orient the clip planes for arcs.
1106 SkASSERT(viewMatrix.isSimilarity());
1107 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1108 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1109 if (upperLeftDet < 0) {
1110 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001111 }
1112
Brian Salomon45c92202018-04-10 10:53:58 -04001113 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1114 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1115 SkPoint roundCaps[2];
1116 if (fRoundCaps) {
1117 // Compute the cap center points in the normalized space.
1118 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1119 roundCaps[0] = startPoint * midRadius;
1120 roundCaps[1] = stopPoint * midRadius;
1121 } else {
1122 roundCaps[0] = kUnusedRoundCaps[0];
1123 roundCaps[1] = kUnusedRoundCaps[1];
1124 }
1125
bsalomon4f3a0ca2016-08-22 13:14:26 -07001126 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001127 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1128 // center of the butts.
1129 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001130 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001131 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001132 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001133 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1134 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1135 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001136 if (useCenter) {
1137 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1138 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001139 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1140 if (arcParams->fSweepAngleRadians < 0) {
1141 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001142 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001143 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001144 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001145 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001146 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001147 color,
1148 innerRadius,
1149 outerRadius,
1150 {norm0.fX, norm0.fY, 0.5f},
1151 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1152 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001153 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001154 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001155 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001156 fClipPlaneIsect = false;
1157 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001158 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001159 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001160 color,
1161 innerRadius,
1162 outerRadius,
1163 {norm0.fX, norm0.fY, 0.5f},
1164 {norm1.fX, norm1.fY, 0.5f},
1165 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001166 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001167 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001168 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001169 fClipPlaneIsect = true;
1170 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001171 }
1172 } else {
1173 // We clip to a secant of the original circle.
1174 startPoint.scale(radius);
1175 stopPoint.scale(radius);
1176 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1177 norm.normalize();
1178 if (arcParams->fSweepAngleRadians > 0) {
1179 norm.negate();
1180 }
1181 SkScalar d = -norm.dot(startPoint) + 0.5f;
1182
Brian Salomon05441c42017-05-15 16:45:49 -04001183 fCircles.emplace_back(
1184 Circle{color,
1185 innerRadius,
1186 outerRadius,
1187 {norm.fX, norm.fY, d},
1188 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1189 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001190 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001191 devBounds,
1192 stroked});
1193 fClipPlane = true;
1194 fClipPlaneIsect = false;
1195 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001196 }
1197 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001198 fCircles.emplace_back(
1199 Circle{color,
1200 innerRadius,
1201 outerRadius,
1202 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1203 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1204 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001205 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001206 devBounds,
1207 stroked});
1208 fClipPlane = false;
1209 fClipPlaneIsect = false;
1210 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001211 }
bsalomon88cf17d2016-07-08 06:40:56 -07001212 // Use the original radius and stroke radius for the bounds so that it does not include the
1213 // AA bloat.
1214 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001215 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001216 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001217 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001218 fVertCount = circle_type_to_vert_count(stroked);
1219 fIndexCount = circle_type_to_index_count(stroked);
1220 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001221 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001222
Brian Salomon289e3d82016-12-14 15:52:56 -05001223 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001224
Chris Dalton1706cbf2019-05-21 19:35:29 -06001225 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001226 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001227 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001228 } else {
1229 fHelper.visitProxies(func);
1230 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001231 }
1232
Brian Osman9a390ac2018-11-12 09:47:48 -05001233#ifdef SK_DEBUG
robertphillipse004bfc2015-11-16 09:06:59 -08001234 SkString dumpInfo() const override {
1235 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001236 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001237 string.appendf(
1238 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1239 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001240 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001241 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1242 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1243 fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001244 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001245 string += fHelper.dumpInfo();
1246 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001247 return string;
1248 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001249#endif
robertphillipse004bfc2015-11-16 09:06:59 -08001250
Chris Dalton6ce447a2019-06-23 18:07:38 -06001251 GrProcessorSet::Analysis finalize(
1252 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1253 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001254 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001255 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001256 GrProcessorAnalysisCoverage::kSingleChannel, color,
1257 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001258 }
1259
1260 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1261
bsalomone46f9fe2015-08-18 06:05:14 -07001262private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001263 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001264
Robert Phillips4133dc42020-03-11 15:55:55 -04001265 void onCreateProgramInfo(const GrCaps* caps,
1266 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001267 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001268 GrAppliedClip&& appliedClip,
1269 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001270 SkMatrix localMatrix;
1271 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001272 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001273 }
1274
Robert Phillips4490d922020-03-03 14:50:59 -05001275 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001276 fClipPlaneIsect, fClipPlaneUnion,
1277 fRoundCaps, fWideColor,
1278 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001279
Brian Salomon8afde5f2020-04-01 16:22:00 -04001280 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001281 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001282 }
1283
Robert Phillips4490d922020-03-03 14:50:59 -05001284 void onPrepareDraws(Target* target) override {
1285 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001286 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001287 if (!fProgramInfo) {
1288 return;
1289 }
1290 }
1291
Brian Salomon12d22642019-01-29 14:38:50 -05001292 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001293 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001294 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1295 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001296 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001297 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001298 return;
1299 }
1300
Brian Salomon12d22642019-01-29 14:38:50 -05001301 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001302 int firstIndex = 0;
1303 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1304 if (!indices) {
1305 SkDebugf("Could not allocate indices\n");
1306 return;
1307 }
1308
1309 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001310 for (const auto& circle : fCircles) {
1311 SkScalar innerRadius = circle.fInnerRadius;
1312 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001313 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001314 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001315
joshualitt76e7fb62015-02-11 08:52:27 -08001316 // The inner radius in the vertex data must be specified in normalized space.
1317 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001318 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001319
1320 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001321 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001322
Brian Osman9a24fee2018-08-03 09:48:42 -04001323 SkVector geoClipPlane = { 0, 0 };
1324 SkScalar offsetClipDist = SK_Scalar1;
1325 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1326 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1327 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1328 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1329 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1330 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1331 // the AA can extend just past the center of the circle.
1332 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1333 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1334 SkAssertResult(geoClipPlane.normalize());
1335 offsetClipDist = 0.5f / halfWidth;
1336 }
1337
Brian Osman7d8f82b2018-11-08 10:24:09 -05001338 for (int i = 0; i < 8; ++i) {
1339 // This clips the normalized offset to the half-plane we computed above. Then we
1340 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001341 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001342 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001343 vertices.write(center + offset * halfWidth,
1344 color,
1345 offset,
1346 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001347 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001348 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001349 }
1350 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001351 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001352 }
1353 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001354 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001355 }
1356 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001357 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001358 }
Brian Salomon45c92202018-04-10 10:53:58 -04001359 }
jvanverth6ca48822016-10-07 06:57:32 -07001360
Brian Salomon05441c42017-05-15 16:45:49 -04001361 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001362 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001363
Brian Osman7d8f82b2018-11-08 10:24:09 -05001364 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1366 color,
1367 kOctagonInner[i] * innerRadius,
1368 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001369 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001370 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001371 }
1372 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001373 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001374 }
1375 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001376 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001377 }
1378 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001379 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001380 }
Brian Salomon45c92202018-04-10 10:53:58 -04001381 }
jvanverth6ca48822016-10-07 06:57:32 -07001382 } else {
1383 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001384 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001385 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001386 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001387 }
jvanverth6ca48822016-10-07 06:57:32 -07001388 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001389 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001390 }
1391 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001392 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001393 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001394 if (fRoundCaps) {
1395 vertices.write(circle.fRoundCapCenters);
1396 }
jvanverth6ca48822016-10-07 06:57:32 -07001397 }
1398
Brian Salomon05441c42017-05-15 16:45:49 -04001399 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1400 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001401 for (int i = 0; i < primIndexCount; ++i) {
1402 *indices++ = primIndices[i] + currStartVertex;
1403 }
1404
Brian Salomon05441c42017-05-15 16:45:49 -04001405 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001406 }
jvanverth6ca48822016-10-07 06:57:32 -07001407
Robert Phillips4490d922020-03-03 14:50:59 -05001408 fMesh = target->allocMesh();
1409 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001410 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001411 }
1412
1413 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001414 if (!fProgramInfo || !fMesh) {
1415 return;
1416 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001417
Chris Dalton765ed362020-03-16 17:34:44 -06001418 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1419 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1420 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001421 }
1422
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001423 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1424 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001425 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001426
1427 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001428 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001429 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001430 }
1431
Brian Salomon05441c42017-05-15 16:45:49 -04001432 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001433 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001434 }
1435
Brian Salomon05441c42017-05-15 16:45:49 -04001436 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001437 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1438 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001439 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001440 }
1441
Brian Salomon289e3d82016-12-14 15:52:56 -05001442 // Because we've set up the ops that don't use the planes with noop values
1443 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001444 fClipPlane |= that->fClipPlane;
1445 fClipPlaneIsect |= that->fClipPlaneIsect;
1446 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001447 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001448 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001449
Brian Salomon05441c42017-05-15 16:45:49 -04001450 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001451 fVertCount += that->fVertCount;
1452 fIndexCount += that->fIndexCount;
1453 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001454 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001455 }
1456
Brian Salomon05441c42017-05-15 16:45:49 -04001457 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001458 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001459 SkScalar fInnerRadius;
1460 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001461 SkScalar fClipPlane[3];
1462 SkScalar fIsectPlane[3];
1463 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001464 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001465 SkRect fDevBounds;
1466 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001467 };
1468
Brian Salomon289e3d82016-12-14 15:52:56 -05001469 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001470 Helper fHelper;
1471 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001472 int fVertCount;
1473 int fIndexCount;
1474 bool fAllFill;
1475 bool fClipPlane;
1476 bool fClipPlaneIsect;
1477 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001478 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001479 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001480
Chris Daltoneb694b72020-03-16 09:25:50 -06001481 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001482 GrProgramInfo* fProgramInfo = nullptr;
1483
Brian Salomon05441c42017-05-15 16:45:49 -04001484 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001485};
1486
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001487class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1488private:
1489 using Helper = GrSimpleMeshDrawOpHelper;
1490
1491public:
1492 DEFINE_OP_CLASS_ID
1493
Robert Phillipsb97da532019-02-12 15:24:12 -05001494 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001495 GrPaint&& paint,
1496 const SkMatrix& viewMatrix,
1497 SkPoint center,
1498 SkScalar radius,
1499 SkScalar strokeWidth,
1500 SkScalar startAngle,
1501 SkScalar onAngle,
1502 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001503 SkScalar phaseAngle) {
1504 SkASSERT(circle_stays_circle(viewMatrix));
1505 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001506 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1507 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001508 onAngle, offAngle, phaseAngle);
1509 }
1510
Brian Osmancf860852018-10-31 14:04:39 -04001511 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001512 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1513 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1514 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001515 : GrMeshDrawOp(ClassID())
1516 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001517 SkASSERT(circle_stays_circle(viewMatrix));
1518 viewMatrix.mapPoints(&center, 1);
1519 radius = viewMatrix.mapRadius(radius);
1520 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1521
1522 // Determine the angle where the circle starts in device space and whether its orientation
1523 // has been reversed.
1524 SkVector start;
1525 bool reflection;
1526 if (!startAngle) {
1527 start = {1, 0};
1528 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001529 start.fY = SkScalarSin(startAngle);
1530 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001531 }
1532 viewMatrix.mapVectors(&start, 1);
1533 startAngle = SkScalarATan2(start.fY, start.fX);
1534 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1535 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1536
1537 auto totalAngle = onAngle + offAngle;
1538 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1539
1540 SkScalar halfWidth = 0;
1541 if (SkScalarNearlyZero(strokeWidth)) {
1542 halfWidth = SK_ScalarHalf;
1543 } else {
1544 halfWidth = SkScalarHalf(strokeWidth);
1545 }
1546
1547 SkScalar outerRadius = radius + halfWidth;
1548 SkScalar innerRadius = radius - halfWidth;
1549
1550 // The radii are outset for two reasons. First, it allows the shader to simply perform
1551 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1552 // Second, the outer radius is used to compute the verts of the bounding box that is
1553 // rendered and the outset ensures the box will cover all partially covered by the circle.
1554 outerRadius += SK_ScalarHalf;
1555 innerRadius -= SK_ScalarHalf;
1556 fViewMatrixIfUsingLocalCoords = viewMatrix;
1557
1558 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1559 center.fX + outerRadius, center.fY + outerRadius);
1560
1561 // We store whether there is a reflection as a negative total angle.
1562 if (reflection) {
1563 totalAngle = -totalAngle;
1564 }
1565 fCircles.push_back(Circle{
1566 color,
1567 outerRadius,
1568 innerRadius,
1569 onAngle,
1570 totalAngle,
1571 startAngle,
1572 phaseAngle,
1573 devBounds
1574 });
1575 // Use the original radius and stroke radius for the bounds so that it does not include the
1576 // AA bloat.
1577 radius += halfWidth;
1578 this->setBounds(
1579 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001580 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001581 fVertCount = circle_type_to_vert_count(true);
1582 fIndexCount = circle_type_to_index_count(true);
1583 }
1584
1585 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1586
Chris Dalton1706cbf2019-05-21 19:35:29 -06001587 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001588 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001589 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001590 } else {
1591 fHelper.visitProxies(func);
1592 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001593 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001594
Brian Osman9a390ac2018-11-12 09:47:48 -05001595#ifdef SK_DEBUG
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001596 SkString dumpInfo() const override {
1597 SkString string;
1598 for (int i = 0; i < fCircles.count(); ++i) {
1599 string.appendf(
1600 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1601 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1602 "Phase: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001603 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001604 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1605 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1606 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1607 fCircles[i].fPhaseAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001608 }
1609 string += fHelper.dumpInfo();
1610 string += INHERITED::dumpInfo();
1611 return string;
1612 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001613#endif
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001614
Chris Dalton6ce447a2019-06-23 18:07:38 -06001615 GrProcessorSet::Analysis finalize(
1616 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1617 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001618 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001619 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001620 GrProcessorAnalysisCoverage::kSingleChannel, color,
1621 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001622 }
1623
1624 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1625
1626private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001627 GrProgramInfo* programInfo() override { return fProgramInfo; }
1628
Robert Phillips4133dc42020-03-11 15:55:55 -04001629 void onCreateProgramInfo(const GrCaps* caps,
1630 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001631 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001632 GrAppliedClip&& appliedClip,
1633 const GrXferProcessor::DstProxyView& dstProxyView) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001634 SkMatrix localMatrix;
1635 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001636 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001637 }
1638
1639 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001640 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001641 fWideColor,
1642 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001643
Brian Salomon8afde5f2020-04-01 16:22:00 -04001644 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001645 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001646 }
1647
Robert Phillips4490d922020-03-03 14:50:59 -05001648 void onPrepareDraws(Target* target) override {
1649 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001650 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001651 if (!fProgramInfo) {
1652 return;
1653 }
1654 }
1655
Brian Salomon12d22642019-01-29 14:38:50 -05001656 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001657 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001658 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1659 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001660 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001661 SkDebugf("Could not allocate vertices\n");
1662 return;
1663 }
1664
Brian Salomon12d22642019-01-29 14:38:50 -05001665 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001666 int firstIndex = 0;
1667 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1668 if (!indices) {
1669 SkDebugf("Could not allocate indices\n");
1670 return;
1671 }
1672
1673 int currStartVertex = 0;
1674 for (const auto& circle : fCircles) {
1675 // The inner radius in the vertex data must be specified in normalized space so that
1676 // length() can be called with smaller values to avoid precision issues with half
1677 // floats.
1678 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1679 const SkRect& bounds = circle.fDevBounds;
1680 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001681 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1682 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1683 };
1684 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001685 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001686 dashParams.totalAngle = -dashParams.totalAngle;
1687 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001688 }
1689
Brian Osmane3caf2d2018-11-21 13:48:36 -05001690 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001691
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001692 // The bounding geometry for the circle is composed of an outer bounding octagon and
1693 // an inner bounded octagon.
1694
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001695 // Compute the vertices of the outer octagon.
1696 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1697 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001698
1699 auto reflectY = [=](const SkPoint& p) {
1700 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001701 };
Brian Osman9d958b52018-11-13 12:46:56 -05001702
1703 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001704 vertices.write(center + kOctagonOuter[i] * halfWidth,
1705 color,
1706 reflectY(kOctagonOuter[i]),
1707 circle.fOuterRadius,
1708 normInnerRadius,
1709 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001710 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001711
1712 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001713 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001714 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1715 color,
1716 reflectY(kOctagonInner[i]) * normInnerRadius,
1717 circle.fOuterRadius,
1718 normInnerRadius,
1719 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001720 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001721
1722 const uint16_t* primIndices = circle_type_to_indices(true);
1723 const int primIndexCount = circle_type_to_index_count(true);
1724 for (int i = 0; i < primIndexCount; ++i) {
1725 *indices++ = primIndices[i] + currStartVertex;
1726 }
1727
1728 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001729 }
1730
Robert Phillips4490d922020-03-03 14:50:59 -05001731 fMesh = target->allocMesh();
1732 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001733 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001734 }
1735
1736 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001737 if (!fProgramInfo || !fMesh) {
1738 return;
1739 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001740
Chris Dalton765ed362020-03-16 17:34:44 -06001741 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1742 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1743 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001744 }
1745
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001746 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1747 const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001748 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1749
1750 // can only represent 65535 unique vertices with 16-bit indices
1751 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001752 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001753 }
1754
1755 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001756 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001757 }
1758
1759 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001760 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1761 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001762 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001763 }
1764
1765 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001766 fVertCount += that->fVertCount;
1767 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001768 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001769 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001770 }
1771
1772 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001773 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001774 SkScalar fOuterRadius;
1775 SkScalar fInnerRadius;
1776 SkScalar fOnAngle;
1777 SkScalar fTotalAngle;
1778 SkScalar fStartAngle;
1779 SkScalar fPhaseAngle;
1780 SkRect fDevBounds;
1781 };
1782
1783 SkMatrix fViewMatrixIfUsingLocalCoords;
1784 Helper fHelper;
1785 SkSTArray<1, Circle, true> fCircles;
1786 int fVertCount;
1787 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001788 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001789
Chris Daltoneb694b72020-03-16 09:25:50 -06001790 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001791 GrProgramInfo* fProgramInfo = nullptr;
1792
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001793 typedef GrMeshDrawOp INHERITED;
1794};
1795
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001796///////////////////////////////////////////////////////////////////////////////
1797
Brian Salomon05441c42017-05-15 16:45:49 -04001798class EllipseOp : public GrMeshDrawOp {
1799private:
1800 using Helper = GrSimpleMeshDrawOpHelper;
1801
1802 struct DeviceSpaceParams {
1803 SkPoint fCenter;
1804 SkScalar fXRadius;
1805 SkScalar fYRadius;
1806 SkScalar fInnerXRadius;
1807 SkScalar fInnerYRadius;
1808 };
1809
joshualitt76e7fb62015-02-11 08:52:27 -08001810public:
Brian Salomon25a88092016-12-01 09:36:50 -05001811 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001812
Robert Phillipsb97da532019-02-12 15:24:12 -05001813 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001814 GrPaint&& paint,
1815 const SkMatrix& viewMatrix,
1816 const SkRect& ellipse,
1817 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001818 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001819 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001820 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1821 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001822 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1823 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001824 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1825 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1826 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1827 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001828
bsalomon4b4a7cc2016-07-08 04:42:54 -07001829 // do (potentially) anisotropic mapping of stroke
1830 SkVector scaledStroke;
1831 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001832 scaledStroke.fX = SkScalarAbs(
1833 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1834 scaledStroke.fY = SkScalarAbs(
1835 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001836
1837 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001838 bool isStrokeOnly =
1839 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001840 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1841
Brian Salomon05441c42017-05-15 16:45:49 -04001842 params.fInnerXRadius = 0;
1843 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001844 if (hasStroke) {
1845 if (SkScalarNearlyZero(scaledStroke.length())) {
1846 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1847 } else {
1848 scaledStroke.scale(SK_ScalarHalf);
1849 }
1850
1851 // we only handle thick strokes for near-circular ellipses
1852 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001853 (0.5f * params.fXRadius > params.fYRadius ||
1854 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001855 return nullptr;
1856 }
1857
1858 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001859 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1860 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1861 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1862 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001863 return nullptr;
1864 }
1865
1866 // this is legit only if scale & translation (which should be the case at the moment)
1867 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001868 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1869 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001870 }
1871
Brian Salomon05441c42017-05-15 16:45:49 -04001872 params.fXRadius += scaledStroke.fX;
1873 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001874 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001875
1876 // For large ovals with low precision floats, we fall back to the path renderer.
1877 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1878 // minimum value to avoid divides by zero. With large ovals and low precision this
1879 // leads to blurring at the edge of the oval.
1880 const SkScalar kMaxOvalRadius = 16384;
1881 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1882 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1883 return nullptr;
1884 }
1885
Greg Daniel2655ede2019-04-10 00:49:28 +00001886 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001887 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001888 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001889
Brian Osmancf860852018-10-31 14:04:39 -04001890 EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001891 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001892 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001893 : INHERITED(ClassID())
1894 , fHelper(helperArgs, GrAAType::kCoverage)
1895 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001896 SkStrokeRec::Style style = stroke.getStyle();
1897 bool isStrokeOnly =
1898 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001899
Brian Salomon05441c42017-05-15 16:45:49 -04001900 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1901 params.fInnerXRadius, params.fInnerYRadius,
1902 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1903 params.fCenter.fY - params.fYRadius,
1904 params.fCenter.fX + params.fXRadius,
1905 params.fCenter.fY + params.fYRadius)});
1906
Greg Daniel5faf4742019-10-01 15:14:44 -04001907 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001908
bsalomon4b4a7cc2016-07-08 04:42:54 -07001909 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001910 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001911
Brian Salomon05441c42017-05-15 16:45:49 -04001912 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1913 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001914 }
joshualitt76e7fb62015-02-11 08:52:27 -08001915
Brian Salomon289e3d82016-12-14 15:52:56 -05001916 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001917
Chris Dalton1706cbf2019-05-21 19:35:29 -06001918 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001919 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001920 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001921 } else {
1922 fHelper.visitProxies(func);
1923 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001924 }
1925
Brian Osman9a390ac2018-11-12 09:47:48 -05001926#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001927 SkString dumpInfo() const override {
1928 SkString string;
1929 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001930 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001931 string.appendf(
1932 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1933 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001934 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001935 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
1936 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001937 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001938 string += fHelper.dumpInfo();
1939 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001940 return string;
1941 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001942#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001943
Chris Dalton6ce447a2019-06-23 18:07:38 -06001944 GrProcessorSet::Analysis finalize(
1945 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1946 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001947 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1948 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001949 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001950 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001951 GrProcessorAnalysisCoverage::kSingleChannel, color,
1952 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001953 }
1954
1955 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1956
bsalomone46f9fe2015-08-18 06:05:14 -07001957private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001958 GrProgramInfo* programInfo() override { return fProgramInfo; }
1959
Robert Phillips4133dc42020-03-11 15:55:55 -04001960 void onCreateProgramInfo(const GrCaps* caps,
1961 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001962 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001963 GrAppliedClip&& appliedClip,
1964 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001965 SkMatrix localMatrix;
1966 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001967 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001968 }
1969
Robert Phillips4490d922020-03-03 14:50:59 -05001970 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1971 fUseScale, localMatrix);
1972
Brian Salomon8afde5f2020-04-01 16:22:00 -04001973 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001974 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001975 }
1976
Robert Phillips4490d922020-03-03 14:50:59 -05001977 void onPrepareDraws(Target* target) override {
1978 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001979 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001980 if (!fProgramInfo) {
1981 return;
1982 }
1983 }
1984
1985 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001986 GrVertexWriter verts{helper.vertices()};
1987 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001988 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001989 return;
1990 }
1991
Brian Salomon05441c42017-05-15 16:45:49 -04001992 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001993 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001994 SkScalar xRadius = ellipse.fXRadius;
1995 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001996
1997 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001998 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1999 SkScalarInvert(xRadius),
2000 SkScalarInvert(yRadius),
2001 SkScalarInvert(ellipse.fInnerXRadius),
2002 SkScalarInvert(ellipse.fInnerYRadius)
2003 };
Greg Daniel2655ede2019-04-10 00:49:28 +00002004 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
2005 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07002006
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002007 if (!fStroked) {
2008 // For filled ellipses we map a unit circle in the vertex attributes rather than
2009 // computing an ellipse and modifying that distance, so we normalize to 1
2010 xMaxOffset /= xRadius;
2011 yMaxOffset /= yRadius;
2012 }
2013
joshualitt76e7fb62015-02-11 08:52:27 -08002014 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002015 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2016 color,
2017 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002018 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002019 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002020 }
Robert Phillips4490d922020-03-03 14:50:59 -05002021 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002022 }
2023
2024 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002025 if (!fProgramInfo || !fMesh) {
2026 return;
2027 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002028
Chris Dalton765ed362020-03-16 17:34:44 -06002029 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2030 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2031 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002032 }
2033
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002034 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2035 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002036 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002037
Brian Salomon05441c42017-05-15 16:45:49 -04002038 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002039 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002040 }
2041
bsalomoncdaa97b2016-03-08 08:30:14 -08002042 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002043 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002044 }
2045
Brian Salomon05441c42017-05-15 16:45:49 -04002046 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002047 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2048 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002049 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002050 }
2051
Brian Salomon05441c42017-05-15 16:45:49 -04002052 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002053 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002054 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002055 }
2056
Brian Salomon05441c42017-05-15 16:45:49 -04002057 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002058 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002059 SkScalar fXRadius;
2060 SkScalar fYRadius;
2061 SkScalar fInnerXRadius;
2062 SkScalar fInnerYRadius;
2063 SkRect fDevBounds;
2064 };
joshualitt76e7fb62015-02-11 08:52:27 -08002065
Brian Salomon289e3d82016-12-14 15:52:56 -05002066 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002067 Helper fHelper;
2068 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002069 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002070 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002071 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002072
Chris Daltoneb694b72020-03-16 09:25:50 -06002073 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002074 GrProgramInfo* fProgramInfo = nullptr;
2075
Brian Salomon05441c42017-05-15 16:45:49 -04002076 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002077};
2078
joshualitt76e7fb62015-02-11 08:52:27 -08002079/////////////////////////////////////////////////////////////////////////////////////////////////
2080
Brian Salomon05441c42017-05-15 16:45:49 -04002081class DIEllipseOp : public GrMeshDrawOp {
2082private:
2083 using Helper = GrSimpleMeshDrawOpHelper;
2084
2085 struct DeviceSpaceParams {
2086 SkPoint fCenter;
2087 SkScalar fXRadius;
2088 SkScalar fYRadius;
2089 SkScalar fInnerXRadius;
2090 SkScalar fInnerYRadius;
2091 DIEllipseStyle fStyle;
2092 };
2093
joshualitt76e7fb62015-02-11 08:52:27 -08002094public:
Brian Salomon25a88092016-12-01 09:36:50 -05002095 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002096
Robert Phillipsb97da532019-02-12 15:24:12 -05002097 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002098 GrPaint&& paint,
2099 const SkMatrix& viewMatrix,
2100 const SkRect& ellipse,
2101 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002102 DeviceSpaceParams params;
2103 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2104 params.fXRadius = SkScalarHalf(ellipse.width());
2105 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002106
bsalomon4b4a7cc2016-07-08 04:42:54 -07002107 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002108 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2109 ? DIEllipseStyle::kStroke
2110 : (SkStrokeRec::kHairline_Style == style)
2111 ? DIEllipseStyle::kHairline
2112 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002113
Brian Salomon05441c42017-05-15 16:45:49 -04002114 params.fInnerXRadius = 0;
2115 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002116 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2117 SkScalar strokeWidth = stroke.getWidth();
2118
2119 if (SkScalarNearlyZero(strokeWidth)) {
2120 strokeWidth = SK_ScalarHalf;
2121 } else {
2122 strokeWidth *= SK_ScalarHalf;
2123 }
2124
2125 // we only handle thick strokes for near-circular ellipses
2126 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002127 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2128 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002129 return nullptr;
2130 }
2131
2132 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002133 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2134 (strokeWidth * strokeWidth) * params.fXRadius) {
2135 return nullptr;
2136 }
2137 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2138 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002139 return nullptr;
2140 }
2141
2142 // set inner radius (if needed)
2143 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002144 params.fInnerXRadius = params.fXRadius - strokeWidth;
2145 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002146 }
2147
Brian Salomon05441c42017-05-15 16:45:49 -04002148 params.fXRadius += strokeWidth;
2149 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002150 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002151
2152 // For large ovals with low precision floats, we fall back to the path renderer.
2153 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2154 // minimum value to avoid divides by zero. With large ovals and low precision this
2155 // leads to blurring at the edge of the oval.
2156 const SkScalar kMaxOvalRadius = 16384;
2157 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2158 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2159 return nullptr;
2160 }
2161
Brian Salomon05441c42017-05-15 16:45:49 -04002162 if (DIEllipseStyle::kStroke == params.fStyle &&
2163 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2164 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002165 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002166 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002167 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002168
Greg Daniel2655ede2019-04-10 00:49:28 +00002169 DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002170 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002171 : INHERITED(ClassID())
2172 , fHelper(helperArgs, GrAAType::kCoverage)
2173 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002174 // This expands the outer rect so that after CTM we end up with a half-pixel border
2175 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2176 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2177 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2178 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2179 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2180 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002181
Brian Salomon05441c42017-05-15 16:45:49 -04002182 fEllipses.emplace_back(
2183 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2184 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2185 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2186 params.fCenter.fY - params.fYRadius - geoDy,
2187 params.fCenter.fX + params.fXRadius + geoDx,
2188 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002189 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002190 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002191 }
2192
Brian Salomon289e3d82016-12-14 15:52:56 -05002193 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002194
Chris Dalton1706cbf2019-05-21 19:35:29 -06002195 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002196 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002197 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002198 } else {
2199 fHelper.visitProxies(func);
2200 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002201 }
2202
Brian Osman9a390ac2018-11-12 09:47:48 -05002203#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002204 SkString dumpInfo() const override {
2205 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002206 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002207 string.appendf(
2208 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2209 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2210 "GeoDY: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002211 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002212 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2213 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002214 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002215 string += fHelper.dumpInfo();
2216 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002217 return string;
2218 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002219#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002220
Chris Dalton6ce447a2019-06-23 18:07:38 -06002221 GrProcessorSet::Analysis finalize(
2222 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2223 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002224 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2225 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002226 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002227 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002228 GrProcessorAnalysisCoverage::kSingleChannel, color,
2229 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002230 }
2231
2232 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2233
bsalomone46f9fe2015-08-18 06:05:14 -07002234private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002235 GrProgramInfo* programInfo() override { return fProgramInfo; }
2236
Robert Phillips4133dc42020-03-11 15:55:55 -04002237 void onCreateProgramInfo(const GrCaps* caps,
2238 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002239 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002240 GrAppliedClip&& appliedClip,
2241 const GrXferProcessor::DstProxyView& dstProxyView) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002242 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2243 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002244 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002245
Brian Salomon8afde5f2020-04-01 16:22:00 -04002246 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002247 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002248 }
2249
Robert Phillips4490d922020-03-03 14:50:59 -05002250 void onPrepareDraws(Target* target) override {
2251 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002252 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002253 }
2254
2255 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002256 GrVertexWriter verts{helper.vertices()};
2257 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002258 return;
2259 }
2260
Brian Salomon05441c42017-05-15 16:45:49 -04002261 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002262 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002263 SkScalar xRadius = ellipse.fXRadius;
2264 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002265
joshualitt76e7fb62015-02-11 08:52:27 -08002266 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002267 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2268 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002269
Brian Osman9d958b52018-11-13 12:46:56 -05002270 // By default, constructed so that inner offset is (0, 0) for all points
2271 SkScalar innerRatioX = -offsetDx;
2272 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002273
Brian Osman9d958b52018-11-13 12:46:56 -05002274 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002275 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002276 innerRatioX = xRadius / ellipse.fInnerXRadius;
2277 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002278 }
joshualitt76e7fb62015-02-11 08:52:27 -08002279
Brian Osman2b6e3902018-11-21 15:29:43 -05002280 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2281 color,
2282 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002283 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002284 origin_centered_tri_strip(innerRatioX + offsetDx,
2285 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002286 }
Robert Phillips4490d922020-03-03 14:50:59 -05002287 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002288 }
2289
2290 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002291 if (!fProgramInfo || !fMesh) {
2292 return;
2293 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002294
Chris Dalton765ed362020-03-16 17:34:44 -06002295 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2296 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2297 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002298 }
halcanary9d524f22016-03-29 09:03:52 -07002299
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002300 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2301 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002302 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002303 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002304 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002305 }
2306
bsalomoncdaa97b2016-03-08 08:30:14 -08002307 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002308 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002309 }
2310
joshualittd96a67b2015-05-05 14:09:05 -07002311 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002312 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002313 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002314 }
2315
Brian Salomon05441c42017-05-15 16:45:49 -04002316 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002317 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002318 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002319 }
2320
Brian Salomon05441c42017-05-15 16:45:49 -04002321 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2322 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002323
Brian Salomon05441c42017-05-15 16:45:49 -04002324 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002325 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002326 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002327 SkScalar fXRadius;
2328 SkScalar fYRadius;
2329 SkScalar fInnerXRadius;
2330 SkScalar fInnerYRadius;
2331 SkScalar fGeoDx;
2332 SkScalar fGeoDy;
2333 DIEllipseStyle fStyle;
2334 SkRect fBounds;
2335 };
2336
Brian Salomon05441c42017-05-15 16:45:49 -04002337 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002338 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002339 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002340 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002341
Chris Daltoneb694b72020-03-16 09:25:50 -06002342 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002343 GrProgramInfo* fProgramInfo = nullptr;
2344
Brian Salomon05441c42017-05-15 16:45:49 -04002345 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002346};
2347
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002348///////////////////////////////////////////////////////////////////////////////
2349
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002350// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002351//
2352// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2353// ____________
2354// |_|________|_|
2355// | | | |
2356// | | | |
2357// | | | |
2358// |_|________|_|
2359// |_|________|_|
2360//
2361// For strokes, we don't draw the center quad.
2362//
2363// For circular roundrects, in the case where the stroke width is greater than twice
2364// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002365// in the center. The shared vertices are duplicated so we can set a different outer radius
2366// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002367// ____________
2368// |_|________|_|
2369// | |\ ____ /| |
2370// | | | | | |
2371// | | |____| | |
2372// |_|/______\|_|
2373// |_|________|_|
2374//
2375// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002376//
2377// For filled rrects that need to provide a distance vector we resuse the overstroke
2378// geometry but make the inner rect degenerate (either a point or a horizontal or
2379// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002380
jvanverth84839f62016-08-29 10:16:40 -07002381static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002382 // clang-format off
2383 // overstroke quads
2384 // we place this at the beginning so that we can skip these indices when rendering normally
2385 16, 17, 19, 16, 19, 18,
2386 19, 17, 23, 19, 23, 21,
2387 21, 23, 22, 21, 22, 20,
2388 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002389
Brian Salomon289e3d82016-12-14 15:52:56 -05002390 // corners
2391 0, 1, 5, 0, 5, 4,
2392 2, 3, 7, 2, 7, 6,
2393 8, 9, 13, 8, 13, 12,
2394 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002395
Brian Salomon289e3d82016-12-14 15:52:56 -05002396 // edges
2397 1, 2, 6, 1, 6, 5,
2398 4, 5, 9, 4, 9, 8,
2399 6, 7, 11, 6, 11, 10,
2400 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002401
Brian Salomon289e3d82016-12-14 15:52:56 -05002402 // center
2403 // we place this at the end so that we can ignore these indices when not rendering as filled
2404 5, 6, 10, 5, 10, 9,
2405 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002406};
Brian Salomon289e3d82016-12-14 15:52:56 -05002407
jvanverth84839f62016-08-29 10:16:40 -07002408// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002409static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002410
jvanverth84839f62016-08-29 10:16:40 -07002411// overstroke count is arraysize minus the center indices
2412static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2413// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002414static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002415// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002416static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2417static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002418static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002419
jvanverthc3d0e422016-08-25 08:12:35 -07002420enum RRectType {
2421 kFill_RRectType,
2422 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002423 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002424};
2425
jvanverth84839f62016-08-29 10:16:40 -07002426static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002427 switch (type) {
2428 case kFill_RRectType:
2429 case kStroke_RRectType:
2430 return kVertsPerStandardRRect;
2431 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002432 return kVertsPerOverstrokeRRect;
2433 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002434 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002435}
2436
2437static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002438 switch (type) {
2439 case kFill_RRectType:
2440 return kIndicesPerFillRRect;
2441 case kStroke_RRectType:
2442 return kIndicesPerStrokeRRect;
2443 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002444 return kIndicesPerOverstrokeRRect;
2445 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002446 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002447}
2448
2449static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002450 switch (type) {
2451 case kFill_RRectType:
2452 case kStroke_RRectType:
2453 return gStandardRRectIndices;
2454 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002455 return gOverstrokeRRectIndices;
2456 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002457 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002458}
2459
joshualitt76e7fb62015-02-11 08:52:27 -08002460///////////////////////////////////////////////////////////////////////////////////////////////////
2461
Robert Phillips79839d42016-10-06 15:03:34 -04002462// For distance computations in the interior of filled rrects we:
2463//
2464// add a interior degenerate (point or line) rect
2465// each vertex of that rect gets -outerRad as its radius
2466// this makes the computation of the distance to the outer edge be negative
2467// negative values are caught and then handled differently in the GP's onEmitCode
2468// each vertex is also given the normalized x & y distance from the interior rect's edge
2469// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2470
Brian Salomon05441c42017-05-15 16:45:49 -04002471class CircularRRectOp : public GrMeshDrawOp {
2472private:
2473 using Helper = GrSimpleMeshDrawOpHelper;
2474
joshualitt76e7fb62015-02-11 08:52:27 -08002475public:
Brian Salomon25a88092016-12-01 09:36:50 -05002476 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002477
bsalomon4b4a7cc2016-07-08 04:42:54 -07002478 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2479 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002480 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002481 GrPaint&& paint,
2482 const SkMatrix& viewMatrix,
2483 const SkRect& devRect,
2484 float devRadius,
2485 float devStrokeWidth,
2486 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002487 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002488 devRect, devRadius,
2489 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002490 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002491 CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002492 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2493 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002494 : INHERITED(ClassID())
2495 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Greg Daniel2655ede2019-04-10 00:49:28 +00002496 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002497 SkRect bounds = devRect;
2498 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2499 SkScalar innerRadius = 0.0f;
2500 SkScalar outerRadius = devRadius;
2501 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002502 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002503 if (devStrokeWidth > 0) {
2504 if (SkScalarNearlyZero(devStrokeWidth)) {
2505 halfWidth = SK_ScalarHalf;
2506 } else {
2507 halfWidth = SkScalarHalf(devStrokeWidth);
2508 }
joshualitt76e7fb62015-02-11 08:52:27 -08002509
bsalomon4b4a7cc2016-07-08 04:42:54 -07002510 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002511 // Outset stroke by 1/4 pixel
2512 devStrokeWidth += 0.25f;
2513 // If stroke is greater than width or height, this is still a fill
2514 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002515 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002516 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002517 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002518 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002519 }
2520 outerRadius += halfWidth;
2521 bounds.outset(halfWidth, halfWidth);
2522 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002523
Greg Daniel2655ede2019-04-10 00:49:28 +00002524 // The radii are outset for two reasons. First, it allows the shader to simply perform
2525 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2526 // Second, the outer radius is used to compute the verts of the bounding box that is
2527 // rendered and the outset ensures the box will cover all partially covered by the rrect
2528 // corners.
2529 outerRadius += SK_ScalarHalf;
2530 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002531
Greg Daniel5faf4742019-10-01 15:14:44 -04002532 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002533
Greg Daniel2655ede2019-04-10 00:49:28 +00002534 // Expand the rect for aa to generate correct vertices.
2535 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002536
Brian Salomon05441c42017-05-15 16:45:49 -04002537 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002538 fVertCount = rrect_type_to_vert_count(type);
2539 fIndexCount = rrect_type_to_index_count(type);
2540 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002541 }
2542
Brian Salomon289e3d82016-12-14 15:52:56 -05002543 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002544
Chris Dalton1706cbf2019-05-21 19:35:29 -06002545 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002546 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002547 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002548 } else {
2549 fHelper.visitProxies(func);
2550 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002551 }
2552
Brian Osman9a390ac2018-11-12 09:47:48 -05002553#ifdef SK_DEBUG
jvanverthc3d0e422016-08-25 08:12:35 -07002554 SkString dumpInfo() const override {
2555 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002556 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002557 string.appendf(
2558 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2559 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002560 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002561 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2562 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2563 fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002564 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002565 string += fHelper.dumpInfo();
2566 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002567 return string;
2568 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002569#endif
jvanverthc3d0e422016-08-25 08:12:35 -07002570
Chris Dalton6ce447a2019-06-23 18:07:38 -06002571 GrProcessorSet::Analysis finalize(
2572 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2573 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002574 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002575 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002576 GrProcessorAnalysisCoverage::kSingleChannel, color,
2577 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002578 }
2579
2580 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2581
Brian Salomon92aee3d2016-12-21 09:20:25 -05002582private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002583 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002584 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002585 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002586 SkASSERT(smInset < bigInset);
2587
2588 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002589 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2590 color,
2591 xOffset, 0.0f,
2592 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002593
2594 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002595 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2596 color,
2597 xOffset, 0.0f,
2598 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002599
Brian Osmana1d4eb92018-12-06 16:33:10 -05002600 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2601 color,
2602 0.0f, 0.0f,
2603 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002604
Brian Osmana1d4eb92018-12-06 16:33:10 -05002605 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2606 color,
2607 0.0f, 0.0f,
2608 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002609
Brian Osmana1d4eb92018-12-06 16:33:10 -05002610 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2611 color,
2612 0.0f, 0.0f,
2613 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002614
Brian Osmana1d4eb92018-12-06 16:33:10 -05002615 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2616 color,
2617 0.0f, 0.0f,
2618 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002619
2620 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002621 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2622 color,
2623 xOffset, 0.0f,
2624 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002625
2626 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002627 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2628 color,
2629 xOffset, 0.0f,
2630 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002631 }
2632
Robert Phillips2669a7b2020-03-12 12:07:19 -04002633 GrProgramInfo* programInfo() override { return fProgramInfo; }
2634
Robert Phillips4133dc42020-03-11 15:55:55 -04002635 void onCreateProgramInfo(const GrCaps* caps,
2636 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002637 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002638 GrAppliedClip&& appliedClip,
2639 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002640 // Invert the view matrix as a local matrix (if any other processors require coords).
2641 SkMatrix localMatrix;
2642 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002643 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002644 }
2645
Robert Phillips4490d922020-03-03 14:50:59 -05002646 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002647 false, false, false, false,
2648 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002649
Brian Salomon8afde5f2020-04-01 16:22:00 -04002650 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002651 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002652 }
2653
Robert Phillips4490d922020-03-03 14:50:59 -05002654 void onPrepareDraws(Target* target) override {
2655 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002656 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002657 if (!fProgramInfo) {
2658 return;
2659 }
2660 }
2661
Brian Salomon12d22642019-01-29 14:38:50 -05002662 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002663 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002664
Robert Phillips4490d922020-03-03 14:50:59 -05002665 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2666 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002667 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002668 SkDebugf("Could not allocate vertices\n");
2669 return;
2670 }
2671
Brian Salomon12d22642019-01-29 14:38:50 -05002672 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002673 int firstIndex = 0;
2674 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2675 if (!indices) {
2676 SkDebugf("Could not allocate indices\n");
2677 return;
2678 }
2679
2680 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002681 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002682 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002683 SkScalar outerRadius = rrect.fOuterRadius;
2684 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002685
Brian Salomon289e3d82016-12-14 15:52:56 -05002686 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2687 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002688
Brian Salomon289e3d82016-12-14 15:52:56 -05002689 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002690 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002691 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002692 SkScalar innerRadius = rrect.fType != kFill_RRectType
2693 ? rrect.fInnerRadius / rrect.fOuterRadius
2694 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002695 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002696 verts.write(bounds.fLeft, yCoords[i],
2697 color,
2698 -1.0f, yOuterRadii[i],
2699 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002700
Brian Osmana1d4eb92018-12-06 16:33:10 -05002701 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2702 color,
2703 0.0f, yOuterRadii[i],
2704 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002705
Brian Osmana1d4eb92018-12-06 16:33:10 -05002706 verts.write(bounds.fRight - outerRadius, yCoords[i],
2707 color,
2708 0.0f, yOuterRadii[i],
2709 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002710
Brian Osmana1d4eb92018-12-06 16:33:10 -05002711 verts.write(bounds.fRight, yCoords[i],
2712 color,
2713 1.0f, yOuterRadii[i],
2714 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002715 }
jvanverthc3d0e422016-08-25 08:12:35 -07002716 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002717 // Effectively this is an additional stroked rrect, with its
2718 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2719 // This will give us correct AA in the center and the correct
2720 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002721 //
jvanvertha4f1af82016-08-29 07:17:47 -07002722 // Also, the outer offset is a constant vector pointing to the right, which
2723 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002724 if (kOverstroke_RRectType == rrect.fType) {
2725 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002726
Brian Salomon05441c42017-05-15 16:45:49 -04002727 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002728 // this is the normalized distance from the outer rectangle of this
2729 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002730 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002731
Brian Osmana1d4eb92018-12-06 16:33:10 -05002732 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002733 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002734 }
jvanverth6a397612016-08-26 08:15:33 -07002735
Brian Salomon05441c42017-05-15 16:45:49 -04002736 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2737 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002738 for (int i = 0; i < primIndexCount; ++i) {
2739 *indices++ = primIndices[i] + currStartVertex;
2740 }
2741
Brian Salomon05441c42017-05-15 16:45:49 -04002742 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002743 }
2744
Robert Phillips4490d922020-03-03 14:50:59 -05002745 fMesh = target->allocMesh();
2746 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002747 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002748 }
2749
2750 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002751 if (!fProgramInfo || !fMesh) {
2752 return;
2753 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002754
Chris Dalton765ed362020-03-16 17:34:44 -06002755 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2756 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2757 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002758 }
2759
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002760 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2761 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002762 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002763
2764 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002765 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002766 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002767 }
2768
Brian Salomon05441c42017-05-15 16:45:49 -04002769 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002770 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002771 }
2772
Brian Salomon05441c42017-05-15 16:45:49 -04002773 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002774 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2775 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002776 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002777 }
2778
Brian Salomon05441c42017-05-15 16:45:49 -04002779 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002780 fVertCount += that->fVertCount;
2781 fIndexCount += that->fIndexCount;
2782 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002783 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002784 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002785 }
2786
Brian Salomon05441c42017-05-15 16:45:49 -04002787 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002788 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002789 SkScalar fInnerRadius;
2790 SkScalar fOuterRadius;
2791 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002792 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002793 };
2794
Brian Salomon289e3d82016-12-14 15:52:56 -05002795 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002796 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002797 int fVertCount;
2798 int fIndexCount;
2799 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002800 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002801 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002802
Chris Daltoneb694b72020-03-16 09:25:50 -06002803 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002804 GrProgramInfo* fProgramInfo = nullptr;
2805
Brian Salomon05441c42017-05-15 16:45:49 -04002806 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002807};
2808
jvanverth84839f62016-08-29 10:16:40 -07002809static const int kNumRRectsInIndexBuffer = 256;
2810
2811GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2812GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002813static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2814 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002815 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2816 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2817 switch (type) {
2818 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002819 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002820 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2821 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002822 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002823 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002824 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2825 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002826 default:
2827 SkASSERT(false);
2828 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002829 }
jvanverth84839f62016-08-29 10:16:40 -07002830}
2831
Brian Salomon05441c42017-05-15 16:45:49 -04002832class EllipticalRRectOp : public GrMeshDrawOp {
2833private:
2834 using Helper = GrSimpleMeshDrawOpHelper;
2835
joshualitt76e7fb62015-02-11 08:52:27 -08002836public:
Brian Salomon25a88092016-12-01 09:36:50 -05002837 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002838
bsalomon4b4a7cc2016-07-08 04:42:54 -07002839 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2840 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002841 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002842 GrPaint&& paint,
2843 const SkMatrix& viewMatrix,
2844 const SkRect& devRect,
2845 float devXRadius,
2846 float devYRadius,
2847 SkVector devStrokeWidths,
2848 bool strokeOnly) {
Brian Osman9fb7fa52019-07-01 16:48:39 -04002849 SkASSERT(devXRadius >= 0.5);
2850 SkASSERT(devYRadius >= 0.5);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002851 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2852 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002853 if (devStrokeWidths.fX > 0) {
2854 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2855 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2856 } else {
2857 devStrokeWidths.scale(SK_ScalarHalf);
2858 }
joshualitt76e7fb62015-02-11 08:52:27 -08002859
bsalomon4b4a7cc2016-07-08 04:42:54 -07002860 // we only handle thick strokes for near-circular ellipses
2861 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002862 (SK_ScalarHalf * devXRadius > devYRadius ||
2863 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002864 return nullptr;
2865 }
2866
2867 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002868 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2869 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002870 return nullptr;
2871 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002872 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2873 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002874 return nullptr;
2875 }
Brian Salomon05441c42017-05-15 16:45:49 -04002876 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002877 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002878 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002879 devXRadius, devYRadius, devStrokeWidths,
2880 strokeOnly);
2881 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002882
Greg Daniel2655ede2019-04-10 00:49:28 +00002883 EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002884 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2885 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002886 : INHERITED(ClassID())
2887 , fHelper(helperArgs, GrAAType::kCoverage)
2888 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002889 SkScalar innerXRadius = 0.0f;
2890 SkScalar innerYRadius = 0.0f;
2891 SkRect bounds = devRect;
2892 bool stroked = false;
2893 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002894 // this is legit only if scale & translation (which should be the case at the moment)
2895 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002896 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2897 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002898 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2899 }
2900
Brian Salomon05441c42017-05-15 16:45:49 -04002901 devXRadius += devStrokeHalfWidths.fX;
2902 devYRadius += devStrokeHalfWidths.fY;
2903 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002904 }
2905
Brian Salomon05441c42017-05-15 16:45:49 -04002906 fStroked = stroked;
2907 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002908 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002909 // Expand the rect for aa in order to generate the correct vertices.
2910 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002911 fRRects.emplace_back(
2912 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002913 }
2914
Brian Salomon289e3d82016-12-14 15:52:56 -05002915 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002916
Chris Dalton1706cbf2019-05-21 19:35:29 -06002917 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002918 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002919 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002920 } else {
2921 fHelper.visitProxies(func);
2922 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002923 }
2924
Brian Osman9a390ac2018-11-12 09:47:48 -05002925#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002926 SkString dumpInfo() const override {
2927 SkString string;
2928 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002929 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002930 string.appendf(
2931 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2932 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002933 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002934 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2935 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002936 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002937 string += fHelper.dumpInfo();
2938 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002939 return string;
2940 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002941#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002942
Chris Dalton6ce447a2019-06-23 18:07:38 -06002943 GrProcessorSet::Analysis finalize(
2944 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2945 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002946 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002947 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002948 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002949 GrProcessorAnalysisCoverage::kSingleChannel, color,
2950 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002951 }
2952
2953 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2954
bsalomone46f9fe2015-08-18 06:05:14 -07002955private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002956 GrProgramInfo* programInfo() override { return fProgramInfo; }
2957
Robert Phillips4133dc42020-03-11 15:55:55 -04002958 void onCreateProgramInfo(const GrCaps* caps,
2959 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002960 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002961 GrAppliedClip&& appliedClip,
2962 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002963 SkMatrix localMatrix;
2964 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002965 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002966 }
2967
Robert Phillips4490d922020-03-03 14:50:59 -05002968 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2969 fUseScale, localMatrix);
2970
Brian Salomon8afde5f2020-04-01 16:22:00 -04002971 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002972 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002973 }
2974
Robert Phillips4490d922020-03-03 14:50:59 -05002975 void onPrepareDraws(Target* target) override {
2976 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002977 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002978 if (!fProgramInfo) {
2979 return;
2980 }
2981 }
joshualitt76e7fb62015-02-11 08:52:27 -08002982
bsalomonb5238a72015-05-05 07:49:49 -07002983 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002984 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002985 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2986 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002987
Brian Salomon12d22642019-01-29 14:38:50 -05002988 if (!indexBuffer) {
2989 SkDebugf("Could not allocate indices\n");
2990 return;
2991 }
Robert Phillips4490d922020-03-03 14:50:59 -05002992 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2993 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002994 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002995 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002996 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002997 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002998 SkDebugf("Could not allocate vertices\n");
2999 return;
3000 }
3001
Brian Salomon05441c42017-05-15 16:45:49 -04003002 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003003 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003004 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003005 float reciprocalRadii[4] = {
3006 SkScalarInvert(rrect.fXRadius),
3007 SkScalarInvert(rrect.fYRadius),
3008 SkScalarInvert(rrect.fInnerXRadius),
3009 SkScalarInvert(rrect.fInnerYRadius)
3010 };
joshualitt76e7fb62015-02-11 08:52:27 -08003011
3012 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003013 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3014 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003015
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003016 SkScalar xMaxOffset = xOuterRadius;
3017 SkScalar yMaxOffset = yOuterRadius;
3018 if (!fStroked) {
3019 // For filled rrects we map a unit circle in the vertex attributes rather than
3020 // computing an ellipse and modifying that distance, so we normalize to 1.
3021 xMaxOffset /= rrect.fXRadius;
3022 yMaxOffset /= rrect.fYRadius;
3023 }
3024
Brian Salomon05441c42017-05-15 16:45:49 -04003025 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003026
Brian Salomon289e3d82016-12-14 15:52:56 -05003027 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3028 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003029 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003030 SK_ScalarNearlyZero, // we're using inversesqrt() in
3031 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003032 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003033
Brian Osman788b9162020-02-07 10:36:46 -05003034 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003035 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003036 verts.write(bounds.fLeft, yCoords[i],
3037 color,
3038 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003039 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003040 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003041
Brian Osmana1d4eb92018-12-06 16:33:10 -05003042 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3043 color,
3044 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003045 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003046 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003047
Brian Osmana1d4eb92018-12-06 16:33:10 -05003048 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3049 color,
3050 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003051 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003052 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003053
Brian Osmana1d4eb92018-12-06 16:33:10 -05003054 verts.write(bounds.fRight, yCoords[i],
3055 color,
3056 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003057 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003058 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003059 }
3060 }
Robert Phillips4490d922020-03-03 14:50:59 -05003061 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003062 }
3063
3064 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003065 if (!fProgramInfo || !fMesh) {
3066 return;
3067 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003068
Chris Dalton765ed362020-03-16 17:34:44 -06003069 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3070 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3071 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003072 }
3073
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05003074 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
3075 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003076 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003077
Brian Salomon05441c42017-05-15 16:45:49 -04003078 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003079 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003080 }
3081
bsalomoncdaa97b2016-03-08 08:30:14 -08003082 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003083 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003084 }
3085
Brian Salomon05441c42017-05-15 16:45:49 -04003086 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003087 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3088 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003089 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003090 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003091
Brian Salomon05441c42017-05-15 16:45:49 -04003092 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003093 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003094 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003095 }
3096
Brian Salomon05441c42017-05-15 16:45:49 -04003097 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003098 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003099 SkScalar fXRadius;
3100 SkScalar fYRadius;
3101 SkScalar fInnerXRadius;
3102 SkScalar fInnerYRadius;
3103 SkRect fDevBounds;
3104 };
3105
Brian Salomon289e3d82016-12-14 15:52:56 -05003106 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003107 Helper fHelper;
3108 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003109 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003110 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003111 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003112
Chris Daltoneb694b72020-03-16 09:25:50 -06003113 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003114 GrProgramInfo* fProgramInfo = nullptr;
3115
Brian Salomon05441c42017-05-15 16:45:49 -04003116 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08003117};
3118
Jim Van Verth64b85892019-06-17 12:01:46 -04003119std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3120 GrPaint&& paint,
3121 const SkMatrix& viewMatrix,
3122 const SkRRect& rrect,
3123 const SkStrokeRec& stroke,
3124 const GrShaderCaps* shaderCaps) {
3125 SkASSERT(viewMatrix.rectStaysRect());
3126 SkASSERT(viewMatrix.isSimilarity());
3127 SkASSERT(rrect.isSimple());
3128 SkASSERT(!rrect.isOval());
3129 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3130
3131 // RRect ops only handle simple, but not too simple, rrects.
3132 // Do any matrix crunching before we reset the draw state for device coords.
3133 const SkRect& rrectBounds = rrect.getBounds();
3134 SkRect bounds;
3135 viewMatrix.mapRect(&bounds, rrectBounds);
3136
3137 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3138 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3139 viewMatrix[SkMatrix::kMSkewY]));
3140
3141 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3142 SkScalar scaledStroke = -1;
3143 SkScalar strokeWidth = stroke.getWidth();
3144 SkStrokeRec::Style style = stroke.getStyle();
3145
3146 bool isStrokeOnly =
3147 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3148 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3149
3150 if (hasStroke) {
3151 if (SkStrokeRec::kHairline_Style == style) {
3152 scaledStroke = SK_Scalar1;
3153 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003154 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3155 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003156 }
3157 }
3158
3159 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3160 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3161 // patch will have fractional coverage. This only matters when the interior is actually filled.
3162 // We could consider falling back to rect rendering here, since a tiny radius is
3163 // indistinguishable from a square corner.
3164 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3165 return nullptr;
3166 }
3167
3168 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3169 scaledStroke, isStrokeOnly);
3170}
3171
Robert Phillipsb97da532019-02-12 15:24:12 -05003172static std::unique_ptr<GrDrawOp> make_rrect_op(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003173 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04003174 const SkMatrix& viewMatrix,
3175 const SkRRect& rrect,
3176 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003177 SkASSERT(viewMatrix.rectStaysRect());
3178 SkASSERT(rrect.isSimple());
3179 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003180
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003181 // RRect ops only handle simple, but not too simple, rrects.
3182 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003183 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003184 SkRect bounds;
3185 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003186
Mike Reed242135a2018-02-22 13:41:39 -05003187 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003188 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3189 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3190 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3191 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003192
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003193 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003194
bsalomon4b4a7cc2016-07-08 04:42:54 -07003195 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3196 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003197 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003198
Brian Salomon289e3d82016-12-14 15:52:56 -05003199 bool isStrokeOnly =
3200 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003201 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3202
3203 if (hasStroke) {
3204 if (SkStrokeRec::kHairline_Style == style) {
3205 scaledStroke.set(1, 1);
3206 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003207 scaledStroke.fX = SkScalarAbs(
3208 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3209 scaledStroke.fY = SkScalarAbs(
3210 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003211 }
3212
Jim Van Verth64b85892019-06-17 12:01:46 -04003213 // if half of strokewidth is greater than radius, we don't handle that right now
3214 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3215 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003216 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003217 }
3218 }
3219
Brian Salomon8a97f562019-04-18 14:07:27 -04003220 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003221 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003222 std::swap(xRadius, yRadius);
3223 std::swap(scaledStroke.fX, scaledStroke.fY);
3224 }
3225
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003226 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3227 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3228 // patch will have fractional coverage. This only matters when the interior is actually filled.
3229 // We could consider falling back to rect rendering here, since a tiny radius is
3230 // indistinguishable from a square corner.
3231 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003232 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003233 }
3234
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003235 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003236 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3237 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003238}
3239
Robert Phillipsb97da532019-02-12 15:24:12 -05003240std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003241 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003242 const SkMatrix& viewMatrix,
3243 const SkRRect& rrect,
3244 const SkStrokeRec& stroke,
3245 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003246 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003247 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003248 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003249 }
3250
3251 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003252 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003253 }
3254
Greg Daniel2655ede2019-04-10 00:49:28 +00003255 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003256}
joshualitt3e708c52015-04-30 13:49:27 -07003257
bsalomon4b4a7cc2016-07-08 04:42:54 -07003258///////////////////////////////////////////////////////////////////////////////
3259
Jim Van Verth64b85892019-06-17 12:01:46 -04003260std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3261 GrPaint&& paint,
3262 const SkMatrix& viewMatrix,
3263 const SkRect& oval,
3264 const GrStyle& style,
3265 const GrShaderCaps* shaderCaps) {
3266 SkScalar width = oval.width();
3267 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3268 circle_stays_circle(viewMatrix));
3269
3270 auto r = width / 2.f;
3271 SkPoint center = { oval.centerX(), oval.centerY() };
3272 if (style.hasNonDashPathEffect()) {
3273 return nullptr;
3274 } else if (style.isDashed()) {
3275 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3276 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3277 return nullptr;
3278 }
3279 auto onInterval = style.dashIntervals()[0];
3280 auto offInterval = style.dashIntervals()[1];
3281 if (offInterval == 0) {
3282 GrStyle strokeStyle(style.strokeRec(), nullptr);
3283 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3284 strokeStyle, shaderCaps);
3285 } else if (onInterval == 0) {
3286 // There is nothing to draw but we have no way to indicate that here.
3287 return nullptr;
3288 }
3289 auto angularOnInterval = onInterval / r;
3290 auto angularOffInterval = offInterval / r;
3291 auto phaseAngle = style.dashPhase() / r;
3292 // Currently this function doesn't accept ovals with different start angles, though
3293 // it could.
3294 static const SkScalar kStartAngle = 0.f;
3295 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3296 style.strokeRec().getWidth(), kStartAngle,
3297 angularOnInterval, angularOffInterval, phaseAngle);
3298 }
3299 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3300}
3301
Robert Phillipsb97da532019-02-12 15:24:12 -05003302std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003303 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003304 const SkMatrix& viewMatrix,
3305 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003306 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003307 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003308 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003309 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003310 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3311 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003312 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003313 }
3314
3315 if (style.pathEffect()) {
3316 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003317 }
3318
Stan Ilieveb868aa2017-02-21 11:06:16 -05003319 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003320 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003321 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003322 }
3323
Stan Ilieveb868aa2017-02-21 11:06:16 -05003324 // Otherwise, if we have shader derivative support, render as device-independent
3325 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003326 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3327 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3328 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3329 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3330 // Check for near-degenerate matrix
3331 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003332 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003333 style.strokeRec());
3334 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003335 }
3336
bsalomon4b4a7cc2016-07-08 04:42:54 -07003337 return nullptr;
3338}
3339
3340///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003341
Robert Phillipsb97da532019-02-12 15:24:12 -05003342std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003343 GrPaint&& paint,
3344 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003345 const SkRect& oval, SkScalar startAngle,
3346 SkScalar sweepAngle, bool useCenter,
3347 const GrStyle& style,
3348 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003349 SkASSERT(!oval.isEmpty());
3350 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003351 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003352 if (SkScalarAbs(sweepAngle) >= 360.f) {
3353 return nullptr;
3354 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003355 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3356 return nullptr;
3357 }
3358 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003359 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3360 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003361 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003362 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003363}
3364
3365///////////////////////////////////////////////////////////////////////////////
3366
Hal Canary6f6961e2017-01-31 13:50:44 -05003367#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003368
Brian Salomon05441c42017-05-15 16:45:49 -04003369GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003370 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003371 SkScalar rotate = random->nextSScalar1() * 360.f;
3372 SkScalar translateX = random->nextSScalar1() * 1000.f;
3373 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003374 SkScalar scale;
3375 do {
3376 scale = random->nextSScalar1() * 100.f;
3377 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003378 SkMatrix viewMatrix;
3379 viewMatrix.setRotate(rotate);
3380 viewMatrix.postTranslate(translateX, translateY);
3381 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003382 SkRect circle = GrTest::TestSquare(random);
3383 SkPoint center = {circle.centerX(), circle.centerY()};
3384 SkScalar radius = circle.width() / 2.f;
3385 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003386 CircleOp::ArcParams arcParamsTmp;
3387 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003388 if (random->nextBool()) {
3389 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003390 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3391 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003392 arcParams = &arcParamsTmp;
3393 }
Greg Daniel2655ede2019-04-10 00:49:28 +00003394 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003395 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003396 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003397 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003398 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003399 }
Mike Klein16885072018-12-11 09:54:31 -05003400 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003401 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003402}
3403
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003404GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3405 SkScalar rotate = random->nextSScalar1() * 360.f;
3406 SkScalar translateX = random->nextSScalar1() * 1000.f;
3407 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003408 SkScalar scale;
3409 do {
3410 scale = random->nextSScalar1() * 100.f;
3411 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003412 SkMatrix viewMatrix;
3413 viewMatrix.setRotate(rotate);
3414 viewMatrix.postTranslate(translateX, translateY);
3415 viewMatrix.postScale(scale, scale);
3416 SkRect circle = GrTest::TestSquare(random);
3417 SkPoint center = {circle.centerX(), circle.centerY()};
3418 SkScalar radius = circle.width() / 2.f;
3419 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3420 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3421 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3422 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3423 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003424 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3425 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003426 startAngle, onAngle, offAngle, phase);
3427}
3428
Brian Salomon05441c42017-05-15 16:45:49 -04003429GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003430 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003431 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003432 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003433 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003434}
3435
Brian Salomon05441c42017-05-15 16:45:49 -04003436GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003437 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003438 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003439 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003440 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003441}
3442
Jim Van Verth64b85892019-06-17 12:01:46 -04003443GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3444 do {
3445 SkScalar rotate = random->nextSScalar1() * 360.f;
3446 SkScalar translateX = random->nextSScalar1() * 1000.f;
3447 SkScalar translateY = random->nextSScalar1() * 1000.f;
3448 SkScalar scale;
3449 do {
3450 scale = random->nextSScalar1() * 100.f;
3451 } while (scale == 0);
3452 SkMatrix viewMatrix;
3453 viewMatrix.setRotate(rotate);
3454 viewMatrix.postTranslate(translateX, translateY);
3455 viewMatrix.postScale(scale, scale);
3456 SkRect rect = GrTest::TestRect(random);
3457 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3458 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3459 if (rrect.isOval()) {
3460 continue;
3461 }
3462 std::unique_ptr<GrDrawOp> op =
3463 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3464 GrTest::TestStrokeRec(random), nullptr);
3465 if (op) {
3466 return op;
3467 }
3468 assert_alive(paint);
3469 } while (true);
3470}
3471
Brian Salomon05441c42017-05-15 16:45:49 -04003472GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003473 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003474 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003475 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003476 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003477}
3478
3479#endif