blob: dd091a34500d85074147b645d4b77e430a5b9e2f [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
10#include "GrContext.h"
11#include "GrContextPriv.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050012#include "GrDrawOpTest.h"
Robert Phillips7c525e62018-06-12 10:11:12 -040013#include "GrMemoryPool.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050014#include "GrOpFlushState.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
Chris Dalton4b62aed2019-01-15 11:53:00 -0700258 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*) override {
259 return GrProcessorSet::EmptySetAnalysis();
Brian Salomon05969092017-07-13 11:20:51 -0400260 }
261
Brian Salomon92aee3d2016-12-21 09:20:25 -0500262private:
Jim Van Verth57061ee2017-04-28 17:30:30 -0400263 struct Geometry {
264 GrColor fColor;
265 SkScalar fOuterRadius;
266 SkScalar fUmbraInset;
267 SkScalar fInnerRadius;
268 SkScalar fBlurRadius;
269 SkRect fDevBounds;
270 RRectType fType;
271 bool fIsCircle;
272 };
273
Jim Van Verthc5903412016-11-17 15:27:09 -0500274 struct CircleVertex {
Brian Salomonfc527d22016-12-14 21:07:01 -0500275 SkPoint fPos;
276 GrColor fColor;
277 SkPoint fOffset;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400278 SkScalar fDistanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500279 };
280
Jim Van Verth57061ee2017-04-28 17:30:30 -0400281 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
282
283 GrColor color = args.fColor;
284 SkScalar outerRadius = args.fOuterRadius;
285 SkScalar innerRadius = args.fInnerRadius;
286 SkScalar blurRadius = args.fBlurRadius;
287 SkScalar distanceCorrection = outerRadius / blurRadius;
288
289 const SkRect& bounds = args.fDevBounds;
290
291 // The inner radius in the vertex data must be specified in normalized space.
292 innerRadius = innerRadius / outerRadius;
293
294 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
295 SkScalar halfWidth = 0.5f * bounds.width();
296 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
297
298 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500299 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400300 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400301 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500302 (*verts)++;
303
Jim Van Verth57061ee2017-04-28 17:30:30 -0400304 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500305 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400306 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400307 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500308 (*verts)++;
309
Jim Van Verth57061ee2017-04-28 17:30:30 -0400310 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500311 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400312 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400313 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500314 (*verts)++;
315
Jim Van Verth57061ee2017-04-28 17:30:30 -0400316 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500317 (*verts)->fColor = color;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400318 (*verts)->fOffset = SkPoint::Make(1, octOffset);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400319 (*verts)->fDistanceCorrection = distanceCorrection;
Jim Van Verthc5903412016-11-17 15:27:09 -0500320 (*verts)++;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400321
322 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
323 (*verts)->fColor = color;
324 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
325 (*verts)->fDistanceCorrection = distanceCorrection;
326 (*verts)++;
327
328 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
329 (*verts)->fColor = color;
330 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
331 (*verts)->fDistanceCorrection = distanceCorrection;
332 (*verts)++;
333
334 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
335 (*verts)->fColor = color;
336 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
337 (*verts)->fDistanceCorrection = distanceCorrection;
338 (*verts)++;
339
340 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
341 (*verts)->fColor = color;
342 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
343 (*verts)->fDistanceCorrection = distanceCorrection;
344 (*verts)++;
345
346 if (isStroked) {
347 // compute the inner ring
348
349 // cosine and sine of pi/8
350 SkScalar c = 0.923579533f;
351 SkScalar s = 0.382683432f;
352 SkScalar r = args.fInnerRadius;
353
354 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
355 (*verts)->fColor = color;
356 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
357 (*verts)->fDistanceCorrection = distanceCorrection;
358 (*verts)++;
359
360 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
361 (*verts)->fColor = color;
362 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
363 (*verts)->fDistanceCorrection = distanceCorrection;
364 (*verts)++;
365
366 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
367 (*verts)->fColor = color;
368 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
369 (*verts)->fDistanceCorrection = distanceCorrection;
370 (*verts)++;
371
372 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
373 (*verts)->fColor = color;
374 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
375 (*verts)->fDistanceCorrection = distanceCorrection;
376 (*verts)++;
377
378 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
379 (*verts)->fColor = color;
380 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
381 (*verts)->fDistanceCorrection = distanceCorrection;
382 (*verts)++;
383
384 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
385 (*verts)->fColor = color;
386 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
387 (*verts)->fDistanceCorrection = distanceCorrection;
388 (*verts)++;
389
390 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
391 (*verts)->fColor = color;
392 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
393 (*verts)->fDistanceCorrection = distanceCorrection;
394 (*verts)++;
395
396 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
397 (*verts)->fColor = color;
398 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
399 (*verts)->fDistanceCorrection = distanceCorrection;
400 (*verts)++;
401 } else {
402 // filled
403 (*verts)->fPos = center;
404 (*verts)->fColor = color;
405 (*verts)->fOffset = SkPoint::Make(0, 0);
406 (*verts)->fDistanceCorrection = distanceCorrection;
407 (*verts)++;
408 }
409 }
410
411 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
412 GrColor color = args.fColor;
413 SkScalar outerRadius = args.fOuterRadius;
414
415 const SkRect& bounds = args.fDevBounds;
416
417 SkScalar umbraInset = args.fUmbraInset;
418 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
419 if (umbraInset > minDim) {
420 umbraInset = minDim;
421 }
422
423 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
424 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
425 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
426 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
427 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
428 bounds.fLeft, bounds.fRight };
429 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
430 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
431 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
432 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
433 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
434 bounds.fBottom, bounds.fBottom };
435
436 SkScalar blurRadius = args.fBlurRadius;
437
438 // In the case where we have to inset more for the umbra, our two triangles in the
439 // corner get skewed to a diamond rather than a square. To correct for that,
440 // we also skew the vectors we send to the shader that help define the circle.
441 // By doing so, we end up with a quarter circle in the corner rather than the
442 // elliptical curve.
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400443
444 // This is a bit magical, but it gives us the correct results at extrema:
445 // a) umbraInset == outerRadius produces an orthogonal vector
446 // b) outerRadius == 0 produces a diagonal vector
447 // And visually the corner looks correct.
448 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400449 outerVec.normalize();
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400450 // We want the circle edge to fall fractionally along the diagonal at
451 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
452 //
453 // Setting the components of the diagonal offset to the following value will give us that.
454 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
455 SkVector diagVec = SkVector::Make(diagVal, diagVal);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400456 SkScalar distanceCorrection = umbraInset / blurRadius;
457
458 // build corner by corner
459 for (int i = 0; i < 4; ++i) {
460 // inner point
461 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
462 (*verts)->fColor = color;
463 (*verts)->fOffset = SkVector::Make(0, 0);
464 (*verts)->fDistanceCorrection = distanceCorrection;
465 (*verts)++;
466
467 // outer points
468 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
469 (*verts)->fColor = color;
470 (*verts)->fOffset = SkVector::Make(0, -1);
471 (*verts)->fDistanceCorrection = distanceCorrection;
472 (*verts)++;
473
474 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
475 (*verts)->fColor = color;
476 (*verts)->fOffset = outerVec;
477 (*verts)->fDistanceCorrection = distanceCorrection;
478 (*verts)++;
479
480 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
481 (*verts)->fColor = color;
482 (*verts)->fOffset = diagVec;
483 (*verts)->fDistanceCorrection = distanceCorrection;
484 (*verts)++;
485
486 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
487 (*verts)->fColor = color;
488 (*verts)->fOffset = outerVec;
489 (*verts)->fDistanceCorrection = distanceCorrection;
490 (*verts)++;
491
492 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
493 (*verts)->fColor = color;
494 (*verts)->fOffset = SkVector::Make(0, -1);
495 (*verts)->fDistanceCorrection = distanceCorrection;
496 (*verts)++;
497 }
498
499 // Add the additional vertices for overstroked rrects.
500 // Effectively this is an additional stroked rrect, with its
501 // parameters equal to those in the center of the 9-patch. This will
502 // give constant values across this inner ring.
503 if (kOverstroke_RRectType == args.fType) {
504 SkASSERT(args.fInnerRadius > 0.0f);
505
506 SkScalar inset = umbraInset + args.fInnerRadius;
507
508 // TL
509 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
510 (*verts)->fColor = color;
511 (*verts)->fOffset = SkPoint::Make(0, 0);
512 (*verts)->fDistanceCorrection = distanceCorrection;
513 (*verts)++;
514
515 // TR
516 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
517 (*verts)->fColor = color;
518 (*verts)->fOffset = SkPoint::Make(0, 0);
519 (*verts)->fDistanceCorrection = distanceCorrection;
520 (*verts)++;
521
522 // BL
523 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
524 (*verts)->fColor = color;
525 (*verts)->fOffset = SkPoint::Make(0, 0);
526 (*verts)->fDistanceCorrection = distanceCorrection;
527 (*verts)++;
528
529 // BR
530 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
531 (*verts)->fColor = color;
532 (*verts)->fOffset = SkPoint::Make(0, 0);
533 (*verts)->fDistanceCorrection = distanceCorrection;
534 (*verts)++;
535 }
536
Jim Van Verthc5903412016-11-17 15:27:09 -0500537 }
538
Brian Salomon91326c32017-08-09 16:02:19 -0400539 void onPrepareDraws(Target* target) override {
Jim Van Verthc5903412016-11-17 15:27:09 -0500540 // Setup geometry processor
Brian Salomon05969092017-07-13 11:20:51 -0400541 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
Jim Van Verthc5903412016-11-17 15:27:09 -0500542
543 int instanceCount = fGeoData.count();
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500544 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
Jim Van Verthc5903412016-11-17 15:27:09 -0500545
Brian Salomon12d22642019-01-29 14:38:50 -0500546 sk_sp<const GrBuffer> vertexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500547 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -0400548 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
549 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
Jim Van Verthc5903412016-11-17 15:27:09 -0500550 if (!verts) {
551 SkDebugf("Could not allocate vertices\n");
552 return;
553 }
554
Brian Salomon12d22642019-01-29 14:38:50 -0500555 sk_sp<const GrBuffer> indexBuffer;
Jim Van Verthc5903412016-11-17 15:27:09 -0500556 int firstIndex = 0;
557 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
558 if (!indices) {
559 SkDebugf("Could not allocate indices\n");
560 return;
561 }
562
563 int currStartVertex = 0;
564 for (int i = 0; i < instanceCount; i++) {
565 const Geometry& args = fGeoData[i];
566
Jim Van Verth57061ee2017-04-28 17:30:30 -0400567 if (args.fIsCircle) {
568 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
569 this->fillInCircleVerts(args, isStroked, &verts);
Jim Van Verthc5903412016-11-17 15:27:09 -0500570
Jim Van Verth57061ee2017-04-28 17:30:30 -0400571 const uint16_t* primIndices = circle_type_to_indices(isStroked);
572 const int primIndexCount = circle_type_to_index_count(isStroked);
573 for (int i = 0; i < primIndexCount; ++i) {
574 *indices++ = primIndices[i] + currStartVertex;
575 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500576
Jim Van Verth57061ee2017-04-28 17:30:30 -0400577 currStartVertex += circle_type_to_vert_count(isStroked);
Jim Van Verthc5903412016-11-17 15:27:09 -0500578
Jim Van Verth57061ee2017-04-28 17:30:30 -0400579 } else {
580 this->fillInRRectVerts(args, &verts);
581
582 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
583 const int primIndexCount = rrect_type_to_index_count(args.fType);
584 for (int i = 0; i < primIndexCount; ++i) {
585 *indices++ = primIndices[i] + currStartVertex;
586 }
587
588 currStartVertex += rrect_type_to_vert_count(args.fType);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400589 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500590 }
591
Brian Salomon05969092017-07-13 11:20:51 -0400592 static const uint32_t kPipelineFlags = 0;
Brian Salomon49348902018-06-26 09:12:38 -0400593 auto pipe = target->makePipeline(kPipelineFlags, GrProcessorSet::MakeEmptySet(),
594 target->detachAppliedClip());
Brian Salomon05969092017-07-13 11:20:51 -0400595
Brian Salomon7eae3e02018-08-07 14:02:38 +0000596 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500597 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000598 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -0500599 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Brian Salomon7eae3e02018-08-07 14:02:38 +0000600 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Jim Van Verthc5903412016-11-17 15:27:09 -0500601 }
602
Brian Salomon7eae3e02018-08-07 14:02:38 +0000603 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500604 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500605 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
Jim Van Verthc5903412016-11-17 15:27:09 -0500606 fVertCount += that->fVertCount;
607 fIndexCount += that->fIndexCount;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000608 return CombineResult::kMerged;
Jim Van Verthc5903412016-11-17 15:27:09 -0500609 }
610
Jim Van Verthc5903412016-11-17 15:27:09 -0500611 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomonfc527d22016-12-14 21:07:01 -0500612 int fVertCount;
613 int fIndexCount;
Jim Van Verthc5903412016-11-17 15:27:09 -0500614
Brian Salomon05969092017-07-13 11:20:51 -0400615 typedef GrMeshDrawOp INHERITED;
Jim Van Verthc5903412016-11-17 15:27:09 -0500616};
617
Brian Salomon05969092017-07-13 11:20:51 -0400618} // anonymous namespace
619
Jim Van Verthc5903412016-11-17 15:27:09 -0500620///////////////////////////////////////////////////////////////////////////////
621
Jim Van Verth57061ee2017-04-28 17:30:30 -0400622namespace GrShadowRRectOp {
Robert Phillips7c525e62018-06-12 10:11:12 -0400623std::unique_ptr<GrDrawOp> Make(GrContext* context,
624 GrColor color,
Brian Salomon05969092017-07-13 11:20:51 -0400625 const SkMatrix& viewMatrix,
626 const SkRRect& rrect,
627 SkScalar blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400628 SkScalar insetWidth) {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500629 // Shadow rrect ops only handle simple circular rrects.
Mike Reed242135a2018-02-22 13:41:39 -0500630 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
Jim Van Verth57061ee2017-04-28 17:30:30 -0400631
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500632 // Do any matrix crunching before we reset the draw state for device coords.
Jim Van Verthc5903412016-11-17 15:27:09 -0500633 const SkRect& rrectBounds = rrect.getBounds();
634 SkRect bounds;
635 viewMatrix.mapRect(&bounds, rrectBounds);
636
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400637 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
Mike Reed242135a2018-02-22 13:41:39 -0500638 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400639 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
640 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
641 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
Jim Van Verthc5903412016-11-17 15:27:09 -0500642
Robert Phillips9da87e02019-02-04 13:26:26 -0500643 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400644
645 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
646 scaledRadius,
647 rrect.isOval(),
648 blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400649 scaledInsetWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500650}
Brian Salomonfc527d22016-12-14 21:07:01 -0500651}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400652
Jim Van Verthc5903412016-11-17 15:27:09 -0500653///////////////////////////////////////////////////////////////////////////////
654
Hal Canary6f6961e2017-01-31 13:50:44 -0500655#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500656
Brian Salomon05969092017-07-13 11:20:51 -0400657GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400658 // create a similarity matrix
659 SkScalar rotate = random->nextSScalar1() * 360.f;
660 SkScalar translateX = random->nextSScalar1() * 1000.f;
661 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -0400662 SkScalar scale;
663 do {
664 scale = random->nextSScalar1() * 100.f;
665 } while (scale == 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400666 SkMatrix viewMatrix;
667 viewMatrix.setRotate(rotate);
668 viewMatrix.postTranslate(translateX, translateY);
669 viewMatrix.postScale(scale, scale);
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400670 SkScalar insetWidth = random->nextSScalar1() * 72.f;
671 SkScalar blurWidth = random->nextSScalar1() * 72.f;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400672 bool isCircle = random->nextBool();
Brian Salomon05969092017-07-13 11:20:51 -0400673 // This op doesn't use a full GrPaint, just a color.
Brian Osman00b29392018-11-05 15:42:43 -0500674 GrColor color = paint.getColor4f().toBytes_RGBA();
Jim Van Verth57061ee2017-04-28 17:30:30 -0400675 if (isCircle) {
676 SkRect circle = GrTest::TestSquare(random);
677 SkRRect rrect = SkRRect::MakeOval(circle);
Jim Van Verthfb186392018-09-11 11:37:46 -0400678 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400679 } else {
Brian Salomon05969092017-07-13 11:20:51 -0400680 SkRRect rrect;
681 do {
682 // This may return a rrect with elliptical corners, which we don't support.
683 rrect = GrTest::TestRRectSimple(random);
Mike Reed242135a2018-02-22 13:41:39 -0500684 } while (!SkRRectPriv::IsSimpleCircular(rrect));
Jim Van Verthfb186392018-09-11 11:37:46 -0400685 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400686 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500687}
688
689#endif