blob: 9d1d74031f1da191dfeaca03346488eaadd16773 [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
Robert Phillipsb7bfbc22020-07-01 12:55:01 -040010#include "include/gpu/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#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 Salomon05969092017-07-13 11:20:51 -0400242 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
243
Chris Dalton6ce447a2019-06-23 18:07:38 -0600244 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
245 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700246 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400247 }
248
Brian Salomon92aee3d2016-12-21 09:20:25 -0500249private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400250 struct Geometry {
251 GrColor fColor;
252 SkScalar fOuterRadius;
253 SkScalar fUmbraInset;
254 SkScalar fInnerRadius;
255 SkScalar fBlurRadius;
256 SkRect fDevBounds;
257 RRectType fType;
258 bool fIsCircle;
259 };
260
Jim Van Verthc5903412016-11-17 15:27:09 -0500261 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500262 SkPoint fPos;
263 GrColor fColor;
264 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400265 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500266 };
267
Jim Van Verth57061ee2017-04-28 17:30:30 -0400268 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
269
270 GrColor color = args.fColor;
271 SkScalar outerRadius = args.fOuterRadius;
272 SkScalar innerRadius = args.fInnerRadius;
273 SkScalar blurRadius = args.fBlurRadius;
274 SkScalar distanceCorrection = outerRadius / blurRadius;
275
276 const SkRect& bounds = args.fDevBounds;
277
278 // The inner radius in the vertex data must be specified in normalized space.
279 innerRadius = innerRadius / outerRadius;
280
281 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
282 SkScalar halfWidth = 0.5f * bounds.width();
283 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
284
285 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500286 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400287 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400288 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500289 (*verts)++;
290
Jim Van Verth57061ee2017-04-28 17:30:30 -0400291 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500292 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400293 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400294 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500295 (*verts)++;
296
Jim Van Verth57061ee2017-04-28 17:30:30 -0400297 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500298 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400299 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400300 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500301 (*verts)++;
302
Jim Van Verth57061ee2017-04-28 17:30:30 -0400303 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500304 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400305 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400306 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500307 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400308
309 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
310 (*verts)->fColor = color;
311 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
312 (*verts)->fDistanceCorrection = distanceCorrection;
313 (*verts)++;
314
315 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
316 (*verts)->fColor = color;
317 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
318 (*verts)->fDistanceCorrection = distanceCorrection;
319 (*verts)++;
320
321 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
322 (*verts)->fColor = color;
323 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
324 (*verts)->fDistanceCorrection = distanceCorrection;
325 (*verts)++;
326
327 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
328 (*verts)->fColor = color;
329 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
330 (*verts)->fDistanceCorrection = distanceCorrection;
331 (*verts)++;
332
333 if (isStroked) {
334 // compute the inner ring
335
336 // cosine and sine of pi/8
337 SkScalar c = 0.923579533f;
338 SkScalar s = 0.382683432f;
339 SkScalar r = args.fInnerRadius;
340
341 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
342 (*verts)->fColor = color;
343 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
344 (*verts)->fDistanceCorrection = distanceCorrection;
345 (*verts)++;
346
347 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
348 (*verts)->fColor = color;
349 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
350 (*verts)->fDistanceCorrection = distanceCorrection;
351 (*verts)++;
352
353 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
354 (*verts)->fColor = color;
355 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
356 (*verts)->fDistanceCorrection = distanceCorrection;
357 (*verts)++;
358
359 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
360 (*verts)->fColor = color;
361 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
362 (*verts)->fDistanceCorrection = distanceCorrection;
363 (*verts)++;
364
365 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
366 (*verts)->fColor = color;
367 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
368 (*verts)->fDistanceCorrection = distanceCorrection;
369 (*verts)++;
370
371 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
372 (*verts)->fColor = color;
373 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
374 (*verts)->fDistanceCorrection = distanceCorrection;
375 (*verts)++;
376
377 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
378 (*verts)->fColor = color;
379 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
380 (*verts)->fDistanceCorrection = distanceCorrection;
381 (*verts)++;
382
383 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
384 (*verts)->fColor = color;
385 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
386 (*verts)->fDistanceCorrection = distanceCorrection;
387 (*verts)++;
388 } else {
389 // filled
390 (*verts)->fPos = center;
391 (*verts)->fColor = color;
392 (*verts)->fOffset = SkPoint::Make(0, 0);
393 (*verts)->fDistanceCorrection = distanceCorrection;
394 (*verts)++;
395 }
396 }
397
398 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
399 GrColor color = args.fColor;
400 SkScalar outerRadius = args.fOuterRadius;
401
402 const SkRect& bounds = args.fDevBounds;
403
404 SkScalar umbraInset = args.fUmbraInset;
Brian Osman788b9162020-02-07 10:36:46 -0500405 SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height());
Jim Van Verth57061ee2017-04-28 17:30:30 -0400406 if (umbraInset > minDim) {
407 umbraInset = minDim;
408 }
409
410 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
411 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
412 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
413 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
414 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
415 bounds.fLeft, bounds.fRight };
416 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
417 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
418 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
419 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
420 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
421 bounds.fBottom, bounds.fBottom };
422
423 SkScalar blurRadius = args.fBlurRadius;
424
425 // In the case where we have to inset more for the umbra, our two triangles in the
426 // corner get skewed to a diamond rather than a square. To correct for that,
427 // we also skew the vectors we send to the shader that help define the circle.
428 // By doing so, we end up with a quarter circle in the corner rather than the
429 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400430
431 // This is a bit magical, but it gives us the correct results at extrema:
432 // a) umbraInset == outerRadius produces an orthogonal vector
433 // b) outerRadius == 0 produces a diagonal vector
434 // And visually the corner looks correct.
435 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400436 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400437 // We want the circle edge to fall fractionally along the diagonal at
438 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
439 //
440 // Setting the components of the diagonal offset to the following value will give us that.
441 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
442 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400443 SkScalar distanceCorrection = umbraInset / blurRadius;
444
445 // build corner by corner
446 for (int i = 0; i < 4; ++i) {
447 // inner point
448 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
449 (*verts)->fColor = color;
450 (*verts)->fOffset = SkVector::Make(0, 0);
451 (*verts)->fDistanceCorrection = distanceCorrection;
452 (*verts)++;
453
454 // outer points
455 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
456 (*verts)->fColor = color;
457 (*verts)->fOffset = SkVector::Make(0, -1);
458 (*verts)->fDistanceCorrection = distanceCorrection;
459 (*verts)++;
460
461 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
462 (*verts)->fColor = color;
463 (*verts)->fOffset = outerVec;
464 (*verts)->fDistanceCorrection = distanceCorrection;
465 (*verts)++;
466
467 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
468 (*verts)->fColor = color;
469 (*verts)->fOffset = diagVec;
470 (*verts)->fDistanceCorrection = distanceCorrection;
471 (*verts)++;
472
473 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
474 (*verts)->fColor = color;
475 (*verts)->fOffset = outerVec;
476 (*verts)->fDistanceCorrection = distanceCorrection;
477 (*verts)++;
478
479 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
480 (*verts)->fColor = color;
481 (*verts)->fOffset = SkVector::Make(0, -1);
482 (*verts)->fDistanceCorrection = distanceCorrection;
483 (*verts)++;
484 }
485
486 // Add the additional vertices for overstroked rrects.
487 // Effectively this is an additional stroked rrect, with its
488 // parameters equal to those in the center of the 9-patch. This will
489 // give constant values across this inner ring.
490 if (kOverstroke_RRectType == args.fType) {
491 SkASSERT(args.fInnerRadius > 0.0f);
492
493 SkScalar inset = umbraInset + args.fInnerRadius;
494
495 // TL
496 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
497 (*verts)->fColor = color;
498 (*verts)->fOffset = SkPoint::Make(0, 0);
499 (*verts)->fDistanceCorrection = distanceCorrection;
500 (*verts)++;
501
502 // TR
503 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
504 (*verts)->fColor = color;
505 (*verts)->fOffset = SkPoint::Make(0, 0);
506 (*verts)->fDistanceCorrection = distanceCorrection;
507 (*verts)++;
508
509 // BL
510 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
511 (*verts)->fColor = color;
512 (*verts)->fOffset = SkPoint::Make(0, 0);
513 (*verts)->fDistanceCorrection = distanceCorrection;
514 (*verts)++;
515
516 // BR
517 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
518 (*verts)->fColor = color;
519 (*verts)->fOffset = SkPoint::Make(0, 0);
520 (*verts)->fDistanceCorrection = distanceCorrection;
521 (*verts)++;
522 }
523
Jim Van Verthc5903412016-11-17 15:27:09 -0500524 }
525
Robert Phillips2669a7b2020-03-12 12:07:19 -0400526 GrProgramInfo* programInfo() override { return fProgramInfo; }
527
Robert Phillips6941f4a2020-03-12 09:41:54 -0400528 void onCreateProgramInfo(const GrCaps* caps,
529 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400530 const GrSurfaceProxyView* writeView,
Robert Phillips6941f4a2020-03-12 09:41:54 -0400531 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400532 const GrXferProcessor::DstProxyView& dstProxyView,
533 GrXferBarrierFlags renderPassXferBarriers) override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400534 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView);
535 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
536
Brian Salomon8afde5f2020-04-01 16:22:00 -0400537 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
Robert Phillips6941f4a2020-03-12 09:41:54 -0400538 std::move(appliedClip),
539 dstProxyView, gp,
540 GrProcessorSet::MakeEmptySet(),
541 GrPrimitiveType::kTriangles,
Greg Danield358cbe2020-09-11 09:33:54 -0400542 renderPassXferBarriers,
Robert Phillips6941f4a2020-03-12 09:41:54 -0400543 GrPipeline::InputFlags::kNone,
Chris Dalton765ed362020-03-16 17:34:44 -0600544 &GrUserStencilSettings::kUnused);
Robert Phillips6941f4a2020-03-12 09:41:54 -0400545 }
546
Brian Salomon91326c32017-08-09 16:02:19 -0400547 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500548 int instanceCount = fGeoData.count();
Jim Van Verthc5903412016-11-17 15:27:09 -0500549
Brian Salomon12d22642019-01-29 14:38:50 -0500550 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500551 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400552 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
553 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500554 if (!verts) {
555 SkDebugf("Could not allocate vertices\n");
556 return;
557 }
558
Brian Salomon12d22642019-01-29 14:38:50 -0500559 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500560 int firstIndex = 0;
561 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
562 if (!indices) {
563 SkDebugf("Could not allocate indices\n");
564 return;
565 }
566
567 int currStartVertex = 0;
568 for (int i = 0; i < instanceCount; i++) {
569 const Geometry& args = fGeoData[i];
570
Jim Van Verth57061ee2017-04-28 17:30:30 -0400571 if (args.fIsCircle) {
572 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
573 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500574
Jim Van Verth57061ee2017-04-28 17:30:30 -0400575 const uint16_t* primIndices = circle_type_to_indices(isStroked);
576 const int primIndexCount = circle_type_to_index_count(isStroked);
577 for (int i = 0; i < primIndexCount; ++i) {
578 *indices++ = primIndices[i] + currStartVertex;
579 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500580
Jim Van Verth57061ee2017-04-28 17:30:30 -0400581 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500582
Jim Van Verth57061ee2017-04-28 17:30:30 -0400583 } else {
584 this->fillInRRectVerts(args, &verts);
585
586 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
587 const int primIndexCount = rrect_type_to_index_count(args.fType);
588 for (int i = 0; i < primIndexCount; ++i) {
589 *indices++ = primIndices[i] + currStartVertex;
590 }
591
592 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400593 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500594 }
595
Robert Phillips6941f4a2020-03-12 09:41:54 -0400596 fMesh = target->allocMesh();
597 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -0600598 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700599 }
600
601 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400602 if (!fProgramInfo) {
603 this->createProgramInfo(flushState);
604 }
Robert Phillips3968fcb2019-12-05 16:40:31 -0500605
Robert Phillips6941f4a2020-03-12 09:41:54 -0400606 if (!fProgramInfo || !fMesh) {
607 return;
608 }
609
Chris Dalton765ed362020-03-16 17:34:44 -0600610 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
611 flushState->bindTextures(fProgramInfo->primProc(), *fFalloffView.proxy(),
612 fProgramInfo->pipeline());
613 flushState->drawMesh(*fMesh);
Jim Van Verthc5903412016-11-17 15:27:09 -0500614 }
615
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500616 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
617 const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500618 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500619 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500620 fVertCount += that->fVertCount;
621 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000622 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500623 }
624
John Stilesaf366522020-08-13 09:57:34 -0400625#if GR_TEST_UTILS
626 SkString onDumpInfo() const override {
627 SkString string;
628 for (int i = 0; i < fGeoData.count(); ++i) {
629 string.appendf(
630 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
631 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
632 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
633 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
634 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
635 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
636 }
637 return string;
638 }
639#endif
640
Jim Van Verth7da048b2019-10-29 13:28:14 -0400641 void visitProxies(const VisitProxyFunc& func) const override {
Brian Salomon7e67dca2020-07-21 09:27:25 -0400642 func(fFalloffView.proxy(), GrMipmapped(false));
Robert Phillips6941f4a2020-03-12 09:41:54 -0400643 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -0600644 fProgramInfo->visitFPProxies(func);
Robert Phillips6941f4a2020-03-12 09:41:54 -0400645 }
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
John Stiles7571f9e2020-09-02 22:42:33 -0400656 using INHERITED = GrMeshDrawOp;
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
Brian Salomondf1bd6d2020-03-26 20:37:01 -0400673 if (sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(key)) {
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500674 GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(),
675 GrColorType::kAlpha_8);
676 return {std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle};
Jim Van Verth7da048b2019-10-29 13:28:14 -0400677 }
678
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500679 static const int kWidth = 128;
680 static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
681 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
682
683 SkBitmap bitmap;
684 bitmap.allocPixels(ii, kRowBytes);
685
686 unsigned char* values = (unsigned char*)bitmap.getPixels();
687 for (int i = 0; i < 128; ++i) {
688 SkScalar d = SK_Scalar1 - i / SkIntToScalar(127);
689 values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255);
690 }
691 bitmap.setImmutable();
692
Brian Salomonbc074a62020-03-18 10:06:13 -0400693 GrBitmapTextureMaker maker(context, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
Brian Salomon7e67dca2020-07-21 09:27:25 -0400694 auto view = maker.view(GrMipmapped::kNo);
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500695 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
696
697 if (view) {
698 proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
699 }
700 return view;
Jim Van Verth7da048b2019-10-29 13:28:14 -0400701}
702
703
Robert Phillipsb97da532019-02-12 15:24:12 -0500704std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400705 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400706 const SkMatrix& viewMatrix,
707 const SkRRect& rrect,
708 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400709 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500710 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500711 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400712
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500713 GrSurfaceProxyView falloffView = create_falloff_texture(context);
714 if (!falloffView) {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400715 return nullptr;
716 }
717
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500718 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500719 const SkRect& rrectBounds = rrect.getBounds();
720 SkRect bounds;
721 viewMatrix.mapRect(&bounds, rrectBounds);
722
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400723 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500724 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400725 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
726 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
727 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500728
Robert Phillipse5763782019-04-17 14:38:24 -0400729 if (scaledInsetWidth <= 0) {
730 return nullptr;
731 }
732
Robert Phillips9da87e02019-02-04 13:26:26 -0500733 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400734
735 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
736 scaledRadius,
737 rrect.isOval(),
738 blurWidth,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400739 scaledInsetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500740 std::move(falloffView));
Jim Van Verthc5903412016-11-17 15:27:09 -0500741}
John Stilesa6841be2020-08-06 14:11:56 -0400742} // namespace GrShadowRRectOp
Jim Van Verth57061ee2017-04-28 17:30:30 -0400743
Jim Van Verthc5903412016-11-17 15:27:09 -0500744///////////////////////////////////////////////////////////////////////////////
745
Hal Canary6f6961e2017-01-31 13:50:44 -0500746#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500747
Brian Salomon05969092017-07-13 11:20:51 -0400748GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Brian Salomonfc118442019-11-22 19:09:27 -0500749 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
750 // an acceptable combination.
Brian Osman4462c042018-06-08 16:35:44 -0400751 do {
Brian Salomonfc118442019-11-22 19:09:27 -0500752 // create a similarity matrix
753 SkScalar rotate = random->nextSScalar1() * 360.f;
754 SkScalar translateX = random->nextSScalar1() * 1000.f;
755 SkScalar translateY = random->nextSScalar1() * 1000.f;
756 SkScalar scale = random->nextSScalar1() * 100.f;
757 SkMatrix viewMatrix;
758 viewMatrix.setRotate(rotate);
759 viewMatrix.postTranslate(translateX, translateY);
760 viewMatrix.postScale(scale, scale);
761 SkScalar insetWidth = random->nextSScalar1() * 72.f;
762 SkScalar blurWidth = random->nextSScalar1() * 72.f;
763 bool isCircle = random->nextBool();
764 // This op doesn't use a full GrPaint, just a color.
765 GrColor color = paint.getColor4f().toBytes_RGBA();
766 if (isCircle) {
767 SkRect circle = GrTest::TestSquare(random);
768 SkRRect rrect = SkRRect::MakeOval(circle);
769 if (auto op = GrShadowRRectOp::Make(
770 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
771 return op;
772 }
773 } else {
774 SkRRect rrect;
775 do {
776 // This may return a rrect with elliptical corners, which will cause an assert.
777 rrect = GrTest::TestRRectSimple(random);
778 } while (!SkRRectPriv::IsSimpleCircular(rrect));
779 if (auto op = GrShadowRRectOp::Make(
780 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
781 return op;
782 }
783 }
784 } while (true);
Jim Van Verthc5903412016-11-17 15:27:09 -0500785}
786
787#endif