blob: ad4e7c1fedf8922a666d1eeed4bb196a9667bce0 [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"
15#include "src/gpu/GrRecordingContextPriv.h"
16#include "src/gpu/effects/GrShadowGeoProc.h"
Jim Van Verthc5903412016-11-17 15:27:09 -050017
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");
Jim Van Verthc5903412016-11-17 15:27:09 -0500158}
159
160static int rrect_type_to_index_count(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500161 switch (type) {
162 case kFill_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400163 return kIndicesPerFillRRect;
Brian Salomonfc527d22016-12-14 21:07:01 -0500164 case kStroke_RRectType:
165 return kIndicesPerStrokeRRect;
166 case kOverstroke_RRectType:
167 return kIndicesPerOverstrokeRRect;
168 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400169 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500170}
171
172static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500173 switch (type) {
174 case kFill_RRectType:
Brian Salomonfc527d22016-12-14 21:07:01 -0500175 case kStroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400176 return gRRectIndices + 6*4;
Brian Salomonfc527d22016-12-14 21:07:01 -0500177 case kOverstroke_RRectType:
Jim Van Verthb6069df2017-04-28 11:00:35 -0400178 return gRRectIndices;
Brian Salomonfc527d22016-12-14 21:07:01 -0500179 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400180 SK_ABORT("Invalid type");
Jim Van Verthc5903412016-11-17 15:27:09 -0500181}
182
Jim Van Verth57061ee2017-04-28 17:30:30 -0400183///////////////////////////////////////////////////////////////////////////////
Brian Salomon05969092017-07-13 11:20:51 -0400184namespace {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400185
Brian Salomon05969092017-07-13 11:20:51 -0400186class ShadowCircularRRectOp final : public GrMeshDrawOp {
Jim Van Verthc5903412016-11-17 15:27:09 -0500187public:
Brian Salomon25a88092016-12-01 09:36:50 -0500188 DEFINE_OP_CLASS_ID
Jim Van Verthc5903412016-11-17 15:27:09 -0500189
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400190 // An insetWidth > 1/2 rect width or height indicates a simple fill.
Brian Salomon05969092017-07-13 11:20:51 -0400191 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
Jim Van Verthfb186392018-09-11 11:37:46 -0400192 float devRadius, bool isCircle, float blurRadius, float insetWidth)
Brian Salomon05969092017-07-13 11:20:51 -0400193 : INHERITED(ClassID()) {
Jim Van Verthc5903412016-11-17 15:27:09 -0500194 SkRect bounds = devRect;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400195 SkASSERT(insetWidth > 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400196 SkScalar innerRadius = 0.0f;
Jim Van Verthc5903412016-11-17 15:27:09 -0500197 SkScalar outerRadius = devRadius;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400198 SkScalar umbraInset;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400199
200 RRectType type = kFill_RRectType;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400201 if (isCircle) {
202 umbraInset = 0;
203 } else {
204 umbraInset = SkTMax(outerRadius, blurRadius);
205 }
206
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400207 // If stroke is greater than width or height, this is still a fill,
208 // otherwise we compute stroke params.
209 if (isCircle) {
210 innerRadius = devRadius - insetWidth;
211 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
212 } else {
213 if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
214 // We don't worry about a real inner radius, we just need to know if we
215 // need to create overstroke vertices.
216 innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
217 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400218 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500219 }
220
Jim Van Verthcf40e302017-03-02 11:28:43 -0500221 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
Jim Van Verthc5903412016-11-17 15:27:09 -0500222
Jim Van Verth57061ee2017-04-28 17:30:30 -0400223 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
Jim Van Verthfb186392018-09-11 11:37:46 -0400224 blurRadius, bounds, type, isCircle});
Jim Van Verth57061ee2017-04-28 17:30:30 -0400225 if (isCircle) {
226 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
227 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
228 } else {
229 fVertCount = rrect_type_to_vert_count(type);
230 fIndexCount = rrect_type_to_index_count(type);
231 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500232 }
233
Brian Salomonfc527d22016-12-14 21:07:01 -0500234 const char* name() const override { return "ShadowCircularRRectOp"; }
Jim Van Verthc5903412016-11-17 15:27:09 -0500235
Brian Osman9a390ac2018-11-12 09:47:48 -0500236#ifdef SK_DEBUG
Jim Van Verthc5903412016-11-17 15:27:09 -0500237 SkString dumpInfo() const override {
238 SkString string;
239 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500240 string.appendf(
241 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
Jim Van Verth57061ee2017-04-28 17:30:30 -0400242 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
Brian Salomonfc527d22016-12-14 21:07:01 -0500243 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
244 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
Jim Van Verthb6069df2017-04-28 11:00:35 -0400245 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
Jim Van Verth57061ee2017-04-28 17:30:30 -0400246 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
Jim Van Verthc5903412016-11-17 15:27:09 -0500247 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500248 string.append(INHERITED::dumpInfo());
249 return string;
250 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500251#endif
Jim Van Verthc5903412016-11-17 15:27:09 -0500252
Brian Salomon05969092017-07-13 11:20:51 -0400253 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
254
Chris Dalton6ce447a2019-06-23 18:07:38 -0600255 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
256 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700257 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400258 }
259
Brian Salomon92aee3d2016-12-21 09:20:25 -0500260private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400261 struct Geometry {
262 GrColor fColor;
263 SkScalar fOuterRadius;
264 SkScalar fUmbraInset;
265 SkScalar fInnerRadius;
266 SkScalar fBlurRadius;
267 SkRect fDevBounds;
268 RRectType fType;
269 bool fIsCircle;
270 };
271
Jim Van Verthc5903412016-11-17 15:27:09 -0500272 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500273 SkPoint fPos;
274 GrColor fColor;
275 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400276 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500277 };
278
Jim Van Verth57061ee2017-04-28 17:30:30 -0400279 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
280
281 GrColor color = args.fColor;
282 SkScalar outerRadius = args.fOuterRadius;
283 SkScalar innerRadius = args.fInnerRadius;
284 SkScalar blurRadius = args.fBlurRadius;
285 SkScalar distanceCorrection = outerRadius / blurRadius;
286
287 const SkRect& bounds = args.fDevBounds;
288
289 // The inner radius in the vertex data must be specified in normalized space.
290 innerRadius = innerRadius / outerRadius;
291
292 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
293 SkScalar halfWidth = 0.5f * bounds.width();
294 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
295
296 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500297 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400298 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400299 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500300 (*verts)++;
301
Jim Van Verth57061ee2017-04-28 17:30:30 -0400302 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500303 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400304 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400305 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500306 (*verts)++;
307
Jim Van Verth57061ee2017-04-28 17:30:30 -0400308 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500309 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400310 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400311 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500312 (*verts)++;
313
Jim Van Verth57061ee2017-04-28 17:30:30 -0400314 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500315 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400316 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400317 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500318 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400319
320 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
321 (*verts)->fColor = color;
322 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
323 (*verts)->fDistanceCorrection = distanceCorrection;
324 (*verts)++;
325
326 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
327 (*verts)->fColor = color;
328 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
329 (*verts)->fDistanceCorrection = distanceCorrection;
330 (*verts)++;
331
332 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
333 (*verts)->fColor = color;
334 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
335 (*verts)->fDistanceCorrection = distanceCorrection;
336 (*verts)++;
337
338 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
339 (*verts)->fColor = color;
340 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
341 (*verts)->fDistanceCorrection = distanceCorrection;
342 (*verts)++;
343
344 if (isStroked) {
345 // compute the inner ring
346
347 // cosine and sine of pi/8
348 SkScalar c = 0.923579533f;
349 SkScalar s = 0.382683432f;
350 SkScalar r = args.fInnerRadius;
351
352 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
353 (*verts)->fColor = color;
354 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
355 (*verts)->fDistanceCorrection = distanceCorrection;
356 (*verts)++;
357
358 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
359 (*verts)->fColor = color;
360 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
361 (*verts)->fDistanceCorrection = distanceCorrection;
362 (*verts)++;
363
364 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
365 (*verts)->fColor = color;
366 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
367 (*verts)->fDistanceCorrection = distanceCorrection;
368 (*verts)++;
369
370 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
371 (*verts)->fColor = color;
372 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
373 (*verts)->fDistanceCorrection = distanceCorrection;
374 (*verts)++;
375
376 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
377 (*verts)->fColor = color;
378 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
379 (*verts)->fDistanceCorrection = distanceCorrection;
380 (*verts)++;
381
382 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
383 (*verts)->fColor = color;
384 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
385 (*verts)->fDistanceCorrection = distanceCorrection;
386 (*verts)++;
387
388 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
389 (*verts)->fColor = color;
390 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
391 (*verts)->fDistanceCorrection = distanceCorrection;
392 (*verts)++;
393
394 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
395 (*verts)->fColor = color;
396 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
397 (*verts)->fDistanceCorrection = distanceCorrection;
398 (*verts)++;
399 } else {
400 // filled
401 (*verts)->fPos = center;
402 (*verts)->fColor = color;
403 (*verts)->fOffset = SkPoint::Make(0, 0);
404 (*verts)->fDistanceCorrection = distanceCorrection;
405 (*verts)++;
406 }
407 }
408
409 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
410 GrColor color = args.fColor;
411 SkScalar outerRadius = args.fOuterRadius;
412
413 const SkRect& bounds = args.fDevBounds;
414
415 SkScalar umbraInset = args.fUmbraInset;
416 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
417 if (umbraInset > minDim) {
418 umbraInset = minDim;
419 }
420
421 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
422 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
423 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
424 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
425 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
426 bounds.fLeft, bounds.fRight };
427 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
428 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
429 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
430 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
431 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
432 bounds.fBottom, bounds.fBottom };
433
434 SkScalar blurRadius = args.fBlurRadius;
435
436 // In the case where we have to inset more for the umbra, our two triangles in the
437 // corner get skewed to a diamond rather than a square. To correct for that,
438 // we also skew the vectors we send to the shader that help define the circle.
439 // By doing so, we end up with a quarter circle in the corner rather than the
440 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400441
442 // This is a bit magical, but it gives us the correct results at extrema:
443 // a) umbraInset == outerRadius produces an orthogonal vector
444 // b) outerRadius == 0 produces a diagonal vector
445 // And visually the corner looks correct.
446 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400447 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400448 // We want the circle edge to fall fractionally along the diagonal at
449 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
450 //
451 // Setting the components of the diagonal offset to the following value will give us that.
452 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
453 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400454 SkScalar distanceCorrection = umbraInset / blurRadius;
455
456 // build corner by corner
457 for (int i = 0; i < 4; ++i) {
458 // inner point
459 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
460 (*verts)->fColor = color;
461 (*verts)->fOffset = SkVector::Make(0, 0);
462 (*verts)->fDistanceCorrection = distanceCorrection;
463 (*verts)++;
464
465 // outer points
466 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
467 (*verts)->fColor = color;
468 (*verts)->fOffset = SkVector::Make(0, -1);
469 (*verts)->fDistanceCorrection = distanceCorrection;
470 (*verts)++;
471
472 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
473 (*verts)->fColor = color;
474 (*verts)->fOffset = outerVec;
475 (*verts)->fDistanceCorrection = distanceCorrection;
476 (*verts)++;
477
478 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
479 (*verts)->fColor = color;
480 (*verts)->fOffset = diagVec;
481 (*verts)->fDistanceCorrection = distanceCorrection;
482 (*verts)++;
483
484 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
485 (*verts)->fColor = color;
486 (*verts)->fOffset = outerVec;
487 (*verts)->fDistanceCorrection = distanceCorrection;
488 (*verts)++;
489
490 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
491 (*verts)->fColor = color;
492 (*verts)->fOffset = SkVector::Make(0, -1);
493 (*verts)->fDistanceCorrection = distanceCorrection;
494 (*verts)++;
495 }
496
497 // Add the additional vertices for overstroked rrects.
498 // Effectively this is an additional stroked rrect, with its
499 // parameters equal to those in the center of the 9-patch. This will
500 // give constant values across this inner ring.
501 if (kOverstroke_RRectType == args.fType) {
502 SkASSERT(args.fInnerRadius > 0.0f);
503
504 SkScalar inset = umbraInset + args.fInnerRadius;
505
506 // TL
507 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
508 (*verts)->fColor = color;
509 (*verts)->fOffset = SkPoint::Make(0, 0);
510 (*verts)->fDistanceCorrection = distanceCorrection;
511 (*verts)++;
512
513 // TR
514 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
515 (*verts)->fColor = color;
516 (*verts)->fOffset = SkPoint::Make(0, 0);
517 (*verts)->fDistanceCorrection = distanceCorrection;
518 (*verts)++;
519
520 // BL
521 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
522 (*verts)->fColor = color;
523 (*verts)->fOffset = SkPoint::Make(0, 0);
524 (*verts)->fDistanceCorrection = distanceCorrection;
525 (*verts)++;
526
527 // BR
528 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
529 (*verts)->fColor = color;
530 (*verts)->fOffset = SkPoint::Make(0, 0);
531 (*verts)->fDistanceCorrection = distanceCorrection;
532 (*verts)++;
533 }
534
Jim Van Verthc5903412016-11-17 15:27:09 -0500535 }
536
Brian Salomon91326c32017-08-09 16:02:19 -0400537 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500538 // Setup geometry processor
Brian Salomon05969092017-07-13 11:20:51 -0400539 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
Jim Van Verthc5903412016-11-17 15:27:09 -0500540
541 int instanceCount = fGeoData.count();
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500542 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
Jim Van Verthc5903412016-11-17 15:27:09 -0500543
Brian Salomon12d22642019-01-29 14:38:50 -0500544 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500545 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400546 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
547 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500548 if (!verts) {
549 SkDebugf("Could not allocate vertices\n");
550 return;
551 }
552
Brian Salomon12d22642019-01-29 14:38:50 -0500553 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500554 int firstIndex = 0;
555 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
556 if (!indices) {
557 SkDebugf("Could not allocate indices\n");
558 return;
559 }
560
561 int currStartVertex = 0;
562 for (int i = 0; i < instanceCount; i++) {
563 const Geometry& args = fGeoData[i];
564
Jim Van Verth57061ee2017-04-28 17:30:30 -0400565 if (args.fIsCircle) {
566 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
567 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500568
Jim Van Verth57061ee2017-04-28 17:30:30 -0400569 const uint16_t* primIndices = circle_type_to_indices(isStroked);
570 const int primIndexCount = circle_type_to_index_count(isStroked);
571 for (int i = 0; i < primIndexCount; ++i) {
572 *indices++ = primIndices[i] + currStartVertex;
573 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500574
Jim Van Verth57061ee2017-04-28 17:30:30 -0400575 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500576
Jim Van Verth57061ee2017-04-28 17:30:30 -0400577 } else {
578 this->fillInRRectVerts(args, &verts);
579
580 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
581 const int primIndexCount = rrect_type_to_index_count(args.fType);
582 for (int i = 0; i < primIndexCount; ++i) {
583 *indices++ = primIndices[i] + currStartVertex;
584 }
585
586 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400587 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500588 }
589
Brian Salomon7eae3e02018-08-07 14:02:38 +0000590 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500591 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000592 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500593 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700594 target->recordDraw(std::move(gp), mesh);
595 }
596
597 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
598 flushState->executeDrawsAndUploadsForMeshDrawOp(
599 this, chainBounds, GrProcessorSet::MakeEmptySet());
Jim Van Verthc5903412016-11-17 15:27:09 -0500600 }
601
Brian Salomon7eae3e02018-08-07 14:02:38 +0000602 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500603 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500604 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500605 fVertCount += that->fVertCount;
606 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000607 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500608 }
609
Jim Van Verthc5903412016-11-17 15:27:09 -0500610 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500611 int fVertCount;
612 int fIndexCount;
Jim Van Verthc5903412016-11-17 15:27:09 -0500613
Brian Salomon05969092017-07-13 11:20:51 -0400614 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500615};
616
Brian Salomon05969092017-07-13 11:20:51 -0400617} // anonymous namespace
618
Jim Van Verthc5903412016-11-17 15:27:09 -0500619///////////////////////////////////////////////////////////////////////////////
620
Jim Van Verth57061ee2017-04-28 17:30:30 -0400621namespace GrShadowRRectOp {
Robert Phillipsb97da532019-02-12 15:24:12 -0500622std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400623 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400624 const SkMatrix& viewMatrix,
625 const SkRRect& rrect,
626 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400627 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500628 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500629 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400630
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500631 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500632 const SkRect& rrectBounds = rrect.getBounds();
633 SkRect bounds;
634 viewMatrix.mapRect(&bounds, rrectBounds);
635
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400636 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500637 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400638 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
639 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
640 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500641
Robert Phillipse5763782019-04-17 14:38:24 -0400642 if (scaledInsetWidth <= 0) {
643 return nullptr;
644 }
645
Robert Phillips9da87e02019-02-04 13:26:26 -0500646 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400647
648 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
649 scaledRadius,
650 rrect.isOval(),
651 blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400652 scaledInsetWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500653}
Brian Salomonfc527d22016-12-14 21:07:01 -0500654}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400655
Jim Van Verthc5903412016-11-17 15:27:09 -0500656///////////////////////////////////////////////////////////////////////////////
657
Hal Canary6f6961e2017-01-31 13:50:44 -0500658#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500659
Brian Salomon05969092017-07-13 11:20:51 -0400660GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400661 // create a similarity matrix
662 SkScalar rotate = random->nextSScalar1() * 360.f;
663 SkScalar translateX = random->nextSScalar1() * 1000.f;
664 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -0400665 SkScalar scale;
666 do {
667 scale = random->nextSScalar1() * 100.f;
668 } while (scale == 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400669 SkMatrix viewMatrix;
670 viewMatrix.setRotate(rotate);
671 viewMatrix.postTranslate(translateX, translateY);
672 viewMatrix.postScale(scale, scale);
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400673 SkScalar insetWidth = random->nextSScalar1() * 72.f;
674 SkScalar blurWidth = random->nextSScalar1() * 72.f;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400675 bool isCircle = random->nextBool();
Brian Salomon05969092017-07-13 11:20:51 -0400676 // This op doesn't use a full GrPaint, just a color.
Brian Osman00b29392018-11-05 15:42:43 -0500677 GrColor color = paint.getColor4f().toBytes_RGBA();
Jim Van Verth57061ee2017-04-28 17:30:30 -0400678 if (isCircle) {
679 SkRect circle = GrTest::TestSquare(random);
680 SkRRect rrect = SkRRect::MakeOval(circle);
Jim Van Verthfb186392018-09-11 11:37:46 -0400681 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400682 } else {
Brian Salomon05969092017-07-13 11:20:51 -0400683 SkRRect rrect;
684 do {
685 // This may return a rrect with elliptical corners, which we don't support.
686 rrect = GrTest::TestRRectSimple(random);
Mike Reed242135a2018-02-22 13:41:39 -0500687 } while (!SkRRectPriv::IsSimpleCircular(rrect));
Jim Van Verthfb186392018-09-11 11:37:46 -0400688 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400689 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500690}
691
692#endif