blob: 119addd3eef17a212648da167266870fe6266560 [file] [log] [blame]
Jim Van Verthc5903412016-11-17 15:27:09 -05001/*
2 * Copyright 2016 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 "src/gpu/ops/GrShadowRRectOp.h"
Robert Phillips7c525e62018-06-12 10:11:12 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/private/GrRecordingContext.h"
11#include "src/core/SkRRectPriv.h"
Greg Daniel6f5441a2020-01-28 17:02:49 -050012#include "src/gpu/GrBitmapTextureMaker.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/gpu/GrDrawOpTest.h"
14#include "src/gpu/GrMemoryPool.h"
15#include "src/gpu/GrOpFlushState.h"
Robert Phillips6941f4a2020-03-12 09:41:54 -040016#include "src/gpu/GrProgramInfo.h"
Jim Van Verth7da048b2019-10-29 13:28:14 -040017#include "src/gpu/GrProxyProvider.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/gpu/GrRecordingContextPriv.h"
19#include "src/gpu/effects/GrShadowGeoProc.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050020#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Jim Van Verthc5903412016-11-17 15:27:09 -050021
22///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040023// Circle Data
24//
Jim Van Verthc5903412016-11-17 15:27:09 -050025// We have two possible cases for geometry for a circle:
26
27// In the case of a normal fill, we draw geometry for the circle as an octagon.
28static const uint16_t gFillCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050029 // enter the octagon
30 // clang-format off
31 0, 1, 8, 1, 2, 8,
32 2, 3, 8, 3, 4, 8,
33 4, 5, 8, 5, 6, 8,
34 6, 7, 8, 7, 0, 8,
35 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050036};
37
38// For stroked circles, we use two nested octagons.
39static const uint16_t gStrokeCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050040 // enter the octagon
41 // clang-format off
42 0, 1, 9, 0, 9, 8,
43 1, 2, 10, 1, 10, 9,
44 2, 3, 11, 2, 11, 10,
45 3, 4, 12, 3, 12, 11,
46 4, 5, 13, 4, 13, 12,
47 5, 6, 14, 5, 14, 13,
48 6, 7, 15, 6, 15, 14,
49 7, 0, 8, 7, 8, 15,
50 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050051};
52
53static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
54static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
55static const int kVertsPerStrokeCircle = 16;
56static const int kVertsPerFillCircle = 9;
57
58static int circle_type_to_vert_count(bool stroked) {
59 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
60}
61
62static int circle_type_to_index_count(bool stroked) {
63 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
64}
65
66static const uint16_t* circle_type_to_indices(bool stroked) {
67 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
68}
69
70///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040071// RoundRect Data
72//
Jim Van Verthb6069df2017-04-28 11:00:35 -040073// The geometry for a shadow roundrect is similar to a 9-patch:
Jim Van Verthc5903412016-11-17 15:27:09 -050074// ____________
75// |_|________|_|
76// | | | |
77// | | | |
78// | | | |
79// |_|________|_|
80// |_|________|_|
81//
Jim Van Verthb6069df2017-04-28 11:00:35 -040082// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
83// shows the upper part of the upper left corner. The bottom triangle would similarly be split
84// into two triangles.)
85// ________
86// |\ \ |
87// | \ \ |
88// | \\ |
89// | \|
90// --------
91//
92// The center of the fan handles the curve of the corner. For roundrects where the stroke width
93// is greater than the corner radius, the outer triangles blend from the curve to the straight
94// sides. Otherwise these triangles will be degenerate.
95//
96// In the case where the stroke width is greater than the corner radius and the
97// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
98// This rectangle extends the coverage values of the center edges of the 9-patch.
Jim Van Verthc5903412016-11-17 15:27:09 -050099// ____________
100// |_|________|_|
101// | |\ ____ /| |
102// | | | | | |
103// | | |____| | |
104// |_|/______\|_|
105// |_|________|_|
106//
Jim Van Verthb6069df2017-04-28 11:00:35 -0400107// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
Jim Van Verthc5903412016-11-17 15:27:09 -0500108
Jim Van Verthb6069df2017-04-28 11:00:35 -0400109static const uint16_t gRRectIndices[] = {
110 // clang-format off
111 // overstroke quads
112 // we place this at the beginning so that we can skip these indices when rendering as filled
113 0, 6, 25, 0, 25, 24,
114 6, 18, 27, 6, 27, 25,
115 18, 12, 26, 18, 26, 27,
116 12, 0, 24, 12, 24, 26,
Jim Van Verthc5903412016-11-17 15:27:09 -0500117
Jim Van Verthb6069df2017-04-28 11:00:35 -0400118 // corners
119 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
120 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
121 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
122 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
Jim Van Verthc5903412016-11-17 15:27:09 -0500123
Jim Van Verthb6069df2017-04-28 11:00:35 -0400124 // edges
125 0, 5, 11, 0, 11, 6,
126 6, 7, 19, 6, 19, 18,
127 18, 23, 17, 18, 17, 12,
128 12, 13, 1, 12, 1, 0,
129
130 // fill quad
131 // we place this at the end so that we can skip these indices when rendering as stroked
132 0, 6, 18, 0, 18, 12,
133 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -0500134};
Jim Van Verthc5903412016-11-17 15:27:09 -0500135
136// overstroke count
Jim Van Verthb6069df2017-04-28 11:00:35 -0400137static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
Jim Van Verthc5903412016-11-17 15:27:09 -0500138// simple stroke count skips overstroke indices
Jim Van Verthb6069df2017-04-28 11:00:35 -0400139static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
140// fill count adds final quad to stroke count
141static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
142static const int kVertsPerStrokeRRect = 24;
143static const int kVertsPerOverstrokeRRect = 28;
144static const int kVertsPerFillRRect = 24;
Jim Van Verthc5903412016-11-17 15:27:09 -0500145
146enum RRectType {
147 kFill_RRectType,
148 kStroke_RRectType,
149 kOverstroke_RRectType,
150};
151
152static int rrect_type_to_vert_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500153 switch (type) {
154 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400155 return kVertsPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500156 case kStroke_RRectType:
157 return kVertsPerStrokeRRect;
158 case kOverstroke_RRectType:
159 return kVertsPerOverstrokeRRect;
160 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400161 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500162}
163
164static int rrect_type_to_index_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500165 switch (type) {
166 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400167 return kIndicesPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500168 case kStroke_RRectType:
169 return kIndicesPerStrokeRRect;
170 case kOverstroke_RRectType:
171 return kIndicesPerOverstrokeRRect;
172 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400173 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500174}
175
176static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500177 switch (type) {
178 case kFill_RRectType:
Brian Salomonfc527d22016-12-14 21:07:01 -0500179 case kStroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400180 return gRRectIndices + 6*4;
Brian Salomonfc527d22016-12-14 21:07:01 -0500181 case kOverstroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400182 return gRRectIndices;
Brian Salomonfc527d22016-12-14 21:07:01 -0500183 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400184 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500185}
186
Jim Van Verth57061ee2017-04-28 17:30:30 -0400187///////////////////////////////////////////////////////////////////////////////
Brian Salomon05969092017-07-13 11:20:51 -0400188namespace {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400189
Brian Salomon05969092017-07-13 11:20:51 -0400190class ShadowCircularRRectOp final : public GrMeshDrawOp {
Jim Van Verthc5903412016-11-17 15:27:09 -0500191public:
Brian Salomon25a88092016-12-01 09:36:50 -0500192 DEFINE_OP_CLASS_ID
Jim Van Verthc5903412016-11-17 15:27:09 -0500193
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400194 // An insetWidth > 1/2 rect width or height indicates a simple fill.
Brian Salomon05969092017-07-13 11:20:51 -0400195 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400196 float devRadius, bool isCircle, float blurRadius, float insetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500197 GrSurfaceProxyView falloffView)
Jim Van Verth7da048b2019-10-29 13:28:14 -0400198 : INHERITED(ClassID())
Greg Danielad994cd2019-12-10 09:35:16 -0500199 , fFalloffView(std::move(falloffView)) {
Jim Van Verthc5903412016-11-17 15:27:09 -0500200 SkRect bounds = devRect;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400201 SkASSERT(insetWidth > 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400202 SkScalar innerRadius = 0.0f;
Jim Van Verthc5903412016-11-17 15:27:09 -0500203 SkScalar outerRadius = devRadius;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400204 SkScalar umbraInset;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400205
206 RRectType type = kFill_RRectType;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400207 if (isCircle) {
208 umbraInset = 0;
209 } else {
Brian Osman788b9162020-02-07 10:36:46 -0500210 umbraInset = std::max(outerRadius, blurRadius);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400211 }
212
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400213 // If stroke is greater than width or height, this is still a fill,
214 // otherwise we compute stroke params.
215 if (isCircle) {
216 innerRadius = devRadius - insetWidth;
217 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
218 } else {
Brian Osman788b9162020-02-07 10:36:46 -0500219 if (insetWidth <= 0.5f*std::min(devRect.width(), devRect.height())) {
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400220 // We don't worry about a real inner radius, we just need to know if we
221 // need to create overstroke vertices.
Brian Osman788b9162020-02-07 10:36:46 -0500222 innerRadius = std::max(insetWidth - umbraInset, 0.0f);
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400223 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400224 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500225 }
226
Greg Daniel5faf4742019-10-01 15:14:44 -0400227 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
Jim Van Verthc5903412016-11-17 15:27:09 -0500228
Jim Van Verth57061ee2017-04-28 17:30:30 -0400229 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
Jim Van Verthfb186392018-09-11 11:37:46 -0400230 blurRadius, bounds, type, isCircle});
Jim Van Verth57061ee2017-04-28 17:30:30 -0400231 if (isCircle) {
232 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
233 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
234 } else {
235 fVertCount = rrect_type_to_vert_count(type);
236 fIndexCount = rrect_type_to_index_count(type);
237 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500238 }
239
Brian Salomonfc527d22016-12-14 21:07:01 -0500240 const char* name() const override { return "ShadowCircularRRectOp"; }
Jim Van Verthc5903412016-11-17 15:27:09 -0500241
Brian Osman9a390ac2018-11-12 09:47:48 -0500242#ifdef SK_DEBUG
Jim Van Verthc5903412016-11-17 15:27:09 -0500243 SkString dumpInfo() const override {
244 SkString string;
245 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500246 string.appendf(
247 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
Jim Van Verth57061ee2017-04-28 17:30:30 -0400248 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
Brian Salomonfc527d22016-12-14 21:07:01 -0500249 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
250 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
Jim Van Verthb6069df2017-04-28 11:00:35 -0400251 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
Jim Van Verth57061ee2017-04-28 17:30:30 -0400252 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
Jim Van Verthc5903412016-11-17 15:27:09 -0500253 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500254 string.append(INHERITED::dumpInfo());
255 return string;
256 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500257#endif
Jim Van Verthc5903412016-11-17 15:27:09 -0500258
Brian Salomon05969092017-07-13 11:20:51 -0400259 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
260
Chris Dalton6ce447a2019-06-23 18:07:38 -0600261 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
262 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700263 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400264 }
265
Brian Salomon92aee3d2016-12-21 09:20:25 -0500266private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400267 struct Geometry {
268 GrColor fColor;
269 SkScalar fOuterRadius;
270 SkScalar fUmbraInset;
271 SkScalar fInnerRadius;
272 SkScalar fBlurRadius;
273 SkRect fDevBounds;
274 RRectType fType;
275 bool fIsCircle;
276 };
277
Jim Van Verthc5903412016-11-17 15:27:09 -0500278 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500279 SkPoint fPos;
280 GrColor fColor;
281 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400282 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500283 };
284
Jim Van Verth57061ee2017-04-28 17:30:30 -0400285 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
286
287 GrColor color = args.fColor;
288 SkScalar outerRadius = args.fOuterRadius;
289 SkScalar innerRadius = args.fInnerRadius;
290 SkScalar blurRadius = args.fBlurRadius;
291 SkScalar distanceCorrection = outerRadius / blurRadius;
292
293 const SkRect& bounds = args.fDevBounds;
294
295 // The inner radius in the vertex data must be specified in normalized space.
296 innerRadius = innerRadius / outerRadius;
297
298 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
299 SkScalar halfWidth = 0.5f * bounds.width();
300 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
301
302 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500303 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400304 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400305 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500306 (*verts)++;
307
Jim Van Verth57061ee2017-04-28 17:30:30 -0400308 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500309 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400310 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400311 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500312 (*verts)++;
313
Jim Van Verth57061ee2017-04-28 17:30:30 -0400314 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500315 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400316 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400317 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500318 (*verts)++;
319
Jim Van Verth57061ee2017-04-28 17:30:30 -0400320 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500321 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400322 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400323 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500324 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400325
326 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
327 (*verts)->fColor = color;
328 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
329 (*verts)->fDistanceCorrection = distanceCorrection;
330 (*verts)++;
331
332 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
333 (*verts)->fColor = color;
334 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
335 (*verts)->fDistanceCorrection = distanceCorrection;
336 (*verts)++;
337
338 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
339 (*verts)->fColor = color;
340 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
341 (*verts)->fDistanceCorrection = distanceCorrection;
342 (*verts)++;
343
344 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
345 (*verts)->fColor = color;
346 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
347 (*verts)->fDistanceCorrection = distanceCorrection;
348 (*verts)++;
349
350 if (isStroked) {
351 // compute the inner ring
352
353 // cosine and sine of pi/8
354 SkScalar c = 0.923579533f;
355 SkScalar s = 0.382683432f;
356 SkScalar r = args.fInnerRadius;
357
358 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
359 (*verts)->fColor = color;
360 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
361 (*verts)->fDistanceCorrection = distanceCorrection;
362 (*verts)++;
363
364 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
365 (*verts)->fColor = color;
366 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
367 (*verts)->fDistanceCorrection = distanceCorrection;
368 (*verts)++;
369
370 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
371 (*verts)->fColor = color;
372 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
373 (*verts)->fDistanceCorrection = distanceCorrection;
374 (*verts)++;
375
376 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
377 (*verts)->fColor = color;
378 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
379 (*verts)->fDistanceCorrection = distanceCorrection;
380 (*verts)++;
381
382 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
383 (*verts)->fColor = color;
384 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
385 (*verts)->fDistanceCorrection = distanceCorrection;
386 (*verts)++;
387
388 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
389 (*verts)->fColor = color;
390 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
391 (*verts)->fDistanceCorrection = distanceCorrection;
392 (*verts)++;
393
394 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
395 (*verts)->fColor = color;
396 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
397 (*verts)->fDistanceCorrection = distanceCorrection;
398 (*verts)++;
399
400 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
401 (*verts)->fColor = color;
402 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
403 (*verts)->fDistanceCorrection = distanceCorrection;
404 (*verts)++;
405 } else {
406 // filled
407 (*verts)->fPos = center;
408 (*verts)->fColor = color;
409 (*verts)->fOffset = SkPoint::Make(0, 0);
410 (*verts)->fDistanceCorrection = distanceCorrection;
411 (*verts)++;
412 }
413 }
414
415 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
416 GrColor color = args.fColor;
417 SkScalar outerRadius = args.fOuterRadius;
418
419 const SkRect& bounds = args.fDevBounds;
420
421 SkScalar umbraInset = args.fUmbraInset;
Brian Osman788b9162020-02-07 10:36:46 -0500422 SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height());
Jim Van Verth57061ee2017-04-28 17:30:30 -0400423 if (umbraInset > minDim) {
424 umbraInset = minDim;
425 }
426
427 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
428 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
429 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
430 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
431 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
432 bounds.fLeft, bounds.fRight };
433 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
434 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
435 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
436 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
437 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
438 bounds.fBottom, bounds.fBottom };
439
440 SkScalar blurRadius = args.fBlurRadius;
441
442 // In the case where we have to inset more for the umbra, our two triangles in the
443 // corner get skewed to a diamond rather than a square. To correct for that,
444 // we also skew the vectors we send to the shader that help define the circle.
445 // By doing so, we end up with a quarter circle in the corner rather than the
446 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400447
448 // This is a bit magical, but it gives us the correct results at extrema:
449 // a) umbraInset == outerRadius produces an orthogonal vector
450 // b) outerRadius == 0 produces a diagonal vector
451 // And visually the corner looks correct.
452 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400453 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400454 // We want the circle edge to fall fractionally along the diagonal at
455 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
456 //
457 // Setting the components of the diagonal offset to the following value will give us that.
458 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
459 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400460 SkScalar distanceCorrection = umbraInset / blurRadius;
461
462 // build corner by corner
463 for (int i = 0; i < 4; ++i) {
464 // inner point
465 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
466 (*verts)->fColor = color;
467 (*verts)->fOffset = SkVector::Make(0, 0);
468 (*verts)->fDistanceCorrection = distanceCorrection;
469 (*verts)++;
470
471 // outer points
472 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
473 (*verts)->fColor = color;
474 (*verts)->fOffset = SkVector::Make(0, -1);
475 (*verts)->fDistanceCorrection = distanceCorrection;
476 (*verts)++;
477
478 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
479 (*verts)->fColor = color;
480 (*verts)->fOffset = outerVec;
481 (*verts)->fDistanceCorrection = distanceCorrection;
482 (*verts)++;
483
484 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
485 (*verts)->fColor = color;
486 (*verts)->fOffset = diagVec;
487 (*verts)->fDistanceCorrection = distanceCorrection;
488 (*verts)++;
489
490 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
491 (*verts)->fColor = color;
492 (*verts)->fOffset = outerVec;
493 (*verts)->fDistanceCorrection = distanceCorrection;
494 (*verts)++;
495
496 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
497 (*verts)->fColor = color;
498 (*verts)->fOffset = SkVector::Make(0, -1);
499 (*verts)->fDistanceCorrection = distanceCorrection;
500 (*verts)++;
501 }
502
503 // Add the additional vertices for overstroked rrects.
504 // Effectively this is an additional stroked rrect, with its
505 // parameters equal to those in the center of the 9-patch. This will
506 // give constant values across this inner ring.
507 if (kOverstroke_RRectType == args.fType) {
508 SkASSERT(args.fInnerRadius > 0.0f);
509
510 SkScalar inset = umbraInset + args.fInnerRadius;
511
512 // TL
513 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
514 (*verts)->fColor = color;
515 (*verts)->fOffset = SkPoint::Make(0, 0);
516 (*verts)->fDistanceCorrection = distanceCorrection;
517 (*verts)++;
518
519 // TR
520 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
521 (*verts)->fColor = color;
522 (*verts)->fOffset = SkPoint::Make(0, 0);
523 (*verts)->fDistanceCorrection = distanceCorrection;
524 (*verts)++;
525
526 // BL
527 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
528 (*verts)->fColor = color;
529 (*verts)->fOffset = SkPoint::Make(0, 0);
530 (*verts)->fDistanceCorrection = distanceCorrection;
531 (*verts)++;
532
533 // BR
534 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
535 (*verts)->fColor = color;
536 (*verts)->fOffset = SkPoint::Make(0, 0);
537 (*verts)->fDistanceCorrection = distanceCorrection;
538 (*verts)++;
539 }
540
Jim Van Verthc5903412016-11-17 15:27:09 -0500541 }
542
Robert Phillips2669a7b2020-03-12 12:07:19 -0400543 GrProgramInfo* programInfo() override { return fProgramInfo; }
544
Robert Phillips6941f4a2020-03-12 09:41:54 -0400545 void onCreateProgramInfo(const GrCaps* caps,
546 SkArenaAlloc* arena,
Robert Phillips4133dc42020-03-11 15:55:55 -0400547 const GrSurfaceProxyView* outputView,
Robert Phillips6941f4a2020-03-12 09:41:54 -0400548 GrAppliedClip&& appliedClip,
549 const GrXferProcessor::DstProxyView& dstProxyView) override {
550 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView);
551 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
552
553 static constexpr int kOnePrimProcTexture = 1;
554 auto fixedDynamicState = GrMeshDrawOp::Target::MakeFixedDynamicState(arena, &appliedClip,
555 kOnePrimProcTexture);
556 fixedDynamicState->fPrimitiveProcessorTextures[0] = fFalloffView.proxy();
557
558 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, outputView,
559 std::move(appliedClip),
560 dstProxyView, gp,
561 GrProcessorSet::MakeEmptySet(),
562 GrPrimitiveType::kTriangles,
563 GrPipeline::InputFlags::kNone,
564 &GrUserStencilSettings::kUnused,
565 fixedDynamicState);
566 }
567
Brian Salomon91326c32017-08-09 16:02:19 -0400568 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500569 int instanceCount = fGeoData.count();
Jim Van Verthc5903412016-11-17 15:27:09 -0500570
Brian Salomon12d22642019-01-29 14:38:50 -0500571 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500572 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400573 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
574 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500575 if (!verts) {
576 SkDebugf("Could not allocate vertices\n");
577 return;
578 }
579
Brian Salomon12d22642019-01-29 14:38:50 -0500580 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500581 int firstIndex = 0;
582 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
583 if (!indices) {
584 SkDebugf("Could not allocate indices\n");
585 return;
586 }
587
588 int currStartVertex = 0;
589 for (int i = 0; i < instanceCount; i++) {
590 const Geometry& args = fGeoData[i];
591
Jim Van Verth57061ee2017-04-28 17:30:30 -0400592 if (args.fIsCircle) {
593 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
594 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500595
Jim Van Verth57061ee2017-04-28 17:30:30 -0400596 const uint16_t* primIndices = circle_type_to_indices(isStroked);
597 const int primIndexCount = circle_type_to_index_count(isStroked);
598 for (int i = 0; i < primIndexCount; ++i) {
599 *indices++ = primIndices[i] + currStartVertex;
600 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500601
Jim Van Verth57061ee2017-04-28 17:30:30 -0400602 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500603
Jim Van Verth57061ee2017-04-28 17:30:30 -0400604 } else {
605 this->fillInRRectVerts(args, &verts);
606
607 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
608 const int primIndexCount = rrect_type_to_index_count(args.fType);
609 for (int i = 0; i < primIndexCount; ++i) {
610 *indices++ = primIndices[i] + currStartVertex;
611 }
612
613 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400614 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500615 }
616
Robert Phillips6941f4a2020-03-12 09:41:54 -0400617 fMesh = target->allocMesh();
618 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -0600619 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700620 }
621
622 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400623 if (!fProgramInfo) {
624 this->createProgramInfo(flushState);
625 }
Robert Phillips3968fcb2019-12-05 16:40:31 -0500626
Robert Phillips6941f4a2020-03-12 09:41:54 -0400627 if (!fProgramInfo || !fMesh) {
628 return;
629 }
630
631 flushState->opsRenderPass()->bindPipeline(*fProgramInfo, chainBounds);
632 flushState->opsRenderPass()->drawMeshes(*fProgramInfo, fMesh, 1);
Jim Van Verthc5903412016-11-17 15:27:09 -0500633 }
634
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500635 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
636 const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500637 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500638 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500639 fVertCount += that->fVertCount;
640 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000641 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500642 }
643
Jim Van Verth7da048b2019-10-29 13:28:14 -0400644 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400645 if (fProgramInfo) {
646 fProgramInfo->visitProxies(func);
647 } else {
648 func(fFalloffView.proxy(), GrMipMapped(false));
649 }
Jim Van Verth7da048b2019-10-29 13:28:14 -0400650 }
651
Jim Van Verthc5903412016-11-17 15:27:09 -0500652 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500653 int fVertCount;
654 int fIndexCount;
Greg Danielad994cd2019-12-10 09:35:16 -0500655 GrSurfaceProxyView fFalloffView;
Jim Van Verthc5903412016-11-17 15:27:09 -0500656
Robert Phillips6941f4a2020-03-12 09:41:54 -0400657 GrMesh* fMesh = nullptr;
658 GrProgramInfo* fProgramInfo = nullptr;
659
Brian Salomon05969092017-07-13 11:20:51 -0400660 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500661};
662
Brian Salomon05969092017-07-13 11:20:51 -0400663} // anonymous namespace
664
Jim Van Verthc5903412016-11-17 15:27:09 -0500665///////////////////////////////////////////////////////////////////////////////
666
Jim Van Verth57061ee2017-04-28 17:30:30 -0400667namespace GrShadowRRectOp {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400668
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500669static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* context) {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400670 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
671 GrUniqueKey key;
672 GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
673 builder.finish();
674
Greg Daniel6f5441a2020-01-28 17:02:49 -0500675 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
676
Greg Daniel3a365112020-02-14 10:47:18 -0500677 if (sk_sp<GrTextureProxy> falloffTexture =
678 proxyProvider->findOrCreateProxyByUniqueKey(key, GrColorType::kAlpha_8)) {
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500679 GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(),
680 GrColorType::kAlpha_8);
681 return {std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle};
Jim Van Verth7da048b2019-10-29 13:28:14 -0400682 }
683
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500684 static const int kWidth = 128;
685 static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
686 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
687
688 SkBitmap bitmap;
689 bitmap.allocPixels(ii, kRowBytes);
690
691 unsigned char* values = (unsigned char*)bitmap.getPixels();
692 for (int i = 0; i < 128; ++i) {
693 SkScalar d = SK_Scalar1 - i / SkIntToScalar(127);
694 values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255);
695 }
696 bitmap.setImmutable();
697
698 GrBitmapTextureMaker maker(context, bitmap);
Brian Salomonecbb0fb2020-02-28 18:07:32 -0500699 auto view = maker.view(GrMipMapped::kNo);
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500700 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
701
702 if (view) {
703 proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
704 }
705 return view;
Jim Van Verth7da048b2019-10-29 13:28:14 -0400706}
707
708
Robert Phillipsb97da532019-02-12 15:24:12 -0500709std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400710 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400711 const SkMatrix& viewMatrix,
712 const SkRRect& rrect,
713 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400714 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500715 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500716 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400717
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500718 GrSurfaceProxyView falloffView = create_falloff_texture(context);
719 if (!falloffView) {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400720 return nullptr;
721 }
722
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500723 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500724 const SkRect& rrectBounds = rrect.getBounds();
725 SkRect bounds;
726 viewMatrix.mapRect(&bounds, rrectBounds);
727
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400728 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500729 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400730 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
731 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
732 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500733
Robert Phillipse5763782019-04-17 14:38:24 -0400734 if (scaledInsetWidth <= 0) {
735 return nullptr;
736 }
737
Robert Phillips9da87e02019-02-04 13:26:26 -0500738 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400739
740 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
741 scaledRadius,
742 rrect.isOval(),
743 blurWidth,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400744 scaledInsetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500745 std::move(falloffView));
Jim Van Verthc5903412016-11-17 15:27:09 -0500746}
Brian Salomonfc527d22016-12-14 21:07:01 -0500747}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400748
Jim Van Verthc5903412016-11-17 15:27:09 -0500749///////////////////////////////////////////////////////////////////////////////
750
Hal Canary6f6961e2017-01-31 13:50:44 -0500751#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500752
Brian Salomon05969092017-07-13 11:20:51 -0400753GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Brian Salomonfc118442019-11-22 19:09:27 -0500754 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
755 // an acceptable combination.
Brian Osman4462c042018-06-08 16:35:44 -0400756 do {
Brian Salomonfc118442019-11-22 19:09:27 -0500757 // create a similarity matrix
758 SkScalar rotate = random->nextSScalar1() * 360.f;
759 SkScalar translateX = random->nextSScalar1() * 1000.f;
760 SkScalar translateY = random->nextSScalar1() * 1000.f;
761 SkScalar scale = random->nextSScalar1() * 100.f;
762 SkMatrix viewMatrix;
763 viewMatrix.setRotate(rotate);
764 viewMatrix.postTranslate(translateX, translateY);
765 viewMatrix.postScale(scale, scale);
766 SkScalar insetWidth = random->nextSScalar1() * 72.f;
767 SkScalar blurWidth = random->nextSScalar1() * 72.f;
768 bool isCircle = random->nextBool();
769 // This op doesn't use a full GrPaint, just a color.
770 GrColor color = paint.getColor4f().toBytes_RGBA();
771 if (isCircle) {
772 SkRect circle = GrTest::TestSquare(random);
773 SkRRect rrect = SkRRect::MakeOval(circle);
774 if (auto op = GrShadowRRectOp::Make(
775 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
776 return op;
777 }
778 } else {
779 SkRRect rrect;
780 do {
781 // This may return a rrect with elliptical corners, which will cause an assert.
782 rrect = GrTest::TestRRectSimple(random);
783 } while (!SkRRectPriv::IsSimpleCircular(rrect));
784 if (auto op = GrShadowRRectOp::Make(
785 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
786 return op;
787 }
788 }
789 } while (true);
Jim Van Verthc5903412016-11-17 15:27:09 -0500790}
791
792#endif