blob: 0a7d191fd701840d613e633f1af1838b59d139e7 [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
239 SkString dumpInfo() const override {
240 SkString string;
241 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomonfc527d22016-12-14 21:07:01 -0500242 string.appendf(
243 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
Jim Van Verth57061ee2017-04-28 17:30:30 -0400244 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
Brian Salomonfc527d22016-12-14 21:07:01 -0500245 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
246 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
Jim Van Verthb6069df2017-04-28 11:00:35 -0400247 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
Jim Van Verth57061ee2017-04-28 17:30:30 -0400248 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
Jim Van Verthc5903412016-11-17 15:27:09 -0500249 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500250 string.append(INHERITED::dumpInfo());
251 return string;
252 }
253
Brian Salomon05969092017-07-13 11:20:51 -0400254 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
255
Brian Osman532b3f92018-07-11 10:02:07 -0400256 RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override {
Brian Salomon05969092017-07-13 11:20:51 -0400257 return RequiresDstTexture::kNo;
258 }
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 Salomon92be2f72018-06-19 14:33:47 -0400542 SkASSERT(sizeof(CircleVertex) == gp->debugOnly_vertexStride());
Jim Van Verthc5903412016-11-17 15:27:09 -0500543
544 const GrBuffer* vertexBuffer;
545 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
553 const GrBuffer* indexBuffer = nullptr;
554 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 Salomon05969092017-07-13 11:20:51 -0400590 static const uint32_t kPipelineFlags = 0;
Brian Salomon49348902018-06-26 09:12:38 -0400591 auto pipe = target->makePipeline(kPipelineFlags, GrProcessorSet::MakeEmptySet(),
592 target->detachAppliedClip());
Brian Salomon05969092017-07-13 11:20:51 -0400593
Brian Salomon7eae3e02018-08-07 14:02:38 +0000594 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
595 mesh->setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
596 GrPrimitiveRestart::kNo);
597 mesh->setVertexData(vertexBuffer, firstVertex);
598 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Jim Van Verthc5903412016-11-17 15:27:09 -0500599 }
600
Brian Salomon7eae3e02018-08-07 14:02:38 +0000601 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomonfc527d22016-12-14 21:07:01 -0500602 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
Jim Van Verthc5903412016-11-17 15:27:09 -0500603 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
604 this->joinBounds(*that);
605 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 Phillips7c525e62018-06-12 10:11:12 -0400622std::unique_ptr<GrDrawOp> Make(GrContext* context,
623 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 Phillipsc994a932018-06-19 13:09:54 -0400642 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
643
644 return pool->allocate<ShadowCircularRRectOp>(color, bounds,
645 scaledRadius,
646 rrect.isOval(),
647 blurWidth,
Jim Van Verthfb186392018-09-11 11:37:46 -0400648 scaledInsetWidth);
Jim Van Verthc5903412016-11-17 15:27:09 -0500649}
Brian Salomonfc527d22016-12-14 21:07:01 -0500650}
Jim Van Verth57061ee2017-04-28 17:30:30 -0400651
Jim Van Verthc5903412016-11-17 15:27:09 -0500652///////////////////////////////////////////////////////////////////////////////
653
Hal Canary6f6961e2017-01-31 13:50:44 -0500654#if GR_TEST_UTILS
Jim Van Verthc5903412016-11-17 15:27:09 -0500655
Brian Salomon05969092017-07-13 11:20:51 -0400656GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
Jim Van Verth57061ee2017-04-28 17:30:30 -0400657 // create a similarity matrix
658 SkScalar rotate = random->nextSScalar1() * 360.f;
659 SkScalar translateX = random->nextSScalar1() * 1000.f;
660 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -0400661 SkScalar scale;
662 do {
663 scale = random->nextSScalar1() * 100.f;
664 } while (scale == 0);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400665 SkMatrix viewMatrix;
666 viewMatrix.setRotate(rotate);
667 viewMatrix.postTranslate(translateX, translateY);
668 viewMatrix.postScale(scale, scale);
Jim Van Verth8d1e0ac2017-05-05 15:53:23 -0400669 SkScalar insetWidth = random->nextSScalar1() * 72.f;
670 SkScalar blurWidth = random->nextSScalar1() * 72.f;
Jim Van Verth57061ee2017-04-28 17:30:30 -0400671 bool isCircle = random->nextBool();
Brian Salomon05969092017-07-13 11:20:51 -0400672 // This op doesn't use a full GrPaint, just a color.
673 GrColor color = paint.getColor();
Jim Van Verth57061ee2017-04-28 17:30:30 -0400674 if (isCircle) {
675 SkRect circle = GrTest::TestSquare(random);
676 SkRRect rrect = SkRRect::MakeOval(circle);
Jim Van Verthfb186392018-09-11 11:37:46 -0400677 return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
Jim Van Verth57061ee2017-04-28 17:30:30 -0400678 } else {
Brian Salomon05969092017-07-13 11:20:51 -0400679 SkRRect rrect;
680 do {
681 // This may return a rrect with elliptical corners, which we don't support.
682 rrect = GrTest::TestRRectSimple(random);
Mike Reed242135a2018-02-22 13:41:39 -0500683 } while (!SkRRectPriv::IsSimpleCircular(rrect));
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 }
Jim Van Verthc5903412016-11-17 15:27:09 -0500686}
687
688#endif