blob: 50ac40f16c30f3ebed6e5357f35a7673dfa0e233 [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
Brian Salomonfc527d22016-12-14 21:07:01 -05008#include "GrShadowRRectOp.h"
Robert Phillips7c525e62018-06-12 10:11:12 -04009
Brian Salomon5ec9def2016-12-20 15:34:05 -050010#include "GrDrawOpTest.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040011#include "GrMemoryPool.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050012#include "GrOpFlushState.h"
Robert Phillipsb97da532019-02-12 15:24:12 -050013#include "GrRecordingContext.h"
14#include "GrRecordingContextPriv.h"
Mike Reed242135a2018-02-22 13:41:39 -050015#include "SkRRectPriv.h"
Jim Van Verthc5903412016-11-17 15:27:09 -050016#include "effects/GrShadowGeoProc.h"
17
18///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040019// Circle Data
20//
Jim Van Verthc5903412016-11-17 15:27:09 -050021// We have two possible cases for geometry for a circle:
22
23// In the case of a normal fill, we draw geometry for the circle as an octagon.
24static const uint16_t gFillCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050025 // enter the octagon
26 // clang-format off
27 0, 1, 8, 1, 2, 8,
28 2, 3, 8, 3, 4, 8,
29 4, 5, 8, 5, 6, 8,
30 6, 7, 8, 7, 0, 8,
31 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050032};
33
34// For stroked circles, we use two nested octagons.
35static const uint16_t gStrokeCircleIndices[] = {
Brian Salomonfc527d22016-12-14 21:07:01 -050036 // enter the octagon
37 // clang-format off
38 0, 1, 9, 0, 9, 8,
39 1, 2, 10, 1, 10, 9,
40 2, 3, 11, 2, 11, 10,
41 3, 4, 12, 3, 12, 11,
42 4, 5, 13, 4, 13, 12,
43 5, 6, 14, 5, 14, 13,
44 6, 7, 15, 6, 15, 14,
45 7, 0, 8, 7, 8, 15,
46 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -050047};
48
49static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
50static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
51static const int kVertsPerStrokeCircle = 16;
52static const int kVertsPerFillCircle = 9;
53
54static int circle_type_to_vert_count(bool stroked) {
55 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
56}
57
58static int circle_type_to_index_count(bool stroked) {
59 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
60}
61
62static const uint16_t* circle_type_to_indices(bool stroked) {
63 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
64}
65
66///////////////////////////////////////////////////////////////////////////////
Jim Van Verth57061ee2017-04-28 17:30:30 -040067// RoundRect Data
68//
Jim Van Verthb6069df2017-04-28 11:00:35 -040069// The geometry for a shadow roundrect is similar to a 9-patch:
Jim Van Verthc5903412016-11-17 15:27:09 -050070// ____________
71// |_|________|_|
72// | | | |
73// | | | |
74// | | | |
75// |_|________|_|
76// |_|________|_|
77//
Jim Van Verthb6069df2017-04-28 11:00:35 -040078// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
79// shows the upper part of the upper left corner. The bottom triangle would similarly be split
80// into two triangles.)
81// ________
82// |\ \ |
83// | \ \ |
84// | \\ |
85// | \|
86// --------
87//
88// The center of the fan handles the curve of the corner. For roundrects where the stroke width
89// is greater than the corner radius, the outer triangles blend from the curve to the straight
90// sides. Otherwise these triangles will be degenerate.
91//
92// In the case where the stroke width is greater than the corner radius and the
93// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
94// This rectangle extends the coverage values of the center edges of the 9-patch.
Jim Van Verthc5903412016-11-17 15:27:09 -050095// ____________
96// |_|________|_|
97// | |\ ____ /| |
98// | | | | | |
99// | | |____| | |
100// |_|/______\|_|
101// |_|________|_|
102//
Jim Van Verthb6069df2017-04-28 11:00:35 -0400103// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
Jim Van Verthc5903412016-11-17 15:27:09 -0500104
Jim Van Verthb6069df2017-04-28 11:00:35 -0400105static const uint16_t gRRectIndices[] = {
106 // clang-format off
107 // overstroke quads
108 // we place this at the beginning so that we can skip these indices when rendering as filled
109 0, 6, 25, 0, 25, 24,
110 6, 18, 27, 6, 27, 25,
111 18, 12, 26, 18, 26, 27,
112 12, 0, 24, 12, 24, 26,
Jim Van Verthc5903412016-11-17 15:27:09 -0500113
Jim Van Verthb6069df2017-04-28 11:00:35 -0400114 // corners
115 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
116 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
117 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
118 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
Jim Van Verthc5903412016-11-17 15:27:09 -0500119
Jim Van Verthb6069df2017-04-28 11:00:35 -0400120 // edges
121 0, 5, 11, 0, 11, 6,
122 6, 7, 19, 6, 19, 18,
123 18, 23, 17, 18, 17, 12,
124 12, 13, 1, 12, 1, 0,
125
126 // fill quad
127 // we place this at the end so that we can skip these indices when rendering as stroked
128 0, 6, 18, 0, 18, 12,
129 // clang-format on
Jim Van Verthc5903412016-11-17 15:27:09 -0500130};
Jim Van Verthc5903412016-11-17 15:27:09 -0500131
132// overstroke count
Jim Van Verthb6069df2017-04-28 11:00:35 -0400133static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
Jim Van Verthc5903412016-11-17 15:27:09 -0500134// simple stroke count skips overstroke indices
Jim Van Verthb6069df2017-04-28 11:00:35 -0400135static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
136// fill count adds final quad to stroke count
137static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
138static const int kVertsPerStrokeRRect = 24;
139static const int kVertsPerOverstrokeRRect = 28;
140static const int kVertsPerFillRRect = 24;
Jim Van Verthc5903412016-11-17 15:27:09 -0500141
142enum RRectType {
143 kFill_RRectType,
144 kStroke_RRectType,
145 kOverstroke_RRectType,
146};
147
148static int rrect_type_to_vert_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500149 switch (type) {
150 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400151 return kVertsPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500152 case kStroke_RRectType:
153 return kVertsPerStrokeRRect;
154 case kOverstroke_RRectType:
155 return kVertsPerOverstrokeRRect;
156 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400157 SK_ABORT("Invalid type");
Brian Salomonfc527d22016-12-14 21:07:01 -0500158 return 0;
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");
Brian Salomonfc527d22016-12-14 21:07:01 -0500171 return 0;
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");
Brian Salomonfc527d22016-12-14 21:07:01 -0500183 return nullptr;
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 Verthfb186392018-09-11 11:37:46 -0400195 float devRadius, bool isCircle, float blurRadius, float insetWidth)
Brian Salomon05969092017-07-13 11:20:51 -0400196 : INHERITED(ClassID()) {
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
Jim Van Verthcf40e302017-03-02 11:28:43 -0500224 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::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
Brian Osman5ced0bf2019-03-15 10:15:29 -0400258 GrProcessorSet::Analysis finalize(
259 const GrCaps&, const GrAppliedClip*, GrFSAAType, 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
Brian Salomon05969092017-07-13 11:20:51 -0400542 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
Jim Van Verthc5903412016-11-17 15:27:09 -0500543
544 int instanceCount = fGeoData.count();
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500545 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
Jim Van Verthc5903412016-11-17 15:27:09 -0500546
Brian Salomon12d22642019-01-29 14:38:50 -0500547 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500548 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400549 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
550 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500551 if (!verts) {
552 SkDebugf("Could not allocate vertices\n");
553 return;
554 }
555
Brian Salomon12d22642019-01-29 14:38:50 -0500556 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500557 int firstIndex = 0;
558 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
559 if (!indices) {
560 SkDebugf("Could not allocate indices\n");
561 return;
562 }
563
564 int currStartVertex = 0;
565 for (int i = 0; i < instanceCount; i++) {
566 const Geometry& args = fGeoData[i];
567
Jim Van Verth57061ee2017-04-28 17:30:30 -0400568 if (args.fIsCircle) {
569 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
570 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500571
Jim Van Verth57061ee2017-04-28 17:30:30 -0400572 const uint16_t* primIndices = circle_type_to_indices(isStroked);
573 const int primIndexCount = circle_type_to_index_count(isStroked);
574 for (int i = 0; i < primIndexCount; ++i) {
575 *indices++ = primIndices[i] + currStartVertex;
576 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500577
Jim Van Verth57061ee2017-04-28 17:30:30 -0400578 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500579
Jim Van Verth57061ee2017-04-28 17:30:30 -0400580 } else {
581 this->fillInRRectVerts(args, &verts);
582
583 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
584 const int primIndexCount = rrect_type_to_index_count(args.fType);
585 for (int i = 0; i < primIndexCount; ++i) {
586 *indices++ = primIndices[i] + currStartVertex;
587 }
588
589 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400590 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500591 }
592
Brian Salomon7eae3e02018-08-07 14:02:38 +0000593 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500594 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000595 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500596 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700597 target->recordDraw(std::move(gp), mesh);
598 }
599
600 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
601 flushState->executeDrawsAndUploadsForMeshDrawOp(
602 this, chainBounds, GrProcessorSet::MakeEmptySet());
Jim Van Verthc5903412016-11-17 15:27:09 -0500603 }
604
Brian Salomon7eae3e02018-08-07 14:02:38 +0000605 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500606 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500607 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500608 fVertCount += that->fVertCount;
609 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000610 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500611 }
612
Jim Van Verthc5903412016-11-17 15:27:09 -0500613 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500614 int fVertCount;
615 int fIndexCount;
Jim Van Verthc5903412016-11-17 15:27:09 -0500616
Brian Salomon05969092017-07-13 11:20:51 -0400617 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500618};
619
Brian Salomon05969092017-07-13 11:20:51 -0400620} // anonymous namespace
621
Jim Van Verthc5903412016-11-17 15:27:09 -0500622///////////////////////////////////////////////////////////////////////////////
623
Jim Van Verth57061ee2017-04-28 17:30:30 -0400624namespace GrShadowRRectOp {
Robert Phillipsb97da532019-02-12 15:24:12 -0500625std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400626 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400627 const SkMatrix& viewMatrix,
628 const SkRRect& rrect,
629 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400630 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500631 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500632 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400633
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500634 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500635 const SkRect& rrectBounds = rrect.getBounds();
636 SkRect bounds;
637 viewMatrix.mapRect(&bounds, rrectBounds);
638
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400639 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500640 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400641 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
642 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
643 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500644
Robert Phillipse5763782019-04-17 14:38:24 -0400645 if (scaledInsetWidth <= 0) {
646 return nullptr;
647 }
648
Robert Phillips9da87e02019-02-04 13:26:26 -0500649 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400650
651 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
652 scaledRadius,
653 rrect.isOval(),
654 blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400655 scaledInsetWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500656}
Brian Salomonfc527d22016-12-14 21:07:01 -0500657}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400658
Jim Van Verthc5903412016-11-17 15:27:09 -0500659///////////////////////////////////////////////////////////////////////////////
660
Hal Canary6f6961e2017-01-31 13:50:44 -0500661#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500662
Brian Salomon05969092017-07-13 11:20:51 -0400663GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400664 // create a similarity matrix
665 SkScalar rotate = random->nextSScalar1() * 360.f;
666 SkScalar translateX = random->nextSScalar1() * 1000.f;
667 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -0400668 SkScalar scale;
669 do {
670 scale = random->nextSScalar1() * 100.f;
671 } while (scale == 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400672 SkMatrix viewMatrix;
673 viewMatrix.setRotate(rotate);
674 viewMatrix.postTranslate(translateX, translateY);
675 viewMatrix.postScale(scale, scale);
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400676 SkScalar insetWidth = random->nextSScalar1() * 72.f;
677 SkScalar blurWidth = random->nextSScalar1() * 72.f;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400678 bool isCircle = random->nextBool();
Brian Salomon05969092017-07-13 11:20:51 -0400679 // This op doesn't use a full GrPaint, just a color.
Brian Osman00b29392018-11-05 15:42:43 -0500680 GrColor color = paint.getColor4f().toBytes_RGBA();
Jim Van Verth57061ee2017-04-28 17:30:30 -0400681 if (isCircle) {
682 SkRect circle = GrTest::TestSquare(random);
683 SkRRect rrect = SkRRect::MakeOval(circle);
Jim Van Verthfb186392018-09-11 11:37:46 -0400684 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400685 } else {
Brian Salomon05969092017-07-13 11:20:51 -0400686 SkRRect rrect;
687 do {
688 // This may return a rrect with elliptical corners, which we don't support.
689 rrect = GrTest::TestRRectSimple(random);
Mike Reed242135a2018-02-22 13:41:39 -0500690 } while (!SkRRectPriv::IsSimpleCircular(rrect));
Jim Van Verthfb186392018-09-11 11:37:46 -0400691 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400692 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500693}
694
695#endif