blob: 0e955f5724963a0d01039cb7ea22d23bda168c81 [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"
Robert Phillipsd464feb2020-10-08 11:00:02 -040019#include "src/gpu/GrThreadSafeCache.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "src/gpu/effects/GrShadowGeoProc.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050021#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Jim Van Verthc5903412016-11-17 15:27:09 -050022
23///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040024// Circle Data
25//
Jim Van Verthc5903412016-11-17 15:27:09 -050026// We have two possible cases for geometry for a circle:
27
28// In the case of a normal fill, we draw geometry for the circle as an octagon.
29static const uint16_t gFillCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050030 // enter the octagon
31 // clang-format off
32 0, 1, 8, 1, 2, 8,
33 2, 3, 8, 3, 4, 8,
34 4, 5, 8, 5, 6, 8,
35 6, 7, 8, 7, 0, 8,
36 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050037};
38
39// For stroked circles, we use two nested octagons.
40static const uint16_t gStrokeCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050041 // enter the octagon
42 // clang-format off
43 0, 1, 9, 0, 9, 8,
44 1, 2, 10, 1, 10, 9,
45 2, 3, 11, 2, 11, 10,
46 3, 4, 12, 3, 12, 11,
47 4, 5, 13, 4, 13, 12,
48 5, 6, 14, 5, 14, 13,
49 6, 7, 15, 6, 15, 14,
50 7, 0, 8, 7, 8, 15,
51 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050052};
53
54static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
55static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
56static const int kVertsPerStrokeCircle = 16;
57static const int kVertsPerFillCircle = 9;
58
59static int circle_type_to_vert_count(bool stroked) {
60 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
61}
62
63static int circle_type_to_index_count(bool stroked) {
64 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
65}
66
67static const uint16_t* circle_type_to_indices(bool stroked) {
68 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
69}
70
71///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040072// RoundRect Data
73//
Jim Van Verthb6069df2017-04-28 11:00:35 -040074// The geometry for a shadow roundrect is similar to a 9-patch:
Jim Van Verthc5903412016-11-17 15:27:09 -050075// ____________
76// |_|________|_|
77// | | | |
78// | | | |
79// | | | |
80// |_|________|_|
81// |_|________|_|
82//
Jim Van Verthb6069df2017-04-28 11:00:35 -040083// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
84// shows the upper part of the upper left corner. The bottom triangle would similarly be split
85// into two triangles.)
86// ________
87// |\ \ |
88// | \ \ |
89// | \\ |
90// | \|
91// --------
92//
93// The center of the fan handles the curve of the corner. For roundrects where the stroke width
94// is greater than the corner radius, the outer triangles blend from the curve to the straight
95// sides. Otherwise these triangles will be degenerate.
96//
97// In the case where the stroke width is greater than the corner radius and the
98// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
99// This rectangle extends the coverage values of the center edges of the 9-patch.
Jim Van Verthc5903412016-11-17 15:27:09 -0500100// ____________
101// |_|________|_|
102// | |\ ____ /| |
103// | | | | | |
104// | | |____| | |
105// |_|/______\|_|
106// |_|________|_|
107//
Jim Van Verthb6069df2017-04-28 11:00:35 -0400108// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
Jim Van Verthc5903412016-11-17 15:27:09 -0500109
Jim Van Verthb6069df2017-04-28 11:00:35 -0400110static const uint16_t gRRectIndices[] = {
111 // clang-format off
112 // overstroke quads
113 // we place this at the beginning so that we can skip these indices when rendering as filled
114 0, 6, 25, 0, 25, 24,
115 6, 18, 27, 6, 27, 25,
116 18, 12, 26, 18, 26, 27,
117 12, 0, 24, 12, 24, 26,
Jim Van Verthc5903412016-11-17 15:27:09 -0500118
Jim Van Verthb6069df2017-04-28 11:00:35 -0400119 // corners
120 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
121 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
122 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
123 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
Jim Van Verthc5903412016-11-17 15:27:09 -0500124
Jim Van Verthb6069df2017-04-28 11:00:35 -0400125 // edges
126 0, 5, 11, 0, 11, 6,
127 6, 7, 19, 6, 19, 18,
128 18, 23, 17, 18, 17, 12,
129 12, 13, 1, 12, 1, 0,
130
131 // fill quad
132 // we place this at the end so that we can skip these indices when rendering as stroked
133 0, 6, 18, 0, 18, 12,
134 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -0500135};
Jim Van Verthc5903412016-11-17 15:27:09 -0500136
137// overstroke count
Jim Van Verthb6069df2017-04-28 11:00:35 -0400138static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
Jim Van Verthc5903412016-11-17 15:27:09 -0500139// simple stroke count skips overstroke indices
Jim Van Verthb6069df2017-04-28 11:00:35 -0400140static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
141// fill count adds final quad to stroke count
142static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
143static const int kVertsPerStrokeRRect = 24;
144static const int kVertsPerOverstrokeRRect = 28;
145static const int kVertsPerFillRRect = 24;
Jim Van Verthc5903412016-11-17 15:27:09 -0500146
147enum RRectType {
148 kFill_RRectType,
149 kStroke_RRectType,
150 kOverstroke_RRectType,
151};
152
153static int rrect_type_to_vert_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500154 switch (type) {
155 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400156 return kVertsPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500157 case kStroke_RRectType:
158 return kVertsPerStrokeRRect;
159 case kOverstroke_RRectType:
160 return kVertsPerOverstrokeRRect;
161 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400162 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500163}
164
165static int rrect_type_to_index_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500166 switch (type) {
167 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400168 return kIndicesPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500169 case kStroke_RRectType:
170 return kIndicesPerStrokeRRect;
171 case kOverstroke_RRectType:
172 return kIndicesPerOverstrokeRRect;
173 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400174 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500175}
176
177static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500178 switch (type) {
179 case kFill_RRectType:
Brian Salomonfc527d22016-12-14 21:07:01 -0500180 case kStroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400181 return gRRectIndices + 6*4;
Brian Salomonfc527d22016-12-14 21:07:01 -0500182 case kOverstroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400183 return gRRectIndices;
Brian Salomonfc527d22016-12-14 21:07:01 -0500184 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400185 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500186}
187
Jim Van Verth57061ee2017-04-28 17:30:30 -0400188///////////////////////////////////////////////////////////////////////////////
Brian Salomon05969092017-07-13 11:20:51 -0400189namespace {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400190
Brian Salomon05969092017-07-13 11:20:51 -0400191class ShadowCircularRRectOp final : public GrMeshDrawOp {
Jim Van Verthc5903412016-11-17 15:27:09 -0500192public:
Brian Salomon25a88092016-12-01 09:36:50 -0500193 DEFINE_OP_CLASS_ID
Jim Van Verthc5903412016-11-17 15:27:09 -0500194
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400195 // An insetWidth > 1/2 rect width or height indicates a simple fill.
Brian Salomon05969092017-07-13 11:20:51 -0400196 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400197 float devRadius, bool isCircle, float blurRadius, float insetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500198 GrSurfaceProxyView falloffView)
Jim Van Verth7da048b2019-10-29 13:28:14 -0400199 : INHERITED(ClassID())
Greg Danielad994cd2019-12-10 09:35:16 -0500200 , fFalloffView(std::move(falloffView)) {
Jim Van Verthc5903412016-11-17 15:27:09 -0500201 SkRect bounds = devRect;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400202 SkASSERT(insetWidth > 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400203 SkScalar innerRadius = 0.0f;
Jim Van Verthc5903412016-11-17 15:27:09 -0500204 SkScalar outerRadius = devRadius;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400205 SkScalar umbraInset;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400206
207 RRectType type = kFill_RRectType;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400208 if (isCircle) {
209 umbraInset = 0;
210 } else {
Brian Osman788b9162020-02-07 10:36:46 -0500211 umbraInset = std::max(outerRadius, blurRadius);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400212 }
213
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400214 // If stroke is greater than width or height, this is still a fill,
215 // otherwise we compute stroke params.
216 if (isCircle) {
217 innerRadius = devRadius - insetWidth;
218 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
219 } else {
Brian Osman788b9162020-02-07 10:36:46 -0500220 if (insetWidth <= 0.5f*std::min(devRect.width(), devRect.height())) {
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400221 // We don't worry about a real inner radius, we just need to know if we
222 // need to create overstroke vertices.
Brian Osman788b9162020-02-07 10:36:46 -0500223 innerRadius = std::max(insetWidth - umbraInset, 0.0f);
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400224 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400225 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500226 }
227
Greg Daniel5faf4742019-10-01 15:14:44 -0400228 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
Jim Van Verthc5903412016-11-17 15:27:09 -0500229
Jim Van Verth57061ee2017-04-28 17:30:30 -0400230 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
Jim Van Verthfb186392018-09-11 11:37:46 -0400231 blurRadius, bounds, type, isCircle});
Jim Van Verth57061ee2017-04-28 17:30:30 -0400232 if (isCircle) {
233 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
234 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
235 } else {
236 fVertCount = rrect_type_to_vert_count(type);
237 fIndexCount = rrect_type_to_index_count(type);
238 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500239 }
240
Brian Salomonfc527d22016-12-14 21:07:01 -0500241 const char* name() const override { return "ShadowCircularRRectOp"; }
Jim Van Verthc5903412016-11-17 15:27:09 -0500242
Brian Salomon05969092017-07-13 11:20:51 -0400243 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
244
Chris Dalton6ce447a2019-06-23 18:07:38 -0600245 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
246 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700247 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400248 }
249
Brian Salomon92aee3d2016-12-21 09:20:25 -0500250private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400251 struct Geometry {
252 GrColor fColor;
253 SkScalar fOuterRadius;
254 SkScalar fUmbraInset;
255 SkScalar fInnerRadius;
256 SkScalar fBlurRadius;
257 SkRect fDevBounds;
258 RRectType fType;
259 bool fIsCircle;
260 };
261
Jim Van Verthc5903412016-11-17 15:27:09 -0500262 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500263 SkPoint fPos;
264 GrColor fColor;
265 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400266 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500267 };
268
Jim Van Verth57061ee2017-04-28 17:30:30 -0400269 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
270
271 GrColor color = args.fColor;
272 SkScalar outerRadius = args.fOuterRadius;
273 SkScalar innerRadius = args.fInnerRadius;
274 SkScalar blurRadius = args.fBlurRadius;
275 SkScalar distanceCorrection = outerRadius / blurRadius;
276
277 const SkRect& bounds = args.fDevBounds;
278
279 // The inner radius in the vertex data must be specified in normalized space.
280 innerRadius = innerRadius / outerRadius;
281
282 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
283 SkScalar halfWidth = 0.5f * bounds.width();
284 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
285
286 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500287 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400288 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400289 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500290 (*verts)++;
291
Jim Van Verth57061ee2017-04-28 17:30:30 -0400292 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500293 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400294 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400295 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500296 (*verts)++;
297
Jim Van Verth57061ee2017-04-28 17:30:30 -0400298 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500299 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400300 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400301 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500302 (*verts)++;
303
Jim Van Verth57061ee2017-04-28 17:30:30 -0400304 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500305 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400306 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400307 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500308 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400309
310 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
311 (*verts)->fColor = color;
312 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
313 (*verts)->fDistanceCorrection = distanceCorrection;
314 (*verts)++;
315
316 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
317 (*verts)->fColor = color;
318 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
319 (*verts)->fDistanceCorrection = distanceCorrection;
320 (*verts)++;
321
322 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
323 (*verts)->fColor = color;
324 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
325 (*verts)->fDistanceCorrection = distanceCorrection;
326 (*verts)++;
327
328 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
329 (*verts)->fColor = color;
330 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
331 (*verts)->fDistanceCorrection = distanceCorrection;
332 (*verts)++;
333
334 if (isStroked) {
335 // compute the inner ring
336
337 // cosine and sine of pi/8
338 SkScalar c = 0.923579533f;
339 SkScalar s = 0.382683432f;
340 SkScalar r = args.fInnerRadius;
341
342 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
343 (*verts)->fColor = color;
344 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
345 (*verts)->fDistanceCorrection = distanceCorrection;
346 (*verts)++;
347
348 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
349 (*verts)->fColor = color;
350 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
351 (*verts)->fDistanceCorrection = distanceCorrection;
352 (*verts)++;
353
354 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
355 (*verts)->fColor = color;
356 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
357 (*verts)->fDistanceCorrection = distanceCorrection;
358 (*verts)++;
359
360 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
361 (*verts)->fColor = color;
362 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
363 (*verts)->fDistanceCorrection = distanceCorrection;
364 (*verts)++;
365
366 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
367 (*verts)->fColor = color;
368 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
369 (*verts)->fDistanceCorrection = distanceCorrection;
370 (*verts)++;
371
372 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
373 (*verts)->fColor = color;
374 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
375 (*verts)->fDistanceCorrection = distanceCorrection;
376 (*verts)++;
377
378 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
379 (*verts)->fColor = color;
380 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
381 (*verts)->fDistanceCorrection = distanceCorrection;
382 (*verts)++;
383
384 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
385 (*verts)->fColor = color;
386 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
387 (*verts)->fDistanceCorrection = distanceCorrection;
388 (*verts)++;
389 } else {
390 // filled
391 (*verts)->fPos = center;
392 (*verts)->fColor = color;
393 (*verts)->fOffset = SkPoint::Make(0, 0);
394 (*verts)->fDistanceCorrection = distanceCorrection;
395 (*verts)++;
396 }
397 }
398
399 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
400 GrColor color = args.fColor;
401 SkScalar outerRadius = args.fOuterRadius;
402
403 const SkRect& bounds = args.fDevBounds;
404
405 SkScalar umbraInset = args.fUmbraInset;
Brian Osman788b9162020-02-07 10:36:46 -0500406 SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height());
Jim Van Verth57061ee2017-04-28 17:30:30 -0400407 if (umbraInset > minDim) {
408 umbraInset = minDim;
409 }
410
411 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
412 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
413 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
414 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
415 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
416 bounds.fLeft, bounds.fRight };
417 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
418 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
419 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
420 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
421 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
422 bounds.fBottom, bounds.fBottom };
423
424 SkScalar blurRadius = args.fBlurRadius;
425
426 // In the case where we have to inset more for the umbra, our two triangles in the
427 // corner get skewed to a diamond rather than a square. To correct for that,
428 // we also skew the vectors we send to the shader that help define the circle.
429 // By doing so, we end up with a quarter circle in the corner rather than the
430 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400431
432 // This is a bit magical, but it gives us the correct results at extrema:
433 // a) umbraInset == outerRadius produces an orthogonal vector
434 // b) outerRadius == 0 produces a diagonal vector
435 // And visually the corner looks correct.
436 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400437 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400438 // We want the circle edge to fall fractionally along the diagonal at
439 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
440 //
441 // Setting the components of the diagonal offset to the following value will give us that.
442 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
443 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400444 SkScalar distanceCorrection = umbraInset / blurRadius;
445
446 // build corner by corner
447 for (int i = 0; i < 4; ++i) {
448 // inner point
449 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
450 (*verts)->fColor = color;
451 (*verts)->fOffset = SkVector::Make(0, 0);
452 (*verts)->fDistanceCorrection = distanceCorrection;
453 (*verts)++;
454
455 // outer points
456 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
457 (*verts)->fColor = color;
458 (*verts)->fOffset = SkVector::Make(0, -1);
459 (*verts)->fDistanceCorrection = distanceCorrection;
460 (*verts)++;
461
462 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
463 (*verts)->fColor = color;
464 (*verts)->fOffset = outerVec;
465 (*verts)->fDistanceCorrection = distanceCorrection;
466 (*verts)++;
467
468 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
469 (*verts)->fColor = color;
470 (*verts)->fOffset = diagVec;
471 (*verts)->fDistanceCorrection = distanceCorrection;
472 (*verts)++;
473
474 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
475 (*verts)->fColor = color;
476 (*verts)->fOffset = outerVec;
477 (*verts)->fDistanceCorrection = distanceCorrection;
478 (*verts)++;
479
480 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
481 (*verts)->fColor = color;
482 (*verts)->fOffset = SkVector::Make(0, -1);
483 (*verts)->fDistanceCorrection = distanceCorrection;
484 (*verts)++;
485 }
486
487 // Add the additional vertices for overstroked rrects.
488 // Effectively this is an additional stroked rrect, with its
489 // parameters equal to those in the center of the 9-patch. This will
490 // give constant values across this inner ring.
491 if (kOverstroke_RRectType == args.fType) {
492 SkASSERT(args.fInnerRadius > 0.0f);
493
494 SkScalar inset = umbraInset + args.fInnerRadius;
495
496 // TL
497 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
498 (*verts)->fColor = color;
499 (*verts)->fOffset = SkPoint::Make(0, 0);
500 (*verts)->fDistanceCorrection = distanceCorrection;
501 (*verts)++;
502
503 // TR
504 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
505 (*verts)->fColor = color;
506 (*verts)->fOffset = SkPoint::Make(0, 0);
507 (*verts)->fDistanceCorrection = distanceCorrection;
508 (*verts)++;
509
510 // BL
511 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
512 (*verts)->fColor = color;
513 (*verts)->fOffset = SkPoint::Make(0, 0);
514 (*verts)->fDistanceCorrection = distanceCorrection;
515 (*verts)++;
516
517 // BR
518 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
519 (*verts)->fColor = color;
520 (*verts)->fOffset = SkPoint::Make(0, 0);
521 (*verts)->fDistanceCorrection = distanceCorrection;
522 (*verts)++;
523 }
524
Jim Van Verthc5903412016-11-17 15:27:09 -0500525 }
526
Robert Phillips2669a7b2020-03-12 12:07:19 -0400527 GrProgramInfo* programInfo() override { return fProgramInfo; }
528
Robert Phillips6941f4a2020-03-12 09:41:54 -0400529 void onCreateProgramInfo(const GrCaps* caps,
530 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400531 const GrSurfaceProxyView* writeView,
Robert Phillips6941f4a2020-03-12 09:41:54 -0400532 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400533 const GrXferProcessor::DstProxyView& dstProxyView,
534 GrXferBarrierFlags renderPassXferBarriers) override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400535 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView);
536 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
537
Brian Salomon8afde5f2020-04-01 16:22:00 -0400538 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
Robert Phillips6941f4a2020-03-12 09:41:54 -0400539 std::move(appliedClip),
540 dstProxyView, gp,
541 GrProcessorSet::MakeEmptySet(),
542 GrPrimitiveType::kTriangles,
Greg Danield358cbe2020-09-11 09:33:54 -0400543 renderPassXferBarriers,
Robert Phillips6941f4a2020-03-12 09:41:54 -0400544 GrPipeline::InputFlags::kNone,
Chris Dalton765ed362020-03-16 17:34:44 -0600545 &GrUserStencilSettings::kUnused);
Robert Phillips6941f4a2020-03-12 09:41:54 -0400546 }
547
Brian Salomon91326c32017-08-09 16:02:19 -0400548 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500549 int instanceCount = fGeoData.count();
Jim Van Verthc5903412016-11-17 15:27:09 -0500550
Brian Salomon12d22642019-01-29 14:38:50 -0500551 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500552 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400553 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
554 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500555 if (!verts) {
556 SkDebugf("Could not allocate vertices\n");
557 return;
558 }
559
Brian Salomon12d22642019-01-29 14:38:50 -0500560 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500561 int firstIndex = 0;
562 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
563 if (!indices) {
564 SkDebugf("Could not allocate indices\n");
565 return;
566 }
567
568 int currStartVertex = 0;
569 for (int i = 0; i < instanceCount; i++) {
570 const Geometry& args = fGeoData[i];
571
Jim Van Verth57061ee2017-04-28 17:30:30 -0400572 if (args.fIsCircle) {
573 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
574 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500575
Jim Van Verth57061ee2017-04-28 17:30:30 -0400576 const uint16_t* primIndices = circle_type_to_indices(isStroked);
577 const int primIndexCount = circle_type_to_index_count(isStroked);
578 for (int i = 0; i < primIndexCount; ++i) {
579 *indices++ = primIndices[i] + currStartVertex;
580 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500581
Jim Van Verth57061ee2017-04-28 17:30:30 -0400582 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500583
Jim Van Verth57061ee2017-04-28 17:30:30 -0400584 } else {
585 this->fillInRRectVerts(args, &verts);
586
587 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
588 const int primIndexCount = rrect_type_to_index_count(args.fType);
589 for (int i = 0; i < primIndexCount; ++i) {
590 *indices++ = primIndices[i] + currStartVertex;
591 }
592
593 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400594 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500595 }
596
Robert Phillips6941f4a2020-03-12 09:41:54 -0400597 fMesh = target->allocMesh();
598 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -0600599 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700600 }
601
602 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips6941f4a2020-03-12 09:41:54 -0400603 if (!fProgramInfo) {
604 this->createProgramInfo(flushState);
605 }
Robert Phillips3968fcb2019-12-05 16:40:31 -0500606
Robert Phillips6941f4a2020-03-12 09:41:54 -0400607 if (!fProgramInfo || !fMesh) {
608 return;
609 }
610
Chris Dalton765ed362020-03-16 17:34:44 -0600611 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
612 flushState->bindTextures(fProgramInfo->primProc(), *fFalloffView.proxy(),
613 fProgramInfo->pipeline());
614 flushState->drawMesh(*fMesh);
Jim Van Verthc5903412016-11-17 15:27:09 -0500615 }
616
Herb Derbye25c3002020-10-27 15:57:27 -0400617 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, 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
Robert Phillips692915e2020-09-30 13:56:51 -0400665static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* rContext) {
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
Robert Phillipsd464feb2020-10-08 11:00:02 -0400671 auto threadSafeCache = rContext->priv().threadSafeCache();
Greg Daniel6f5441a2020-01-28 17:02:49 -0500672
Robert Phillipsd464feb2020-10-08 11:00:02 -0400673 GrSurfaceProxyView view = threadSafeCache->find(key);
Robert Phillips692915e2020-09-30 13:56:51 -0400674 if (view) {
675 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
676 return view;
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
Robert Phillips692915e2020-09-30 13:56:51 -0400693 GrBitmapTextureMaker maker(rContext, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
694 view = maker.view(GrMipmapped::kNo);
695 if (!view) {
696 return {};
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500697 }
Robert Phillips692915e2020-09-30 13:56:51 -0400698
Robert Phillipsd464feb2020-10-08 11:00:02 -0400699 view = threadSafeCache->add(key, view);
Robert Phillips692915e2020-09-30 13:56:51 -0400700 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
Greg Daniel9f0dfbd2020-02-10 11:47:11 -0500701 return view;
Jim Van Verth7da048b2019-10-29 13:28:14 -0400702}
703
704
Herb Derbyc76d4092020-10-07 16:46:15 -0400705GrOp::Owner Make(GrRecordingContext* context,
706 GrColor color,
707 const SkMatrix& viewMatrix,
708 const SkRRect& rrect,
709 SkScalar blurWidth,
710 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
Herb Derbyc76d4092020-10-07 16:46:15 -0400734 return GrOp::Make<ShadowCircularRRectOp>(context,
735 color,
736 bounds,
737 scaledRadius,
738 rrect.isOval(),
739 blurWidth,
740 scaledInsetWidth,
741 std::move(falloffView));
Jim Van Verthc5903412016-11-17 15:27:09 -0500742}
John Stilesa6841be2020-08-06 14:11:56 -0400743} // namespace GrShadowRRectOp
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