blob: a9e7b7239cd12bd396fc55bd79dfdba7d576e8a8 [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"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrMemoryPool.h"
14#include "src/gpu/GrOpFlushState.h"
Jim Van Verth7da048b2019-10-29 13:28:14 -040015#include "src/gpu/GrProxyProvider.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/gpu/GrRecordingContextPriv.h"
17#include "src/gpu/effects/GrShadowGeoProc.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050018#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Jim Van Verthc5903412016-11-17 15:27:09 -050019
20///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040021// Circle Data
22//
Jim Van Verthc5903412016-11-17 15:27:09 -050023// We have two possible cases for geometry for a circle:
24
25// In the case of a normal fill, we draw geometry for the circle as an octagon.
26static const uint16_t gFillCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050027 // enter the octagon
28 // clang-format off
29 0, 1, 8, 1, 2, 8,
30 2, 3, 8, 3, 4, 8,
31 4, 5, 8, 5, 6, 8,
32 6, 7, 8, 7, 0, 8,
33 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050034};
35
36// For stroked circles, we use two nested octagons.
37static const uint16_t gStrokeCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050038 // enter the octagon
39 // clang-format off
40 0, 1, 9, 0, 9, 8,
41 1, 2, 10, 1, 10, 9,
42 2, 3, 11, 2, 11, 10,
43 3, 4, 12, 3, 12, 11,
44 4, 5, 13, 4, 13, 12,
45 5, 6, 14, 5, 14, 13,
46 6, 7, 15, 6, 15, 14,
47 7, 0, 8, 7, 8, 15,
48 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050049};
50
51static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
52static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
53static const int kVertsPerStrokeCircle = 16;
54static const int kVertsPerFillCircle = 9;
55
56static int circle_type_to_vert_count(bool stroked) {
57 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
58}
59
60static int circle_type_to_index_count(bool stroked) {
61 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
62}
63
64static const uint16_t* circle_type_to_indices(bool stroked) {
65 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
66}
67
68///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040069// RoundRect Data
70//
Jim Van Verthb6069df2017-04-28 11:00:35 -040071// The geometry for a shadow roundrect is similar to a 9-patch:
Jim Van Verthc5903412016-11-17 15:27:09 -050072// ____________
73// |_|________|_|
74// | | | |
75// | | | |
76// | | | |
77// |_|________|_|
78// |_|________|_|
79//
Jim Van Verthb6069df2017-04-28 11:00:35 -040080// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
81// shows the upper part of the upper left corner. The bottom triangle would similarly be split
82// into two triangles.)
83// ________
84// |\ \ |
85// | \ \ |
86// | \\ |
87// | \|
88// --------
89//
90// The center of the fan handles the curve of the corner. For roundrects where the stroke width
91// is greater than the corner radius, the outer triangles blend from the curve to the straight
92// sides. Otherwise these triangles will be degenerate.
93//
94// In the case where the stroke width is greater than the corner radius and the
95// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
96// This rectangle extends the coverage values of the center edges of the 9-patch.
Jim Van Verthc5903412016-11-17 15:27:09 -050097// ____________
98// |_|________|_|
99// | |\ ____ /| |
100// | | | | | |
101// | | |____| | |
102// |_|/______\|_|
103// |_|________|_|
104//
Jim Van Verthb6069df2017-04-28 11:00:35 -0400105// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
Jim Van Verthc5903412016-11-17 15:27:09 -0500106
Jim Van Verthb6069df2017-04-28 11:00:35 -0400107static const uint16_t gRRectIndices[] = {
108 // clang-format off
109 // overstroke quads
110 // we place this at the beginning so that we can skip these indices when rendering as filled
111 0, 6, 25, 0, 25, 24,
112 6, 18, 27, 6, 27, 25,
113 18, 12, 26, 18, 26, 27,
114 12, 0, 24, 12, 24, 26,
Jim Van Verthc5903412016-11-17 15:27:09 -0500115
Jim Van Verthb6069df2017-04-28 11:00:35 -0400116 // corners
117 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
118 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
119 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
120 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
Jim Van Verthc5903412016-11-17 15:27:09 -0500121
Jim Van Verthb6069df2017-04-28 11:00:35 -0400122 // edges
123 0, 5, 11, 0, 11, 6,
124 6, 7, 19, 6, 19, 18,
125 18, 23, 17, 18, 17, 12,
126 12, 13, 1, 12, 1, 0,
127
128 // fill quad
129 // we place this at the end so that we can skip these indices when rendering as stroked
130 0, 6, 18, 0, 18, 12,
131 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -0500132};
Jim Van Verthc5903412016-11-17 15:27:09 -0500133
134// overstroke count
Jim Van Verthb6069df2017-04-28 11:00:35 -0400135static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
Jim Van Verthc5903412016-11-17 15:27:09 -0500136// simple stroke count skips overstroke indices
Jim Van Verthb6069df2017-04-28 11:00:35 -0400137static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
138// fill count adds final quad to stroke count
139static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
140static const int kVertsPerStrokeRRect = 24;
141static const int kVertsPerOverstrokeRRect = 28;
142static const int kVertsPerFillRRect = 24;
Jim Van Verthc5903412016-11-17 15:27:09 -0500143
144enum RRectType {
145 kFill_RRectType,
146 kStroke_RRectType,
147 kOverstroke_RRectType,
148};
149
150static int rrect_type_to_vert_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500151 switch (type) {
152 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400153 return kVertsPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500154 case kStroke_RRectType:
155 return kVertsPerStrokeRRect;
156 case kOverstroke_RRectType:
157 return kVertsPerOverstrokeRRect;
158 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400159 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500160}
161
162static int rrect_type_to_index_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500163 switch (type) {
164 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400165 return kIndicesPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500166 case kStroke_RRectType:
167 return kIndicesPerStrokeRRect;
168 case kOverstroke_RRectType:
169 return kIndicesPerOverstrokeRRect;
170 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400171 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500172}
173
174static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500175 switch (type) {
176 case kFill_RRectType:
Brian Salomonfc527d22016-12-14 21:07:01 -0500177 case kStroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400178 return gRRectIndices + 6*4;
Brian Salomonfc527d22016-12-14 21:07:01 -0500179 case kOverstroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400180 return gRRectIndices;
Brian Salomonfc527d22016-12-14 21:07:01 -0500181 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400182 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500183}
184
Jim Van Verth57061ee2017-04-28 17:30:30 -0400185///////////////////////////////////////////////////////////////////////////////
Brian Salomon05969092017-07-13 11:20:51 -0400186namespace {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400187
Brian Salomon05969092017-07-13 11:20:51 -0400188class ShadowCircularRRectOp final : public GrMeshDrawOp {
Jim Van Verthc5903412016-11-17 15:27:09 -0500189public:
Brian Salomon25a88092016-12-01 09:36:50 -0500190 DEFINE_OP_CLASS_ID
Jim Van Verthc5903412016-11-17 15:27:09 -0500191
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400192 // An insetWidth > 1/2 rect width or height indicates a simple fill.
Brian Salomon05969092017-07-13 11:20:51 -0400193 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400194 float devRadius, bool isCircle, float blurRadius, float insetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500195 GrSurfaceProxyView falloffView)
Jim Van Verth7da048b2019-10-29 13:28:14 -0400196 : INHERITED(ClassID())
Greg Danielad994cd2019-12-10 09:35:16 -0500197 , fFalloffView(std::move(falloffView)) {
Jim Van Verthc5903412016-11-17 15:27:09 -0500198 SkRect bounds = devRect;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400199 SkASSERT(insetWidth > 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400200 SkScalar innerRadius = 0.0f;
Jim Van Verthc5903412016-11-17 15:27:09 -0500201 SkScalar outerRadius = devRadius;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400202 SkScalar umbraInset;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400203
204 RRectType type = kFill_RRectType;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400205 if (isCircle) {
206 umbraInset = 0;
207 } else {
208 umbraInset = SkTMax(outerRadius, blurRadius);
209 }
210
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400211 // If stroke is greater than width or height, this is still a fill,
212 // otherwise we compute stroke params.
213 if (isCircle) {
214 innerRadius = devRadius - insetWidth;
215 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
216 } else {
217 if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
218 // We don't worry about a real inner radius, we just need to know if we
219 // need to create overstroke vertices.
220 innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
221 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400222 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500223 }
224
Greg Daniel5faf4742019-10-01 15:14:44 -0400225 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
Jim Van Verthc5903412016-11-17 15:27:09 -0500226
Jim Van Verth57061ee2017-04-28 17:30:30 -0400227 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
Jim Van Verthfb186392018-09-11 11:37:46 -0400228 blurRadius, bounds, type, isCircle});
Jim Van Verth57061ee2017-04-28 17:30:30 -0400229 if (isCircle) {
230 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
231 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
232 } else {
233 fVertCount = rrect_type_to_vert_count(type);
234 fIndexCount = rrect_type_to_index_count(type);
235 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500236 }
237
Brian Salomonfc527d22016-12-14 21:07:01 -0500238 const char* name() const override { return "ShadowCircularRRectOp"; }
Jim Van Verthc5903412016-11-17 15:27:09 -0500239
Brian Osman9a390ac2018-11-12 09:47:48 -0500240#ifdef SK_DEBUG
Jim Van Verthc5903412016-11-17 15:27:09 -0500241 SkString dumpInfo() const override {
242 SkString string;
243 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500244 string.appendf(
245 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
Jim Van Verth57061ee2017-04-28 17:30:30 -0400246 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
Brian Salomonfc527d22016-12-14 21:07:01 -0500247 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
248 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
Jim Van Verthb6069df2017-04-28 11:00:35 -0400249 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
Jim Van Verth57061ee2017-04-28 17:30:30 -0400250 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
Jim Van Verthc5903412016-11-17 15:27:09 -0500251 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500252 string.append(INHERITED::dumpInfo());
253 return string;
254 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500255#endif
Jim Van Verthc5903412016-11-17 15:27:09 -0500256
Brian Salomon05969092017-07-13 11:20:51 -0400257 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
258
Chris Dalton6ce447a2019-06-23 18:07:38 -0600259 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
260 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700261 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400262 }
263
Brian Salomon92aee3d2016-12-21 09:20:25 -0500264private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400265 struct Geometry {
266 GrColor fColor;
267 SkScalar fOuterRadius;
268 SkScalar fUmbraInset;
269 SkScalar fInnerRadius;
270 SkScalar fBlurRadius;
271 SkRect fDevBounds;
272 RRectType fType;
273 bool fIsCircle;
274 };
275
Jim Van Verthc5903412016-11-17 15:27:09 -0500276 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500277 SkPoint fPos;
278 GrColor fColor;
279 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400280 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500281 };
282
Jim Van Verth57061ee2017-04-28 17:30:30 -0400283 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
284
285 GrColor color = args.fColor;
286 SkScalar outerRadius = args.fOuterRadius;
287 SkScalar innerRadius = args.fInnerRadius;
288 SkScalar blurRadius = args.fBlurRadius;
289 SkScalar distanceCorrection = outerRadius / blurRadius;
290
291 const SkRect& bounds = args.fDevBounds;
292
293 // The inner radius in the vertex data must be specified in normalized space.
294 innerRadius = innerRadius / outerRadius;
295
296 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
297 SkScalar halfWidth = 0.5f * bounds.width();
298 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
299
300 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500301 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400302 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400303 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500304 (*verts)++;
305
Jim Van Verth57061ee2017-04-28 17:30:30 -0400306 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500307 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400308 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400309 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500310 (*verts)++;
311
Jim Van Verth57061ee2017-04-28 17:30:30 -0400312 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500313 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400314 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400315 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500316 (*verts)++;
317
Jim Van Verth57061ee2017-04-28 17:30:30 -0400318 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500319 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400320 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400321 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500322 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400323
324 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
325 (*verts)->fColor = color;
326 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
327 (*verts)->fDistanceCorrection = distanceCorrection;
328 (*verts)++;
329
330 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
331 (*verts)->fColor = color;
332 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
333 (*verts)->fDistanceCorrection = distanceCorrection;
334 (*verts)++;
335
336 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
337 (*verts)->fColor = color;
338 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
339 (*verts)->fDistanceCorrection = distanceCorrection;
340 (*verts)++;
341
342 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
343 (*verts)->fColor = color;
344 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
345 (*verts)->fDistanceCorrection = distanceCorrection;
346 (*verts)++;
347
348 if (isStroked) {
349 // compute the inner ring
350
351 // cosine and sine of pi/8
352 SkScalar c = 0.923579533f;
353 SkScalar s = 0.382683432f;
354 SkScalar r = args.fInnerRadius;
355
356 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
357 (*verts)->fColor = color;
358 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
359 (*verts)->fDistanceCorrection = distanceCorrection;
360 (*verts)++;
361
362 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
363 (*verts)->fColor = color;
364 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
365 (*verts)->fDistanceCorrection = distanceCorrection;
366 (*verts)++;
367
368 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
369 (*verts)->fColor = color;
370 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
371 (*verts)->fDistanceCorrection = distanceCorrection;
372 (*verts)++;
373
374 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
375 (*verts)->fColor = color;
376 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
377 (*verts)->fDistanceCorrection = distanceCorrection;
378 (*verts)++;
379
380 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
381 (*verts)->fColor = color;
382 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
383 (*verts)->fDistanceCorrection = distanceCorrection;
384 (*verts)++;
385
386 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
387 (*verts)->fColor = color;
388 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
389 (*verts)->fDistanceCorrection = distanceCorrection;
390 (*verts)++;
391
392 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
393 (*verts)->fColor = color;
394 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
395 (*verts)->fDistanceCorrection = distanceCorrection;
396 (*verts)++;
397
398 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
399 (*verts)->fColor = color;
400 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
401 (*verts)->fDistanceCorrection = distanceCorrection;
402 (*verts)++;
403 } else {
404 // filled
405 (*verts)->fPos = center;
406 (*verts)->fColor = color;
407 (*verts)->fOffset = SkPoint::Make(0, 0);
408 (*verts)->fDistanceCorrection = distanceCorrection;
409 (*verts)++;
410 }
411 }
412
413 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
414 GrColor color = args.fColor;
415 SkScalar outerRadius = args.fOuterRadius;
416
417 const SkRect& bounds = args.fDevBounds;
418
419 SkScalar umbraInset = args.fUmbraInset;
420 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
421 if (umbraInset > minDim) {
422 umbraInset = minDim;
423 }
424
425 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
426 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
427 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
428 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
429 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
430 bounds.fLeft, bounds.fRight };
431 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
432 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
433 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
434 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
435 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
436 bounds.fBottom, bounds.fBottom };
437
438 SkScalar blurRadius = args.fBlurRadius;
439
440 // In the case where we have to inset more for the umbra, our two triangles in the
441 // corner get skewed to a diamond rather than a square. To correct for that,
442 // we also skew the vectors we send to the shader that help define the circle.
443 // By doing so, we end up with a quarter circle in the corner rather than the
444 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400445
446 // This is a bit magical, but it gives us the correct results at extrema:
447 // a) umbraInset == outerRadius produces an orthogonal vector
448 // b) outerRadius == 0 produces a diagonal vector
449 // And visually the corner looks correct.
450 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400451 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400452 // We want the circle edge to fall fractionally along the diagonal at
453 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
454 //
455 // Setting the components of the diagonal offset to the following value will give us that.
456 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
457 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400458 SkScalar distanceCorrection = umbraInset / blurRadius;
459
460 // build corner by corner
461 for (int i = 0; i < 4; ++i) {
462 // inner point
463 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
464 (*verts)->fColor = color;
465 (*verts)->fOffset = SkVector::Make(0, 0);
466 (*verts)->fDistanceCorrection = distanceCorrection;
467 (*verts)++;
468
469 // outer points
470 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
471 (*verts)->fColor = color;
472 (*verts)->fOffset = SkVector::Make(0, -1);
473 (*verts)->fDistanceCorrection = distanceCorrection;
474 (*verts)++;
475
476 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
477 (*verts)->fColor = color;
478 (*verts)->fOffset = outerVec;
479 (*verts)->fDistanceCorrection = distanceCorrection;
480 (*verts)++;
481
482 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
483 (*verts)->fColor = color;
484 (*verts)->fOffset = diagVec;
485 (*verts)->fDistanceCorrection = distanceCorrection;
486 (*verts)++;
487
488 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
489 (*verts)->fColor = color;
490 (*verts)->fOffset = outerVec;
491 (*verts)->fDistanceCorrection = distanceCorrection;
492 (*verts)++;
493
494 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
495 (*verts)->fColor = color;
496 (*verts)->fOffset = SkVector::Make(0, -1);
497 (*verts)->fDistanceCorrection = distanceCorrection;
498 (*verts)++;
499 }
500
501 // Add the additional vertices for overstroked rrects.
502 // Effectively this is an additional stroked rrect, with its
503 // parameters equal to those in the center of the 9-patch. This will
504 // give constant values across this inner ring.
505 if (kOverstroke_RRectType == args.fType) {
506 SkASSERT(args.fInnerRadius > 0.0f);
507
508 SkScalar inset = umbraInset + args.fInnerRadius;
509
510 // TL
511 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
512 (*verts)->fColor = color;
513 (*verts)->fOffset = SkPoint::Make(0, 0);
514 (*verts)->fDistanceCorrection = distanceCorrection;
515 (*verts)++;
516
517 // TR
518 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
519 (*verts)->fColor = color;
520 (*verts)->fOffset = SkPoint::Make(0, 0);
521 (*verts)->fDistanceCorrection = distanceCorrection;
522 (*verts)++;
523
524 // BL
525 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
526 (*verts)->fColor = color;
527 (*verts)->fOffset = SkPoint::Make(0, 0);
528 (*verts)->fDistanceCorrection = distanceCorrection;
529 (*verts)++;
530
531 // BR
532 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
533 (*verts)->fColor = color;
534 (*verts)->fOffset = SkPoint::Make(0, 0);
535 (*verts)->fDistanceCorrection = distanceCorrection;
536 (*verts)++;
537 }
538
Jim Van Verthc5903412016-11-17 15:27:09 -0500539 }
540
Brian Salomon91326c32017-08-09 16:02:19 -0400541 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500542 // Setup geometry processor
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500543 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(target->allocator(),
Greg Danielad994cd2019-12-10 09:35:16 -0500544 fFalloffView);
Jim Van Verthc5903412016-11-17 15:27:09 -0500545
546 int instanceCount = fGeoData.count();
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500547 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
Jim Van Verthc5903412016-11-17 15:27:09 -0500548
Brian Salomon12d22642019-01-29 14:38:50 -0500549 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500550 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400551 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
552 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500553 if (!verts) {
554 SkDebugf("Could not allocate vertices\n");
555 return;
556 }
557
Brian Salomon12d22642019-01-29 14:38:50 -0500558 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500559 int firstIndex = 0;
560 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
561 if (!indices) {
562 SkDebugf("Could not allocate indices\n");
563 return;
564 }
565
566 int currStartVertex = 0;
567 for (int i = 0; i < instanceCount; i++) {
568 const Geometry& args = fGeoData[i];
569
Jim Van Verth57061ee2017-04-28 17:30:30 -0400570 if (args.fIsCircle) {
571 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
572 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500573
Jim Van Verth57061ee2017-04-28 17:30:30 -0400574 const uint16_t* primIndices = circle_type_to_indices(isStroked);
575 const int primIndexCount = circle_type_to_index_count(isStroked);
576 for (int i = 0; i < primIndexCount; ++i) {
577 *indices++ = primIndices[i] + currStartVertex;
578 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500579
Jim Van Verth57061ee2017-04-28 17:30:30 -0400580 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500581
Jim Van Verth57061ee2017-04-28 17:30:30 -0400582 } else {
583 this->fillInRRectVerts(args, &verts);
584
585 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
586 const int primIndexCount = rrect_type_to_index_count(args.fType);
587 for (int i = 0; i < primIndexCount; ++i) {
588 *indices++ = primIndices[i] + currStartVertex;
589 }
590
591 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400592 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500593 }
594
Jim Van Verth7da048b2019-10-29 13:28:14 -0400595 auto fixedDynamicState = target->makeFixedDynamicState(1);
Greg Danielad994cd2019-12-10 09:35:16 -0500596 fixedDynamicState->fPrimitiveProcessorTextures[0] = fFalloffView.proxy();
Jim Van Verth7da048b2019-10-29 13:28:14 -0400597
Brian Salomon7eae3e02018-08-07 14:02:38 +0000598 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500599 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000600 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500601 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500602 target->recordDraw(gp, mesh, 1, fixedDynamicState, nullptr, GrPrimitiveType::kTriangles);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700603 }
604
605 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips3968fcb2019-12-05 16:40:31 -0500606 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState,
607 GrProcessorSet::MakeEmptySet(),
608 GrPipeline::InputFlags::kNone);
609
610 flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline);
Jim Van Verthc5903412016-11-17 15:27:09 -0500611 }
612
Brian Salomon7eae3e02018-08-07 14:02:38 +0000613 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500614 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500615 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500616 fVertCount += that->fVertCount;
617 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000618 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500619 }
620
Jim Van Verth7da048b2019-10-29 13:28:14 -0400621 void visitProxies(const VisitProxyFunc& func) const override {
Greg Danielad994cd2019-12-10 09:35:16 -0500622 func(fFalloffView.proxy(), GrMipMapped(false));
Jim Van Verth7da048b2019-10-29 13:28:14 -0400623 }
624
Jim Van Verthc5903412016-11-17 15:27:09 -0500625 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500626 int fVertCount;
627 int fIndexCount;
Greg Danielad994cd2019-12-10 09:35:16 -0500628 GrSurfaceProxyView fFalloffView;
Jim Van Verthc5903412016-11-17 15:27:09 -0500629
Brian Salomon05969092017-07-13 11:20:51 -0400630 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500631};
632
Brian Salomon05969092017-07-13 11:20:51 -0400633} // anonymous namespace
634
Jim Van Verthc5903412016-11-17 15:27:09 -0500635///////////////////////////////////////////////////////////////////////////////
636
Jim Van Verth57061ee2017-04-28 17:30:30 -0400637namespace GrShadowRRectOp {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400638
639static sk_sp<GrTextureProxy> create_falloff_texture(GrProxyProvider* proxyProvider) {
640 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
641 GrUniqueKey key;
642 GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
643 builder.finish();
644
645 sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(
646 key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
647 if (!falloffTexture) {
648 static const int kWidth = 128;
649 static const size_t kRowBytes = kWidth*GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
650 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
651
652 sk_sp<SkData> data = SkData::MakeUninitialized(kRowBytes);
653 if (!data) {
654 return nullptr;
655 }
656 unsigned char* values = (unsigned char*) data->writable_data();
657 for (int i = 0; i < 128; ++i) {
658 SkScalar d = SK_Scalar1 - i/SkIntToScalar(127);
659 values[i] = SkScalarRoundToInt((SkScalarExp(-4*d*d) - 0.018f)*255);
660 }
661
662 sk_sp<SkImage> img = SkImage::MakeRasterData(ii, std::move(data), kRowBytes);
663 if (!img) {
664 return nullptr;
665 }
666
667 falloffTexture = proxyProvider->createTextureProxy(std::move(img), 1, SkBudgeted::kYes,
668 SkBackingFit::kExact);
669 if (!falloffTexture) {
670 return nullptr;
671 }
672
673 SkASSERT(falloffTexture->origin() == kTopLeft_GrSurfaceOrigin);
674 proxyProvider->assignUniqueKeyToProxy(key, falloffTexture.get());
675 }
676
677 return falloffTexture;
678}
679
680
Robert Phillipsb97da532019-02-12 15:24:12 -0500681std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400682 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400683 const SkMatrix& viewMatrix,
684 const SkRRect& rrect,
685 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400686 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500687 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500688 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400689
Jim Van Verth7da048b2019-10-29 13:28:14 -0400690 sk_sp<GrTextureProxy> falloffTexture = create_falloff_texture(context->priv().proxyProvider());
691 if (!falloffTexture) {
692 return nullptr;
693 }
Greg Danielad994cd2019-12-10 09:35:16 -0500694 GrSwizzle swizzle = context->priv().caps()->getTextureSwizzle(falloffTexture->backendFormat(),
695 GrColorType::kAlpha_8);
696
697 GrSurfaceProxyView falloffView(std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle);
Jim Van Verth7da048b2019-10-29 13:28:14 -0400698
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500699 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500700 const SkRect& rrectBounds = rrect.getBounds();
701 SkRect bounds;
702 viewMatrix.mapRect(&bounds, rrectBounds);
703
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400704 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500705 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400706 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
707 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
708 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500709
Robert Phillipse5763782019-04-17 14:38:24 -0400710 if (scaledInsetWidth <= 0) {
711 return nullptr;
712 }
713
Robert Phillips9da87e02019-02-04 13:26:26 -0500714 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400715
716 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
717 scaledRadius,
718 rrect.isOval(),
719 blurWidth,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400720 scaledInsetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500721 std::move(falloffView));
Jim Van Verthc5903412016-11-17 15:27:09 -0500722}
Brian Salomonfc527d22016-12-14 21:07:01 -0500723}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400724
Jim Van Verthc5903412016-11-17 15:27:09 -0500725///////////////////////////////////////////////////////////////////////////////
726
Hal Canary6f6961e2017-01-31 13:50:44 -0500727#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500728
Brian Salomon05969092017-07-13 11:20:51 -0400729GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Brian Salomonfc118442019-11-22 19:09:27 -0500730 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
731 // an acceptable combination.
Brian Osman4462c042018-06-08 16:35:44 -0400732 do {
Brian Salomonfc118442019-11-22 19:09:27 -0500733 // create a similarity matrix
734 SkScalar rotate = random->nextSScalar1() * 360.f;
735 SkScalar translateX = random->nextSScalar1() * 1000.f;
736 SkScalar translateY = random->nextSScalar1() * 1000.f;
737 SkScalar scale = random->nextSScalar1() * 100.f;
738 SkMatrix viewMatrix;
739 viewMatrix.setRotate(rotate);
740 viewMatrix.postTranslate(translateX, translateY);
741 viewMatrix.postScale(scale, scale);
742 SkScalar insetWidth = random->nextSScalar1() * 72.f;
743 SkScalar blurWidth = random->nextSScalar1() * 72.f;
744 bool isCircle = random->nextBool();
745 // This op doesn't use a full GrPaint, just a color.
746 GrColor color = paint.getColor4f().toBytes_RGBA();
747 if (isCircle) {
748 SkRect circle = GrTest::TestSquare(random);
749 SkRRect rrect = SkRRect::MakeOval(circle);
750 if (auto op = GrShadowRRectOp::Make(
751 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
752 return op;
753 }
754 } else {
755 SkRRect rrect;
756 do {
757 // This may return a rrect with elliptical corners, which will cause an assert.
758 rrect = GrTest::TestRRectSimple(random);
759 } while (!SkRRectPriv::IsSimpleCircular(rrect));
760 if (auto op = GrShadowRRectOp::Make(
761 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
762 return op;
763 }
764 }
765 } while (true);
Jim Van Verthc5903412016-11-17 15:27:09 -0500766}
767
768#endif