blob: 6eada792fe5b8f33c75143651feff7e9546c0ce2 [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"
Jim Van Verthc5903412016-11-17 15:27:09 -050018
19///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040020// Circle Data
21//
Jim Van Verthc5903412016-11-17 15:27:09 -050022// We have two possible cases for geometry for a circle:
23
24// In the case of a normal fill, we draw geometry for the circle as an octagon.
25static const uint16_t gFillCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050026 // enter the octagon
27 // clang-format off
28 0, 1, 8, 1, 2, 8,
29 2, 3, 8, 3, 4, 8,
30 4, 5, 8, 5, 6, 8,
31 6, 7, 8, 7, 0, 8,
32 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050033};
34
35// For stroked circles, we use two nested octagons.
36static const uint16_t gStrokeCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050037 // enter the octagon
38 // clang-format off
39 0, 1, 9, 0, 9, 8,
40 1, 2, 10, 1, 10, 9,
41 2, 3, 11, 2, 11, 10,
42 3, 4, 12, 3, 12, 11,
43 4, 5, 13, 4, 13, 12,
44 5, 6, 14, 5, 14, 13,
45 6, 7, 15, 6, 15, 14,
46 7, 0, 8, 7, 8, 15,
47 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050048};
49
50static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
51static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
52static const int kVertsPerStrokeCircle = 16;
53static const int kVertsPerFillCircle = 9;
54
55static int circle_type_to_vert_count(bool stroked) {
56 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
57}
58
59static int circle_type_to_index_count(bool stroked) {
60 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
61}
62
63static const uint16_t* circle_type_to_indices(bool stroked) {
64 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
65}
66
67///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040068// RoundRect Data
69//
Jim Van Verthb6069df2017-04-28 11:00:35 -040070// The geometry for a shadow roundrect is similar to a 9-patch:
Jim Van Verthc5903412016-11-17 15:27:09 -050071// ____________
72// |_|________|_|
73// | | | |
74// | | | |
75// | | | |
76// |_|________|_|
77// |_|________|_|
78//
Jim Van Verthb6069df2017-04-28 11:00:35 -040079// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
80// shows the upper part of the upper left corner. The bottom triangle would similarly be split
81// into two triangles.)
82// ________
83// |\ \ |
84// | \ \ |
85// | \\ |
86// | \|
87// --------
88//
89// The center of the fan handles the curve of the corner. For roundrects where the stroke width
90// is greater than the corner radius, the outer triangles blend from the curve to the straight
91// sides. Otherwise these triangles will be degenerate.
92//
93// In the case where the stroke width is greater than the corner radius and the
94// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
95// This rectangle extends the coverage values of the center edges of the 9-patch.
Jim Van Verthc5903412016-11-17 15:27:09 -050096// ____________
97// |_|________|_|
98// | |\ ____ /| |
99// | | | | | |
100// | | |____| | |
101// |_|/______\|_|
102// |_|________|_|
103//
Jim Van Verthb6069df2017-04-28 11:00:35 -0400104// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
Jim Van Verthc5903412016-11-17 15:27:09 -0500105
Jim Van Verthb6069df2017-04-28 11:00:35 -0400106static const uint16_t gRRectIndices[] = {
107 // clang-format off
108 // overstroke quads
109 // we place this at the beginning so that we can skip these indices when rendering as filled
110 0, 6, 25, 0, 25, 24,
111 6, 18, 27, 6, 27, 25,
112 18, 12, 26, 18, 26, 27,
113 12, 0, 24, 12, 24, 26,
Jim Van Verthc5903412016-11-17 15:27:09 -0500114
Jim Van Verthb6069df2017-04-28 11:00:35 -0400115 // corners
116 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
117 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
118 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
119 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
Jim Van Verthc5903412016-11-17 15:27:09 -0500120
Jim Van Verthb6069df2017-04-28 11:00:35 -0400121 // edges
122 0, 5, 11, 0, 11, 6,
123 6, 7, 19, 6, 19, 18,
124 18, 23, 17, 18, 17, 12,
125 12, 13, 1, 12, 1, 0,
126
127 // fill quad
128 // we place this at the end so that we can skip these indices when rendering as stroked
129 0, 6, 18, 0, 18, 12,
130 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -0500131};
Jim Van Verthc5903412016-11-17 15:27:09 -0500132
133// overstroke count
Jim Van Verthb6069df2017-04-28 11:00:35 -0400134static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
Jim Van Verthc5903412016-11-17 15:27:09 -0500135// simple stroke count skips overstroke indices
Jim Van Verthb6069df2017-04-28 11:00:35 -0400136static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
137// fill count adds final quad to stroke count
138static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
139static const int kVertsPerStrokeRRect = 24;
140static const int kVertsPerOverstrokeRRect = 28;
141static const int kVertsPerFillRRect = 24;
Jim Van Verthc5903412016-11-17 15:27:09 -0500142
143enum RRectType {
144 kFill_RRectType,
145 kStroke_RRectType,
146 kOverstroke_RRectType,
147};
148
149static int rrect_type_to_vert_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500150 switch (type) {
151 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400152 return kVertsPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500153 case kStroke_RRectType:
154 return kVertsPerStrokeRRect;
155 case kOverstroke_RRectType:
156 return kVertsPerOverstrokeRRect;
157 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400158 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500159}
160
161static int rrect_type_to_index_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500162 switch (type) {
163 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400164 return kIndicesPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500165 case kStroke_RRectType:
166 return kIndicesPerStrokeRRect;
167 case kOverstroke_RRectType:
168 return kIndicesPerOverstrokeRRect;
169 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400170 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500171}
172
173static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500174 switch (type) {
175 case kFill_RRectType:
Brian Salomonfc527d22016-12-14 21:07:01 -0500176 case kStroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400177 return gRRectIndices + 6*4;
Brian Salomonfc527d22016-12-14 21:07:01 -0500178 case kOverstroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400179 return gRRectIndices;
Brian Salomonfc527d22016-12-14 21:07:01 -0500180 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400181 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500182}
183
Jim Van Verth57061ee2017-04-28 17:30:30 -0400184///////////////////////////////////////////////////////////////////////////////
Brian Salomon05969092017-07-13 11:20:51 -0400185namespace {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400186
Brian Salomon05969092017-07-13 11:20:51 -0400187class ShadowCircularRRectOp final : public GrMeshDrawOp {
Jim Van Verthc5903412016-11-17 15:27:09 -0500188public:
Brian Salomon25a88092016-12-01 09:36:50 -0500189 DEFINE_OP_CLASS_ID
Jim Van Verthc5903412016-11-17 15:27:09 -0500190
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400191 // An insetWidth > 1/2 rect width or height indicates a simple fill.
Brian Salomon05969092017-07-13 11:20:51 -0400192 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400193 float devRadius, bool isCircle, float blurRadius, float insetWidth,
194 sk_sp<GrTextureProxy> falloffProxy)
195 : INHERITED(ClassID())
196 , fFalloffProxy(falloffProxy) {
Jim Van Verthc5903412016-11-17 15:27:09 -0500197 SkRect bounds = devRect;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400198 SkASSERT(insetWidth > 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400199 SkScalar innerRadius = 0.0f;
Jim Van Verthc5903412016-11-17 15:27:09 -0500200 SkScalar outerRadius = devRadius;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400201 SkScalar umbraInset;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400202
203 RRectType type = kFill_RRectType;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400204 if (isCircle) {
205 umbraInset = 0;
206 } else {
207 umbraInset = SkTMax(outerRadius, blurRadius);
208 }
209
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400210 // If stroke is greater than width or height, this is still a fill,
211 // otherwise we compute stroke params.
212 if (isCircle) {
213 innerRadius = devRadius - insetWidth;
214 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
215 } else {
216 if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
217 // We don't worry about a real inner radius, we just need to know if we
218 // need to create overstroke vertices.
219 innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
220 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400221 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500222 }
223
Greg Daniel5faf4742019-10-01 15:14:44 -0400224 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
Jim Van Verthc5903412016-11-17 15:27:09 -0500225
Jim Van Verth57061ee2017-04-28 17:30:30 -0400226 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
Jim Van Verthfb186392018-09-11 11:37:46 -0400227 blurRadius, bounds, type, isCircle});
Jim Van Verth57061ee2017-04-28 17:30:30 -0400228 if (isCircle) {
229 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
230 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
231 } else {
232 fVertCount = rrect_type_to_vert_count(type);
233 fIndexCount = rrect_type_to_index_count(type);
234 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500235 }
236
Brian Salomonfc527d22016-12-14 21:07:01 -0500237 const char* name() const override { return "ShadowCircularRRectOp"; }
Jim Van Verthc5903412016-11-17 15:27:09 -0500238
Brian Osman9a390ac2018-11-12 09:47:48 -0500239#ifdef SK_DEBUG
Jim Van Verthc5903412016-11-17 15:27:09 -0500240 SkString dumpInfo() const override {
241 SkString string;
242 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500243 string.appendf(
244 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
Jim Van Verth57061ee2017-04-28 17:30:30 -0400245 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
Brian Salomonfc527d22016-12-14 21:07:01 -0500246 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
247 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
Jim Van Verthb6069df2017-04-28 11:00:35 -0400248 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
Jim Van Verth57061ee2017-04-28 17:30:30 -0400249 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
Jim Van Verthc5903412016-11-17 15:27:09 -0500250 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500251 string.append(INHERITED::dumpInfo());
252 return string;
253 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500254#endif
Jim Van Verthc5903412016-11-17 15:27:09 -0500255
Brian Salomon05969092017-07-13 11:20:51 -0400256 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
257
Chris Dalton6ce447a2019-06-23 18:07:38 -0600258 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
259 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700260 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400261 }
262
Brian Salomon92aee3d2016-12-21 09:20:25 -0500263private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400264 struct Geometry {
265 GrColor fColor;
266 SkScalar fOuterRadius;
267 SkScalar fUmbraInset;
268 SkScalar fInnerRadius;
269 SkScalar fBlurRadius;
270 SkRect fDevBounds;
271 RRectType fType;
272 bool fIsCircle;
273 };
274
Jim Van Verthc5903412016-11-17 15:27:09 -0500275 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500276 SkPoint fPos;
277 GrColor fColor;
278 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400279 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500280 };
281
Jim Van Verth57061ee2017-04-28 17:30:30 -0400282 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
283
284 GrColor color = args.fColor;
285 SkScalar outerRadius = args.fOuterRadius;
286 SkScalar innerRadius = args.fInnerRadius;
287 SkScalar blurRadius = args.fBlurRadius;
288 SkScalar distanceCorrection = outerRadius / blurRadius;
289
290 const SkRect& bounds = args.fDevBounds;
291
292 // The inner radius in the vertex data must be specified in normalized space.
293 innerRadius = innerRadius / outerRadius;
294
295 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
296 SkScalar halfWidth = 0.5f * bounds.width();
297 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
298
299 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500300 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400301 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400302 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500303 (*verts)++;
304
Jim Van Verth57061ee2017-04-28 17:30:30 -0400305 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500306 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400307 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400308 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500309 (*verts)++;
310
Jim Van Verth57061ee2017-04-28 17:30:30 -0400311 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500312 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400313 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400314 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500315 (*verts)++;
316
Jim Van Verth57061ee2017-04-28 17:30:30 -0400317 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500318 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400319 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400320 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500321 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400322
323 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
324 (*verts)->fColor = color;
325 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
326 (*verts)->fDistanceCorrection = distanceCorrection;
327 (*verts)++;
328
329 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
330 (*verts)->fColor = color;
331 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
332 (*verts)->fDistanceCorrection = distanceCorrection;
333 (*verts)++;
334
335 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
336 (*verts)->fColor = color;
337 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
338 (*verts)->fDistanceCorrection = distanceCorrection;
339 (*verts)++;
340
341 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
342 (*verts)->fColor = color;
343 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
344 (*verts)->fDistanceCorrection = distanceCorrection;
345 (*verts)++;
346
347 if (isStroked) {
348 // compute the inner ring
349
350 // cosine and sine of pi/8
351 SkScalar c = 0.923579533f;
352 SkScalar s = 0.382683432f;
353 SkScalar r = args.fInnerRadius;
354
355 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
356 (*verts)->fColor = color;
357 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
358 (*verts)->fDistanceCorrection = distanceCorrection;
359 (*verts)++;
360
361 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
362 (*verts)->fColor = color;
363 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
364 (*verts)->fDistanceCorrection = distanceCorrection;
365 (*verts)++;
366
367 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
368 (*verts)->fColor = color;
369 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
370 (*verts)->fDistanceCorrection = distanceCorrection;
371 (*verts)++;
372
373 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
374 (*verts)->fColor = color;
375 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
376 (*verts)->fDistanceCorrection = distanceCorrection;
377 (*verts)++;
378
379 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
380 (*verts)->fColor = color;
381 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
382 (*verts)->fDistanceCorrection = distanceCorrection;
383 (*verts)++;
384
385 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
386 (*verts)->fColor = color;
387 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
388 (*verts)->fDistanceCorrection = distanceCorrection;
389 (*verts)++;
390
391 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
392 (*verts)->fColor = color;
393 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
394 (*verts)->fDistanceCorrection = distanceCorrection;
395 (*verts)++;
396
397 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
398 (*verts)->fColor = color;
399 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
400 (*verts)->fDistanceCorrection = distanceCorrection;
401 (*verts)++;
402 } else {
403 // filled
404 (*verts)->fPos = center;
405 (*verts)->fColor = color;
406 (*verts)->fOffset = SkPoint::Make(0, 0);
407 (*verts)->fDistanceCorrection = distanceCorrection;
408 (*verts)++;
409 }
410 }
411
412 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
413 GrColor color = args.fColor;
414 SkScalar outerRadius = args.fOuterRadius;
415
416 const SkRect& bounds = args.fDevBounds;
417
418 SkScalar umbraInset = args.fUmbraInset;
419 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
420 if (umbraInset > minDim) {
421 umbraInset = minDim;
422 }
423
424 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
425 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
426 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
427 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
428 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
429 bounds.fLeft, bounds.fRight };
430 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
431 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
432 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
433 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
434 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
435 bounds.fBottom, bounds.fBottom };
436
437 SkScalar blurRadius = args.fBlurRadius;
438
439 // In the case where we have to inset more for the umbra, our two triangles in the
440 // corner get skewed to a diamond rather than a square. To correct for that,
441 // we also skew the vectors we send to the shader that help define the circle.
442 // By doing so, we end up with a quarter circle in the corner rather than the
443 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400444
445 // This is a bit magical, but it gives us the correct results at extrema:
446 // a) umbraInset == outerRadius produces an orthogonal vector
447 // b) outerRadius == 0 produces a diagonal vector
448 // And visually the corner looks correct.
449 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400450 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400451 // We want the circle edge to fall fractionally along the diagonal at
452 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
453 //
454 // Setting the components of the diagonal offset to the following value will give us that.
455 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
456 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400457 SkScalar distanceCorrection = umbraInset / blurRadius;
458
459 // build corner by corner
460 for (int i = 0; i < 4; ++i) {
461 // inner point
462 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
463 (*verts)->fColor = color;
464 (*verts)->fOffset = SkVector::Make(0, 0);
465 (*verts)->fDistanceCorrection = distanceCorrection;
466 (*verts)++;
467
468 // outer points
469 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
470 (*verts)->fColor = color;
471 (*verts)->fOffset = SkVector::Make(0, -1);
472 (*verts)->fDistanceCorrection = distanceCorrection;
473 (*verts)++;
474
475 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
476 (*verts)->fColor = color;
477 (*verts)->fOffset = outerVec;
478 (*verts)->fDistanceCorrection = distanceCorrection;
479 (*verts)++;
480
481 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
482 (*verts)->fColor = color;
483 (*verts)->fOffset = diagVec;
484 (*verts)->fDistanceCorrection = distanceCorrection;
485 (*verts)++;
486
487 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
488 (*verts)->fColor = color;
489 (*verts)->fOffset = outerVec;
490 (*verts)->fDistanceCorrection = distanceCorrection;
491 (*verts)++;
492
493 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
494 (*verts)->fColor = color;
495 (*verts)->fOffset = SkVector::Make(0, -1);
496 (*verts)->fDistanceCorrection = distanceCorrection;
497 (*verts)++;
498 }
499
500 // Add the additional vertices for overstroked rrects.
501 // Effectively this is an additional stroked rrect, with its
502 // parameters equal to those in the center of the 9-patch. This will
503 // give constant values across this inner ring.
504 if (kOverstroke_RRectType == args.fType) {
505 SkASSERT(args.fInnerRadius > 0.0f);
506
507 SkScalar inset = umbraInset + args.fInnerRadius;
508
509 // TL
510 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
511 (*verts)->fColor = color;
512 (*verts)->fOffset = SkPoint::Make(0, 0);
513 (*verts)->fDistanceCorrection = distanceCorrection;
514 (*verts)++;
515
516 // TR
517 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
518 (*verts)->fColor = color;
519 (*verts)->fOffset = SkPoint::Make(0, 0);
520 (*verts)->fDistanceCorrection = distanceCorrection;
521 (*verts)++;
522
523 // BL
524 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
525 (*verts)->fColor = color;
526 (*verts)->fOffset = SkPoint::Make(0, 0);
527 (*verts)->fDistanceCorrection = distanceCorrection;
528 (*verts)++;
529
530 // BR
531 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
532 (*verts)->fColor = color;
533 (*verts)->fOffset = SkPoint::Make(0, 0);
534 (*verts)->fDistanceCorrection = distanceCorrection;
535 (*verts)++;
536 }
537
Jim Van Verthc5903412016-11-17 15:27:09 -0500538 }
539
Brian Salomon91326c32017-08-09 16:02:19 -0400540 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500541 // Setup geometry processor
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500542 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(target->allocator(),
543 fFalloffProxy.get());
Jim Van Verthc5903412016-11-17 15:27:09 -0500544
545 int instanceCount = fGeoData.count();
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500546 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
Jim Van Verthc5903412016-11-17 15:27:09 -0500547
Brian Salomon12d22642019-01-29 14:38:50 -0500548 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500549 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400550 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
551 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500552 if (!verts) {
553 SkDebugf("Could not allocate vertices\n");
554 return;
555 }
556
Brian Salomon12d22642019-01-29 14:38:50 -0500557 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500558 int firstIndex = 0;
559 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
560 if (!indices) {
561 SkDebugf("Could not allocate indices\n");
562 return;
563 }
564
565 int currStartVertex = 0;
566 for (int i = 0; i < instanceCount; i++) {
567 const Geometry& args = fGeoData[i];
568
Jim Van Verth57061ee2017-04-28 17:30:30 -0400569 if (args.fIsCircle) {
570 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
571 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500572
Jim Van Verth57061ee2017-04-28 17:30:30 -0400573 const uint16_t* primIndices = circle_type_to_indices(isStroked);
574 const int primIndexCount = circle_type_to_index_count(isStroked);
575 for (int i = 0; i < primIndexCount; ++i) {
576 *indices++ = primIndices[i] + currStartVertex;
577 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500578
Jim Van Verth57061ee2017-04-28 17:30:30 -0400579 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500580
Jim Van Verth57061ee2017-04-28 17:30:30 -0400581 } else {
582 this->fillInRRectVerts(args, &verts);
583
584 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
585 const int primIndexCount = rrect_type_to_index_count(args.fType);
586 for (int i = 0; i < primIndexCount; ++i) {
587 *indices++ = primIndices[i] + currStartVertex;
588 }
589
590 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400591 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500592 }
593
Jim Van Verth7da048b2019-10-29 13:28:14 -0400594 auto fixedDynamicState = target->makeFixedDynamicState(1);
595 fixedDynamicState->fPrimitiveProcessorTextures[0] = fFalloffProxy.get();
596
Brian Salomon7eae3e02018-08-07 14:02:38 +0000597 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500598 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000599 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500600 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500601 target->recordDraw(gp, mesh, 1, fixedDynamicState, nullptr, GrPrimitiveType::kTriangles);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700602 }
603
604 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
605 flushState->executeDrawsAndUploadsForMeshDrawOp(
606 this, chainBounds, GrProcessorSet::MakeEmptySet());
Jim Van Verthc5903412016-11-17 15:27:09 -0500607 }
608
Brian Salomon7eae3e02018-08-07 14:02:38 +0000609 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500610 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500611 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500612 fVertCount += that->fVertCount;
613 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000614 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500615 }
616
Jim Van Verth7da048b2019-10-29 13:28:14 -0400617 void visitProxies(const VisitProxyFunc& func) const override {
618 func(fFalloffProxy.get(), GrMipMapped(false));
619 }
620
Jim Van Verthc5903412016-11-17 15:27:09 -0500621 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500622 int fVertCount;
623 int fIndexCount;
Jim Van Verth7da048b2019-10-29 13:28:14 -0400624 sk_sp<GrTextureProxy> fFalloffProxy;
Jim Van Verthc5903412016-11-17 15:27:09 -0500625
Brian Salomon05969092017-07-13 11:20:51 -0400626 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500627};
628
Brian Salomon05969092017-07-13 11:20:51 -0400629} // anonymous namespace
630
Jim Van Verthc5903412016-11-17 15:27:09 -0500631///////////////////////////////////////////////////////////////////////////////
632
Jim Van Verth57061ee2017-04-28 17:30:30 -0400633namespace GrShadowRRectOp {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400634
635static sk_sp<GrTextureProxy> create_falloff_texture(GrProxyProvider* proxyProvider) {
636 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
637 GrUniqueKey key;
638 GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
639 builder.finish();
640
641 sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(
642 key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
643 if (!falloffTexture) {
644 static const int kWidth = 128;
645 static const size_t kRowBytes = kWidth*GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
646 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
647
648 sk_sp<SkData> data = SkData::MakeUninitialized(kRowBytes);
649 if (!data) {
650 return nullptr;
651 }
652 unsigned char* values = (unsigned char*) data->writable_data();
653 for (int i = 0; i < 128; ++i) {
654 SkScalar d = SK_Scalar1 - i/SkIntToScalar(127);
655 values[i] = SkScalarRoundToInt((SkScalarExp(-4*d*d) - 0.018f)*255);
656 }
657
658 sk_sp<SkImage> img = SkImage::MakeRasterData(ii, std::move(data), kRowBytes);
659 if (!img) {
660 return nullptr;
661 }
662
663 falloffTexture = proxyProvider->createTextureProxy(std::move(img), 1, SkBudgeted::kYes,
664 SkBackingFit::kExact);
665 if (!falloffTexture) {
666 return nullptr;
667 }
668
669 SkASSERT(falloffTexture->origin() == kTopLeft_GrSurfaceOrigin);
670 proxyProvider->assignUniqueKeyToProxy(key, falloffTexture.get());
671 }
672
673 return falloffTexture;
674}
675
676
Robert Phillipsb97da532019-02-12 15:24:12 -0500677std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400678 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400679 const SkMatrix& viewMatrix,
680 const SkRRect& rrect,
681 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400682 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500683 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500684 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400685
Jim Van Verth7da048b2019-10-29 13:28:14 -0400686 sk_sp<GrTextureProxy> falloffTexture = create_falloff_texture(context->priv().proxyProvider());
687 if (!falloffTexture) {
688 return nullptr;
689 }
690
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500691 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500692 const SkRect& rrectBounds = rrect.getBounds();
693 SkRect bounds;
694 viewMatrix.mapRect(&bounds, rrectBounds);
695
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400696 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500697 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400698 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
699 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
700 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500701
Robert Phillipse5763782019-04-17 14:38:24 -0400702 if (scaledInsetWidth <= 0) {
703 return nullptr;
704 }
705
Robert Phillips9da87e02019-02-04 13:26:26 -0500706 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400707
708 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
709 scaledRadius,
710 rrect.isOval(),
711 blurWidth,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400712 scaledInsetWidth,
713 std::move(falloffTexture));
Jim Van Verthc5903412016-11-17 15:27:09 -0500714}
Brian Salomonfc527d22016-12-14 21:07:01 -0500715}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400716
Jim Van Verthc5903412016-11-17 15:27:09 -0500717///////////////////////////////////////////////////////////////////////////////
718
Hal Canary6f6961e2017-01-31 13:50:44 -0500719#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500720
Brian Salomon05969092017-07-13 11:20:51 -0400721GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Robert Phillips50299de2019-11-23 15:20:57 +0000722 // create a similarity matrix
723 SkScalar rotate = random->nextSScalar1() * 360.f;
724 SkScalar translateX = random->nextSScalar1() * 1000.f;
725 SkScalar translateY = random->nextSScalar1() * 1000.f;
726 SkScalar scale;
Brian Osman4462c042018-06-08 16:35:44 -0400727 do {
Robert Phillips50299de2019-11-23 15:20:57 +0000728 scale = random->nextSScalar1() * 100.f;
729 } while (scale == 0);
730 SkMatrix viewMatrix;
731 viewMatrix.setRotate(rotate);
732 viewMatrix.postTranslate(translateX, translateY);
733 viewMatrix.postScale(scale, scale);
734 SkScalar insetWidth = random->nextSScalar1() * 72.f;
735 SkScalar blurWidth = random->nextSScalar1() * 72.f;
736 bool isCircle = random->nextBool();
737 // This op doesn't use a full GrPaint, just a color.
738 GrColor color = paint.getColor4f().toBytes_RGBA();
739 if (isCircle) {
740 SkRect circle = GrTest::TestSquare(random);
741 SkRRect rrect = SkRRect::MakeOval(circle);
742 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
743 } else {
744 SkRRect rrect;
745 do {
746 // This may return a rrect with elliptical corners, which we don't support.
747 rrect = GrTest::TestRRectSimple(random);
748 } while (!SkRRectPriv::IsSimpleCircular(rrect));
749 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
750 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500751}
752
753#endif