blob: e74a20adaaebc9c09140dd5eeae50394c5fa19ac [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
Robert Phillips6941f4a2020-03-12 09:41:54 -0400553 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, outputView,
554 std::move(appliedClip),
555 dstProxyView, gp,
556 GrProcessorSet::MakeEmptySet(),
557 GrPrimitiveType::kTriangles,
558 GrPipeline::InputFlags::kNone,
Chris Dalton765ed362020-03-16 17:34:44 -0600559 &GrUserStencilSettings::kUnused);
Robert Phillips6941f4a2020-03-12 09:41:54 -0400560 }
561
Brian Salomon91326c32017-08-09 16:02:19 -0400562 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500563 int instanceCount = fGeoData.count();
Jim Van Verthc5903412016-11-17 15:27:09 -0500564
Brian Salomon12d22642019-01-29 14:38:50 -0500565 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500566 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400567 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
568 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500569 if (!verts) {
570 SkDebugf("Could not allocate vertices\n");
571 return;
572 }
573
Brian Salomon12d22642019-01-29 14:38:50 -0500574 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500575 int firstIndex = 0;
576 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
577 if (!indices) {
578 SkDebugf("Could not allocate indices\n");
579 return;
580 }
581
582 int currStartVertex = 0;
583 for (int i = 0; i < instanceCount; i++) {
584 const Geometry& args = fGeoData[i];
585
Jim Van Verth57061ee2017-04-28 17:30:30 -0400586 if (args.fIsCircle) {
587 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
588 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500589
Jim Van Verth57061ee2017-04-28 17:30:30 -0400590 const uint16_t* primIndices = circle_type_to_indices(isStroked);
591 const int primIndexCount = circle_type_to_index_count(isStroked);
592 for (int i = 0; i < primIndexCount; ++i) {
593 *indices++ = primIndices[i] + currStartVertex;
594 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500595
Jim Van Verth57061ee2017-04-28 17:30:30 -0400596 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500597
Jim Van Verth57061ee2017-04-28 17:30:30 -0400598 } else {
599 this->fillInRRectVerts(args, &verts);
600
601 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
602 const int primIndexCount = rrect_type_to_index_count(args.fType);
603 for (int i = 0; i < primIndexCount; ++i) {
604 *indices++ = primIndices[i] + currStartVertex;
605 }
606
607 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400608 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500609 }
610
Robert Phillips6941f4a2020-03-12 09:41:54 -0400611 fMesh = target->allocMesh();
612 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -0600613 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700614 }
615
616 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400617 if (!fProgramInfo) {
618 this->createProgramInfo(flushState);
619 }
Robert Phillips3968fcb2019-12-05 16:40:31 -0500620
Robert Phillips6941f4a2020-03-12 09:41:54 -0400621 if (!fProgramInfo || !fMesh) {
622 return;
623 }
624
Chris Dalton765ed362020-03-16 17:34:44 -0600625 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
626 flushState->bindTextures(fProgramInfo->primProc(), *fFalloffView.proxy(),
627 fProgramInfo->pipeline());
628 flushState->drawMesh(*fMesh);
Jim Van Verthc5903412016-11-17 15:27:09 -0500629 }
630
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500631 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
632 const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500633 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500634 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500635 fVertCount += that->fVertCount;
636 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000637 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500638 }
639
Jim Van Verth7da048b2019-10-29 13:28:14 -0400640 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400641 if (fProgramInfo) {
642 fProgramInfo->visitProxies(func);
643 } else {
644 func(fFalloffView.proxy(), GrMipMapped(false));
645 }
Jim Van Verth7da048b2019-10-29 13:28:14 -0400646 }
647
Jim Van Verthc5903412016-11-17 15:27:09 -0500648 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500649 int fVertCount;
650 int fIndexCount;
Greg Danielad994cd2019-12-10 09:35:16 -0500651 GrSurfaceProxyView fFalloffView;
Jim Van Verthc5903412016-11-17 15:27:09 -0500652
Chris Daltoneb694b72020-03-16 09:25:50 -0600653 GrSimpleMesh* fMesh = nullptr;
Robert Phillips6941f4a2020-03-12 09:41:54 -0400654 GrProgramInfo* fProgramInfo = nullptr;
655
Brian Salomon05969092017-07-13 11:20:51 -0400656 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500657};
658
Brian Salomon05969092017-07-13 11:20:51 -0400659} // anonymous namespace
660
Jim Van Verthc5903412016-11-17 15:27:09 -0500661///////////////////////////////////////////////////////////////////////////////
662
Jim Van Verth57061ee2017-04-28 17:30:30 -0400663namespace GrShadowRRectOp {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400664
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500665static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* context) {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400666 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
667 GrUniqueKey key;
668 GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
669 builder.finish();
670
Greg Daniel6f5441a2020-01-28 17:02:49 -0500671 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
672
Greg Daniel3a365112020-02-14 10:47:18 -0500673 if (sk_sp<GrTextureProxy> falloffTexture =
674 proxyProvider->findOrCreateProxyByUniqueKey(key, GrColorType::kAlpha_8)) {
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500675 GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(),
676 GrColorType::kAlpha_8);
677 return {std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle};
Jim Van Verth7da048b2019-10-29 13:28:14 -0400678 }
679
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500680 static const int kWidth = 128;
681 static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
682 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
683
684 SkBitmap bitmap;
685 bitmap.allocPixels(ii, kRowBytes);
686
687 unsigned char* values = (unsigned char*)bitmap.getPixels();
688 for (int i = 0; i < 128; ++i) {
689 SkScalar d = SK_Scalar1 - i / SkIntToScalar(127);
690 values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255);
691 }
692 bitmap.setImmutable();
693
694 GrBitmapTextureMaker maker(context, bitmap);
Brian Salomonecbb0fb2020-02-28 18:07:32 -0500695 auto view = maker.view(GrMipMapped::kNo);
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500696 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
697
698 if (view) {
699 proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
700 }
701 return view;
Jim Van Verth7da048b2019-10-29 13:28:14 -0400702}
703
704
Robert Phillipsb97da532019-02-12 15:24:12 -0500705std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400706 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400707 const SkMatrix& viewMatrix,
708 const SkRRect& rrect,
709 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400710 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500711 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500712 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400713
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500714 GrSurfaceProxyView falloffView = create_falloff_texture(context);
715 if (!falloffView) {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400716 return nullptr;
717 }
718
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500719 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500720 const SkRect& rrectBounds = rrect.getBounds();
721 SkRect bounds;
722 viewMatrix.mapRect(&bounds, rrectBounds);
723
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400724 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500725 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400726 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
727 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
728 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500729
Robert Phillipse5763782019-04-17 14:38:24 -0400730 if (scaledInsetWidth <= 0) {
731 return nullptr;
732 }
733
Robert Phillips9da87e02019-02-04 13:26:26 -0500734 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400735
736 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
737 scaledRadius,
738 rrect.isOval(),
739 blurWidth,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400740 scaledInsetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500741 std::move(falloffView));
Jim Van Verthc5903412016-11-17 15:27:09 -0500742}
Brian Salomonfc527d22016-12-14 21:07:01 -0500743}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400744
Jim Van Verthc5903412016-11-17 15:27:09 -0500745///////////////////////////////////////////////////////////////////////////////
746
Hal Canary6f6961e2017-01-31 13:50:44 -0500747#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500748
Brian Salomon05969092017-07-13 11:20:51 -0400749GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Brian Salomonfc118442019-11-22 19:09:27 -0500750 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
751 // an acceptable combination.
Brian Osman4462c042018-06-08 16:35:44 -0400752 do {
Brian Salomonfc118442019-11-22 19:09:27 -0500753 // create a similarity matrix
754 SkScalar rotate = random->nextSScalar1() * 360.f;
755 SkScalar translateX = random->nextSScalar1() * 1000.f;
756 SkScalar translateY = random->nextSScalar1() * 1000.f;
757 SkScalar scale = random->nextSScalar1() * 100.f;
758 SkMatrix viewMatrix;
759 viewMatrix.setRotate(rotate);
760 viewMatrix.postTranslate(translateX, translateY);
761 viewMatrix.postScale(scale, scale);
762 SkScalar insetWidth = random->nextSScalar1() * 72.f;
763 SkScalar blurWidth = random->nextSScalar1() * 72.f;
764 bool isCircle = random->nextBool();
765 // This op doesn't use a full GrPaint, just a color.
766 GrColor color = paint.getColor4f().toBytes_RGBA();
767 if (isCircle) {
768 SkRect circle = GrTest::TestSquare(random);
769 SkRRect rrect = SkRRect::MakeOval(circle);
770 if (auto op = GrShadowRRectOp::Make(
771 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
772 return op;
773 }
774 } else {
775 SkRRect rrect;
776 do {
777 // This may return a rrect with elliptical corners, which will cause an assert.
778 rrect = GrTest::TestRRectSimple(random);
779 } while (!SkRRectPriv::IsSimpleCircular(rrect));
780 if (auto op = GrShadowRRectOp::Make(
781 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
782 return op;
783 }
784 }
785 } while (true);
Jim Van Verthc5903412016-11-17 15:27:09 -0500786}
787
788#endif