blob: 496e24cf0b0b86ef95ebf13fcbddbb556e120fea [file] [log] [blame]
Jim Van Verthc5903412016-11-17 15:27:09 -05001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrShadowRRectOp.h"
Robert Phillips7c525e62018-06-12 10:11:12 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/private/GrRecordingContext.h"
11#include "src/core/SkRRectPriv.h"
Greg Daniel6f5441a2020-01-28 17:02:49 -050012#include "src/gpu/GrBitmapTextureMaker.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/gpu/GrDrawOpTest.h"
14#include "src/gpu/GrMemoryPool.h"
15#include "src/gpu/GrOpFlushState.h"
Jim Van Verth7da048b2019-10-29 13:28:14 -040016#include "src/gpu/GrProxyProvider.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrRecordingContextPriv.h"
18#include "src/gpu/effects/GrShadowGeoProc.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050019#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Jim Van Verthc5903412016-11-17 15:27:09 -050020
21///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040022// Circle Data
23//
Jim Van Verthc5903412016-11-17 15:27:09 -050024// We have two possible cases for geometry for a circle:
25
26// In the case of a normal fill, we draw geometry for the circle as an octagon.
27static const uint16_t gFillCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050028 // enter the octagon
29 // clang-format off
30 0, 1, 8, 1, 2, 8,
31 2, 3, 8, 3, 4, 8,
32 4, 5, 8, 5, 6, 8,
33 6, 7, 8, 7, 0, 8,
34 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050035};
36
37// For stroked circles, we use two nested octagons.
38static const uint16_t gStrokeCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050039 // enter the octagon
40 // clang-format off
41 0, 1, 9, 0, 9, 8,
42 1, 2, 10, 1, 10, 9,
43 2, 3, 11, 2, 11, 10,
44 3, 4, 12, 3, 12, 11,
45 4, 5, 13, 4, 13, 12,
46 5, 6, 14, 5, 14, 13,
47 6, 7, 15, 6, 15, 14,
48 7, 0, 8, 7, 8, 15,
49 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050050};
51
52static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
53static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
54static const int kVertsPerStrokeCircle = 16;
55static const int kVertsPerFillCircle = 9;
56
57static int circle_type_to_vert_count(bool stroked) {
58 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
59}
60
61static int circle_type_to_index_count(bool stroked) {
62 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
63}
64
65static const uint16_t* circle_type_to_indices(bool stroked) {
66 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
67}
68
69///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040070// RoundRect Data
71//
Jim Van Verthb6069df2017-04-28 11:00:35 -040072// The geometry for a shadow roundrect is similar to a 9-patch:
Jim Van Verthc5903412016-11-17 15:27:09 -050073// ____________
74// |_|________|_|
75// | | | |
76// | | | |
77// | | | |
78// |_|________|_|
79// |_|________|_|
80//
Jim Van Verthb6069df2017-04-28 11:00:35 -040081// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
82// shows the upper part of the upper left corner. The bottom triangle would similarly be split
83// into two triangles.)
84// ________
85// |\ \ |
86// | \ \ |
87// | \\ |
88// | \|
89// --------
90//
91// The center of the fan handles the curve of the corner. For roundrects where the stroke width
92// is greater than the corner radius, the outer triangles blend from the curve to the straight
93// sides. Otherwise these triangles will be degenerate.
94//
95// In the case where the stroke width is greater than the corner radius and the
96// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
97// This rectangle extends the coverage values of the center edges of the 9-patch.
Jim Van Verthc5903412016-11-17 15:27:09 -050098// ____________
99// |_|________|_|
100// | |\ ____ /| |
101// | | | | | |
102// | | |____| | |
103// |_|/______\|_|
104// |_|________|_|
105//
Jim Van Verthb6069df2017-04-28 11:00:35 -0400106// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
Jim Van Verthc5903412016-11-17 15:27:09 -0500107
Jim Van Verthb6069df2017-04-28 11:00:35 -0400108static const uint16_t gRRectIndices[] = {
109 // clang-format off
110 // overstroke quads
111 // we place this at the beginning so that we can skip these indices when rendering as filled
112 0, 6, 25, 0, 25, 24,
113 6, 18, 27, 6, 27, 25,
114 18, 12, 26, 18, 26, 27,
115 12, 0, 24, 12, 24, 26,
Jim Van Verthc5903412016-11-17 15:27:09 -0500116
Jim Van Verthb6069df2017-04-28 11:00:35 -0400117 // corners
118 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
119 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
120 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
121 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
Jim Van Verthc5903412016-11-17 15:27:09 -0500122
Jim Van Verthb6069df2017-04-28 11:00:35 -0400123 // edges
124 0, 5, 11, 0, 11, 6,
125 6, 7, 19, 6, 19, 18,
126 18, 23, 17, 18, 17, 12,
127 12, 13, 1, 12, 1, 0,
128
129 // fill quad
130 // we place this at the end so that we can skip these indices when rendering as stroked
131 0, 6, 18, 0, 18, 12,
132 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -0500133};
Jim Van Verthc5903412016-11-17 15:27:09 -0500134
135// overstroke count
Jim Van Verthb6069df2017-04-28 11:00:35 -0400136static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
Jim Van Verthc5903412016-11-17 15:27:09 -0500137// simple stroke count skips overstroke indices
Jim Van Verthb6069df2017-04-28 11:00:35 -0400138static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
139// fill count adds final quad to stroke count
140static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
141static const int kVertsPerStrokeRRect = 24;
142static const int kVertsPerOverstrokeRRect = 28;
143static const int kVertsPerFillRRect = 24;
Jim Van Verthc5903412016-11-17 15:27:09 -0500144
145enum RRectType {
146 kFill_RRectType,
147 kStroke_RRectType,
148 kOverstroke_RRectType,
149};
150
151static int rrect_type_to_vert_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500152 switch (type) {
153 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400154 return kVertsPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500155 case kStroke_RRectType:
156 return kVertsPerStrokeRRect;
157 case kOverstroke_RRectType:
158 return kVertsPerOverstrokeRRect;
159 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400160 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500161}
162
163static int rrect_type_to_index_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500164 switch (type) {
165 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400166 return kIndicesPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500167 case kStroke_RRectType:
168 return kIndicesPerStrokeRRect;
169 case kOverstroke_RRectType:
170 return kIndicesPerOverstrokeRRect;
171 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400172 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500173}
174
175static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500176 switch (type) {
177 case kFill_RRectType:
Brian Salomonfc527d22016-12-14 21:07:01 -0500178 case kStroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400179 return gRRectIndices + 6*4;
Brian Salomonfc527d22016-12-14 21:07:01 -0500180 case kOverstroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400181 return gRRectIndices;
Brian Salomonfc527d22016-12-14 21:07:01 -0500182 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400183 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500184}
185
Jim Van Verth57061ee2017-04-28 17:30:30 -0400186///////////////////////////////////////////////////////////////////////////////
Brian Salomon05969092017-07-13 11:20:51 -0400187namespace {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400188
Brian Salomon05969092017-07-13 11:20:51 -0400189class ShadowCircularRRectOp final : public GrMeshDrawOp {
Jim Van Verthc5903412016-11-17 15:27:09 -0500190public:
Brian Salomon25a88092016-12-01 09:36:50 -0500191 DEFINE_OP_CLASS_ID
Jim Van Verthc5903412016-11-17 15:27:09 -0500192
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400193 // An insetWidth > 1/2 rect width or height indicates a simple fill.
Brian Salomon05969092017-07-13 11:20:51 -0400194 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400195 float devRadius, bool isCircle, float blurRadius, float insetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500196 GrSurfaceProxyView falloffView)
Jim Van Verth7da048b2019-10-29 13:28:14 -0400197 : INHERITED(ClassID())
Greg Danielad994cd2019-12-10 09:35:16 -0500198 , fFalloffView(std::move(falloffView)) {
Jim Van Verthc5903412016-11-17 15:27:09 -0500199 SkRect bounds = devRect;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400200 SkASSERT(insetWidth > 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400201 SkScalar innerRadius = 0.0f;
Jim Van Verthc5903412016-11-17 15:27:09 -0500202 SkScalar outerRadius = devRadius;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400203 SkScalar umbraInset;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400204
205 RRectType type = kFill_RRectType;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400206 if (isCircle) {
207 umbraInset = 0;
208 } else {
Brian Osman788b9162020-02-07 10:36:46 -0500209 umbraInset = std::max(outerRadius, blurRadius);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400210 }
211
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400212 // If stroke is greater than width or height, this is still a fill,
213 // otherwise we compute stroke params.
214 if (isCircle) {
215 innerRadius = devRadius - insetWidth;
216 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
217 } else {
Brian Osman788b9162020-02-07 10:36:46 -0500218 if (insetWidth <= 0.5f*std::min(devRect.width(), devRect.height())) {
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400219 // We don't worry about a real inner radius, we just need to know if we
220 // need to create overstroke vertices.
Brian Osman788b9162020-02-07 10:36:46 -0500221 innerRadius = std::max(insetWidth - umbraInset, 0.0f);
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400222 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400223 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500224 }
225
Greg Daniel5faf4742019-10-01 15:14:44 -0400226 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
Jim Van Verthc5903412016-11-17 15:27:09 -0500227
Jim Van Verth57061ee2017-04-28 17:30:30 -0400228 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
Jim Van Verthfb186392018-09-11 11:37:46 -0400229 blurRadius, bounds, type, isCircle});
Jim Van Verth57061ee2017-04-28 17:30:30 -0400230 if (isCircle) {
231 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
232 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
233 } else {
234 fVertCount = rrect_type_to_vert_count(type);
235 fIndexCount = rrect_type_to_index_count(type);
236 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500237 }
238
Brian Salomonfc527d22016-12-14 21:07:01 -0500239 const char* name() const override { return "ShadowCircularRRectOp"; }
Jim Van Verthc5903412016-11-17 15:27:09 -0500240
Brian Osman9a390ac2018-11-12 09:47:48 -0500241#ifdef SK_DEBUG
Jim Van Verthc5903412016-11-17 15:27:09 -0500242 SkString dumpInfo() const override {
243 SkString string;
244 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500245 string.appendf(
246 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
Jim Van Verth57061ee2017-04-28 17:30:30 -0400247 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
Brian Salomonfc527d22016-12-14 21:07:01 -0500248 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
249 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
Jim Van Verthb6069df2017-04-28 11:00:35 -0400250 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
Jim Van Verth57061ee2017-04-28 17:30:30 -0400251 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
Jim Van Verthc5903412016-11-17 15:27:09 -0500252 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500253 string.append(INHERITED::dumpInfo());
254 return string;
255 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500256#endif
Jim Van Verthc5903412016-11-17 15:27:09 -0500257
Brian Salomon05969092017-07-13 11:20:51 -0400258 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
259
Chris Dalton6ce447a2019-06-23 18:07:38 -0600260 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
261 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700262 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400263 }
264
Brian Salomon92aee3d2016-12-21 09:20:25 -0500265private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400266 struct Geometry {
267 GrColor fColor;
268 SkScalar fOuterRadius;
269 SkScalar fUmbraInset;
270 SkScalar fInnerRadius;
271 SkScalar fBlurRadius;
272 SkRect fDevBounds;
273 RRectType fType;
274 bool fIsCircle;
275 };
276
Jim Van Verthc5903412016-11-17 15:27:09 -0500277 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500278 SkPoint fPos;
279 GrColor fColor;
280 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400281 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500282 };
283
Jim Van Verth57061ee2017-04-28 17:30:30 -0400284 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
285
286 GrColor color = args.fColor;
287 SkScalar outerRadius = args.fOuterRadius;
288 SkScalar innerRadius = args.fInnerRadius;
289 SkScalar blurRadius = args.fBlurRadius;
290 SkScalar distanceCorrection = outerRadius / blurRadius;
291
292 const SkRect& bounds = args.fDevBounds;
293
294 // The inner radius in the vertex data must be specified in normalized space.
295 innerRadius = innerRadius / outerRadius;
296
297 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
298 SkScalar halfWidth = 0.5f * bounds.width();
299 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
300
301 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500302 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400303 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400304 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500305 (*verts)++;
306
Jim Van Verth57061ee2017-04-28 17:30:30 -0400307 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500308 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400309 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400310 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500311 (*verts)++;
312
Jim Van Verth57061ee2017-04-28 17:30:30 -0400313 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500314 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400315 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400316 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500317 (*verts)++;
318
Jim Van Verth57061ee2017-04-28 17:30:30 -0400319 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500320 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400321 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400322 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500323 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400324
325 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
326 (*verts)->fColor = color;
327 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
328 (*verts)->fDistanceCorrection = distanceCorrection;
329 (*verts)++;
330
331 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
332 (*verts)->fColor = color;
333 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
334 (*verts)->fDistanceCorrection = distanceCorrection;
335 (*verts)++;
336
337 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
338 (*verts)->fColor = color;
339 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
340 (*verts)->fDistanceCorrection = distanceCorrection;
341 (*verts)++;
342
343 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
344 (*verts)->fColor = color;
345 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
346 (*verts)->fDistanceCorrection = distanceCorrection;
347 (*verts)++;
348
349 if (isStroked) {
350 // compute the inner ring
351
352 // cosine and sine of pi/8
353 SkScalar c = 0.923579533f;
354 SkScalar s = 0.382683432f;
355 SkScalar r = args.fInnerRadius;
356
357 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
358 (*verts)->fColor = color;
359 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
360 (*verts)->fDistanceCorrection = distanceCorrection;
361 (*verts)++;
362
363 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
364 (*verts)->fColor = color;
365 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
366 (*verts)->fDistanceCorrection = distanceCorrection;
367 (*verts)++;
368
369 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
370 (*verts)->fColor = color;
371 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
372 (*verts)->fDistanceCorrection = distanceCorrection;
373 (*verts)++;
374
375 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
376 (*verts)->fColor = color;
377 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
378 (*verts)->fDistanceCorrection = distanceCorrection;
379 (*verts)++;
380
381 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
382 (*verts)->fColor = color;
383 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
384 (*verts)->fDistanceCorrection = distanceCorrection;
385 (*verts)++;
386
387 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
388 (*verts)->fColor = color;
389 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
390 (*verts)->fDistanceCorrection = distanceCorrection;
391 (*verts)++;
392
393 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
394 (*verts)->fColor = color;
395 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
396 (*verts)->fDistanceCorrection = distanceCorrection;
397 (*verts)++;
398
399 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
400 (*verts)->fColor = color;
401 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
402 (*verts)->fDistanceCorrection = distanceCorrection;
403 (*verts)++;
404 } else {
405 // filled
406 (*verts)->fPos = center;
407 (*verts)->fColor = color;
408 (*verts)->fOffset = SkPoint::Make(0, 0);
409 (*verts)->fDistanceCorrection = distanceCorrection;
410 (*verts)++;
411 }
412 }
413
414 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
415 GrColor color = args.fColor;
416 SkScalar outerRadius = args.fOuterRadius;
417
418 const SkRect& bounds = args.fDevBounds;
419
420 SkScalar umbraInset = args.fUmbraInset;
Brian Osman788b9162020-02-07 10:36:46 -0500421 SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height());
Jim Van Verth57061ee2017-04-28 17:30:30 -0400422 if (umbraInset > minDim) {
423 umbraInset = minDim;
424 }
425
426 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
427 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
428 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
429 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
430 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
431 bounds.fLeft, bounds.fRight };
432 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
433 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
434 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
435 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
436 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
437 bounds.fBottom, bounds.fBottom };
438
439 SkScalar blurRadius = args.fBlurRadius;
440
441 // In the case where we have to inset more for the umbra, our two triangles in the
442 // corner get skewed to a diamond rather than a square. To correct for that,
443 // we also skew the vectors we send to the shader that help define the circle.
444 // By doing so, we end up with a quarter circle in the corner rather than the
445 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400446
447 // This is a bit magical, but it gives us the correct results at extrema:
448 // a) umbraInset == outerRadius produces an orthogonal vector
449 // b) outerRadius == 0 produces a diagonal vector
450 // And visually the corner looks correct.
451 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400452 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400453 // We want the circle edge to fall fractionally along the diagonal at
454 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
455 //
456 // Setting the components of the diagonal offset to the following value will give us that.
457 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
458 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400459 SkScalar distanceCorrection = umbraInset / blurRadius;
460
461 // build corner by corner
462 for (int i = 0; i < 4; ++i) {
463 // inner point
464 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
465 (*verts)->fColor = color;
466 (*verts)->fOffset = SkVector::Make(0, 0);
467 (*verts)->fDistanceCorrection = distanceCorrection;
468 (*verts)++;
469
470 // outer points
471 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
472 (*verts)->fColor = color;
473 (*verts)->fOffset = SkVector::Make(0, -1);
474 (*verts)->fDistanceCorrection = distanceCorrection;
475 (*verts)++;
476
477 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
478 (*verts)->fColor = color;
479 (*verts)->fOffset = outerVec;
480 (*verts)->fDistanceCorrection = distanceCorrection;
481 (*verts)++;
482
483 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
484 (*verts)->fColor = color;
485 (*verts)->fOffset = diagVec;
486 (*verts)->fDistanceCorrection = distanceCorrection;
487 (*verts)++;
488
489 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
490 (*verts)->fColor = color;
491 (*verts)->fOffset = outerVec;
492 (*verts)->fDistanceCorrection = distanceCorrection;
493 (*verts)++;
494
495 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
496 (*verts)->fColor = color;
497 (*verts)->fOffset = SkVector::Make(0, -1);
498 (*verts)->fDistanceCorrection = distanceCorrection;
499 (*verts)++;
500 }
501
502 // Add the additional vertices for overstroked rrects.
503 // Effectively this is an additional stroked rrect, with its
504 // parameters equal to those in the center of the 9-patch. This will
505 // give constant values across this inner ring.
506 if (kOverstroke_RRectType == args.fType) {
507 SkASSERT(args.fInnerRadius > 0.0f);
508
509 SkScalar inset = umbraInset + args.fInnerRadius;
510
511 // TL
512 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
513 (*verts)->fColor = color;
514 (*verts)->fOffset = SkPoint::Make(0, 0);
515 (*verts)->fDistanceCorrection = distanceCorrection;
516 (*verts)++;
517
518 // TR
519 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
520 (*verts)->fColor = color;
521 (*verts)->fOffset = SkPoint::Make(0, 0);
522 (*verts)->fDistanceCorrection = distanceCorrection;
523 (*verts)++;
524
525 // BL
526 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
527 (*verts)->fColor = color;
528 (*verts)->fOffset = SkPoint::Make(0, 0);
529 (*verts)->fDistanceCorrection = distanceCorrection;
530 (*verts)++;
531
532 // BR
533 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
534 (*verts)->fColor = color;
535 (*verts)->fOffset = SkPoint::Make(0, 0);
536 (*verts)->fDistanceCorrection = distanceCorrection;
537 (*verts)++;
538 }
539
Jim Van Verthc5903412016-11-17 15:27:09 -0500540 }
541
Brian Salomon91326c32017-08-09 16:02:19 -0400542 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500543 // Setup geometry processor
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500544 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(target->allocator(),
Greg Danielad994cd2019-12-10 09:35:16 -0500545 fFalloffView);
Jim Van Verthc5903412016-11-17 15:27:09 -0500546
547 int instanceCount = fGeoData.count();
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500548 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
Jim Van Verthc5903412016-11-17 15:27:09 -0500549
Brian Salomon12d22642019-01-29 14:38:50 -0500550 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500551 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400552 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
553 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500554 if (!verts) {
555 SkDebugf("Could not allocate vertices\n");
556 return;
557 }
558
Brian Salomon12d22642019-01-29 14:38:50 -0500559 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500560 int firstIndex = 0;
561 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
562 if (!indices) {
563 SkDebugf("Could not allocate indices\n");
564 return;
565 }
566
567 int currStartVertex = 0;
568 for (int i = 0; i < instanceCount; i++) {
569 const Geometry& args = fGeoData[i];
570
Jim Van Verth57061ee2017-04-28 17:30:30 -0400571 if (args.fIsCircle) {
572 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
573 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500574
Jim Van Verth57061ee2017-04-28 17:30:30 -0400575 const uint16_t* primIndices = circle_type_to_indices(isStroked);
576 const int primIndexCount = circle_type_to_index_count(isStroked);
577 for (int i = 0; i < primIndexCount; ++i) {
578 *indices++ = primIndices[i] + currStartVertex;
579 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500580
Jim Van Verth57061ee2017-04-28 17:30:30 -0400581 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500582
Jim Van Verth57061ee2017-04-28 17:30:30 -0400583 } else {
584 this->fillInRRectVerts(args, &verts);
585
586 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
587 const int primIndexCount = rrect_type_to_index_count(args.fType);
588 for (int i = 0; i < primIndexCount; ++i) {
589 *indices++ = primIndices[i] + currStartVertex;
590 }
591
592 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400593 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500594 }
595
Jim Van Verth7da048b2019-10-29 13:28:14 -0400596 auto fixedDynamicState = target->makeFixedDynamicState(1);
Greg Danielad994cd2019-12-10 09:35:16 -0500597 fixedDynamicState->fPrimitiveProcessorTextures[0] = fFalloffView.proxy();
Jim Van Verth7da048b2019-10-29 13:28:14 -0400598
Brian Salomon7eae3e02018-08-07 14:02:38 +0000599 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500600 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000601 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500602 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500603 target->recordDraw(gp, mesh, 1, fixedDynamicState, nullptr, GrPrimitiveType::kTriangles);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700604 }
605
606 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips3968fcb2019-12-05 16:40:31 -0500607 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState,
608 GrProcessorSet::MakeEmptySet(),
609 GrPipeline::InputFlags::kNone);
610
611 flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline);
Jim Van Verthc5903412016-11-17 15:27:09 -0500612 }
613
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500614 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
615 const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500616 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500617 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500618 fVertCount += that->fVertCount;
619 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000620 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500621 }
622
Jim Van Verth7da048b2019-10-29 13:28:14 -0400623 void visitProxies(const VisitProxyFunc& func) const override {
Greg Danielad994cd2019-12-10 09:35:16 -0500624 func(fFalloffView.proxy(), GrMipMapped(false));
Jim Van Verth7da048b2019-10-29 13:28:14 -0400625 }
626
Jim Van Verthc5903412016-11-17 15:27:09 -0500627 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500628 int fVertCount;
629 int fIndexCount;
Greg Danielad994cd2019-12-10 09:35:16 -0500630 GrSurfaceProxyView fFalloffView;
Jim Van Verthc5903412016-11-17 15:27:09 -0500631
Brian Salomon05969092017-07-13 11:20:51 -0400632 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500633};
634
Brian Salomon05969092017-07-13 11:20:51 -0400635} // anonymous namespace
636
Jim Van Verthc5903412016-11-17 15:27:09 -0500637///////////////////////////////////////////////////////////////////////////////
638
Jim Van Verth57061ee2017-04-28 17:30:30 -0400639namespace GrShadowRRectOp {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400640
Greg Daniel6f5441a2020-01-28 17:02:49 -0500641static sk_sp<GrTextureProxy> create_falloff_texture(GrRecordingContext* context) {
Jim Van Verth7da048b2019-10-29 13:28:14 -0400642 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
643 GrUniqueKey key;
644 GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
645 builder.finish();
646
Greg Daniel6f5441a2020-01-28 17:02:49 -0500647 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
648
Jim Van Verth7da048b2019-10-29 13:28:14 -0400649 sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(
650 key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
651 if (!falloffTexture) {
652 static const int kWidth = 128;
653 static const size_t kRowBytes = kWidth*GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
654 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
655
Greg Daniel6f5441a2020-01-28 17:02:49 -0500656 SkBitmap bitmap;
657 bitmap.allocPixels(ii, kRowBytes);
658
659 unsigned char* values = (unsigned char*) bitmap.getPixels();
Jim Van Verth7da048b2019-10-29 13:28:14 -0400660 for (int i = 0; i < 128; ++i) {
661 SkScalar d = SK_Scalar1 - i/SkIntToScalar(127);
662 values[i] = SkScalarRoundToInt((SkScalarExp(-4*d*d) - 0.018f)*255);
663 }
Greg Daniel6f5441a2020-01-28 17:02:49 -0500664 bitmap.setImmutable();
Jim Van Verth7da048b2019-10-29 13:28:14 -0400665
Greg Daniel6f5441a2020-01-28 17:02:49 -0500666 GrBitmapTextureMaker maker(context, bitmap);
Greg Danielc61d7e32020-02-04 14:27:45 -0500667 auto[view, grCT] = maker.view(GrMipMapped::kNo);
Greg Danielcc104db2020-02-03 14:17:08 -0500668 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
Jim Van Verth7da048b2019-10-29 13:28:14 -0400669
Greg Danielcc104db2020-02-03 14:17:08 -0500670 falloffTexture = view.asTextureProxyRef();
Jim Van Verth7da048b2019-10-29 13:28:14 -0400671 if (!falloffTexture) {
672 return nullptr;
673 }
674
Jim Van Verth7da048b2019-10-29 13:28:14 -0400675 proxyProvider->assignUniqueKeyToProxy(key, falloffTexture.get());
676 }
677
678 return falloffTexture;
679}
680
681
Robert Phillipsb97da532019-02-12 15:24:12 -0500682std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400683 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400684 const SkMatrix& viewMatrix,
685 const SkRRect& rrect,
686 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400687 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500688 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500689 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400690
Greg Daniel6f5441a2020-01-28 17:02:49 -0500691 sk_sp<GrTextureProxy> falloffTexture = create_falloff_texture(context);
Jim Van Verth7da048b2019-10-29 13:28:14 -0400692 if (!falloffTexture) {
693 return nullptr;
694 }
Greg Daniel14b57212019-12-17 16:18:06 -0500695 GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(),
Greg Danielad994cd2019-12-10 09:35:16 -0500696 GrColorType::kAlpha_8);
697
698 GrSurfaceProxyView falloffView(std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle);
Jim Van Verth7da048b2019-10-29 13:28:14 -0400699
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500700 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500701 const SkRect& rrectBounds = rrect.getBounds();
702 SkRect bounds;
703 viewMatrix.mapRect(&bounds, rrectBounds);
704
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400705 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500706 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400707 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
708 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
709 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500710
Robert Phillipse5763782019-04-17 14:38:24 -0400711 if (scaledInsetWidth <= 0) {
712 return nullptr;
713 }
714
Robert Phillips9da87e02019-02-04 13:26:26 -0500715 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400716
717 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
718 scaledRadius,
719 rrect.isOval(),
720 blurWidth,
Jim Van Verth7da048b2019-10-29 13:28:14 -0400721 scaledInsetWidth,
Greg Danielad994cd2019-12-10 09:35:16 -0500722 std::move(falloffView));
Jim Van Verthc5903412016-11-17 15:27:09 -0500723}
Brian Salomonfc527d22016-12-14 21:07:01 -0500724}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400725
Jim Van Verthc5903412016-11-17 15:27:09 -0500726///////////////////////////////////////////////////////////////////////////////
727
Hal Canary6f6961e2017-01-31 13:50:44 -0500728#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500729
Brian Salomon05969092017-07-13 11:20:51 -0400730GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Brian Salomonfc118442019-11-22 19:09:27 -0500731 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
732 // an acceptable combination.
Brian Osman4462c042018-06-08 16:35:44 -0400733 do {
Brian Salomonfc118442019-11-22 19:09:27 -0500734 // create a similarity matrix
735 SkScalar rotate = random->nextSScalar1() * 360.f;
736 SkScalar translateX = random->nextSScalar1() * 1000.f;
737 SkScalar translateY = random->nextSScalar1() * 1000.f;
738 SkScalar scale = random->nextSScalar1() * 100.f;
739 SkMatrix viewMatrix;
740 viewMatrix.setRotate(rotate);
741 viewMatrix.postTranslate(translateX, translateY);
742 viewMatrix.postScale(scale, scale);
743 SkScalar insetWidth = random->nextSScalar1() * 72.f;
744 SkScalar blurWidth = random->nextSScalar1() * 72.f;
745 bool isCircle = random->nextBool();
746 // This op doesn't use a full GrPaint, just a color.
747 GrColor color = paint.getColor4f().toBytes_RGBA();
748 if (isCircle) {
749 SkRect circle = GrTest::TestSquare(random);
750 SkRRect rrect = SkRRect::MakeOval(circle);
751 if (auto op = GrShadowRRectOp::Make(
752 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
753 return op;
754 }
755 } else {
756 SkRRect rrect;
757 do {
758 // This may return a rrect with elliptical corners, which will cause an assert.
759 rrect = GrTest::TestRRectSimple(random);
760 } while (!SkRRectPriv::IsSimpleCircular(rrect));
761 if (auto op = GrShadowRRectOp::Make(
762 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
763 return op;
764 }
765 }
766 } while (true);
Jim Van Verthc5903412016-11-17 15:27:09 -0500767}
768
769#endif