1 | /* |
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 | |
8 | #include "src/gpu/ops/GrShadowRRectOp.h" |
9 | |
10 | #include "include/gpu/GrRecordingContext.h" |
11 | #include "src/core/SkRRectPriv.h" |
12 | #include "src/gpu/GrBitmapTextureMaker.h" |
13 | #include "src/gpu/GrDrawOpTest.h" |
14 | #include "src/gpu/GrMemoryPool.h" |
15 | #include "src/gpu/GrOpFlushState.h" |
16 | #include "src/gpu/GrProgramInfo.h" |
17 | #include "src/gpu/GrProxyProvider.h" |
18 | #include "src/gpu/GrRecordingContextPriv.h" |
19 | #include "src/gpu/effects/GrShadowGeoProc.h" |
20 | #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" |
21 | |
22 | /////////////////////////////////////////////////////////////////////////////// |
23 | // Circle Data |
24 | // |
25 | // We have two possible cases for geometry for a circle: |
26 | |
27 | // In the case of a normal fill, we draw geometry for the circle as an octagon. |
28 | static const uint16_t gFillCircleIndices[] = { |
29 | // enter the octagon |
30 | // clang-format off |
31 | 0, 1, 8, 1, 2, 8, |
32 | 2, 3, 8, 3, 4, 8, |
33 | 4, 5, 8, 5, 6, 8, |
34 | 6, 7, 8, 7, 0, 8, |
35 | // clang-format on |
36 | }; |
37 | |
38 | // For stroked circles, we use two nested octagons. |
39 | static const uint16_t gStrokeCircleIndices[] = { |
40 | // enter the octagon |
41 | // clang-format off |
42 | 0, 1, 9, 0, 9, 8, |
43 | 1, 2, 10, 1, 10, 9, |
44 | 2, 3, 11, 2, 11, 10, |
45 | 3, 4, 12, 3, 12, 11, |
46 | 4, 5, 13, 4, 13, 12, |
47 | 5, 6, 14, 5, 14, 13, |
48 | 6, 7, 15, 6, 15, 14, |
49 | 7, 0, 8, 7, 8, 15, |
50 | // clang-format on |
51 | }; |
52 | |
53 | static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); |
54 | static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); |
55 | static const int kVertsPerStrokeCircle = 16; |
56 | static const int kVertsPerFillCircle = 9; |
57 | |
58 | static int circle_type_to_vert_count(bool stroked) { |
59 | return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; |
60 | } |
61 | |
62 | static int circle_type_to_index_count(bool stroked) { |
63 | return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; |
64 | } |
65 | |
66 | static const uint16_t* circle_type_to_indices(bool stroked) { |
67 | return stroked ? gStrokeCircleIndices : gFillCircleIndices; |
68 | } |
69 | |
70 | /////////////////////////////////////////////////////////////////////////////// |
71 | // RoundRect Data |
72 | // |
73 | // The geometry for a shadow roundrect is similar to a 9-patch: |
74 | // ____________ |
75 | // |_|________|_| |
76 | // | | | | |
77 | // | | | | |
78 | // | | | | |
79 | // |_|________|_| |
80 | // |_|________|_| |
81 | // |
82 | // However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram |
83 | // shows the upper part of the upper left corner. The bottom triangle would similarly be split |
84 | // into two triangles.) |
85 | // ________ |
86 | // |\ \ | |
87 | // | \ \ | |
88 | // | \\ | |
89 | // | \| |
90 | // -------- |
91 | // |
92 | // The center of the fan handles the curve of the corner. For roundrects where the stroke width |
93 | // is greater than the corner radius, the outer triangles blend from the curve to the straight |
94 | // sides. Otherwise these triangles will be degenerate. |
95 | // |
96 | // In the case where the stroke width is greater than the corner radius and the |
97 | // blur radius (overstroke), we add additional geometry to mark out the rectangle in the center. |
98 | // This rectangle extends the coverage values of the center edges of the 9-patch. |
99 | // ____________ |
100 | // |_|________|_| |
101 | // | |\ ____ /| | |
102 | // | | | | | | |
103 | // | | |____| | | |
104 | // |_|/______\|_| |
105 | // |_|________|_| |
106 | // |
107 | // For filled rrects we reuse the stroke geometry but add an additional quad to the center. |
108 | |
109 | static const uint16_t gRRectIndices[] = { |
110 | // clang-format off |
111 | // overstroke quads |
112 | // we place this at the beginning so that we can skip these indices when rendering as filled |
113 | 0, 6, 25, 0, 25, 24, |
114 | 6, 18, 27, 6, 27, 25, |
115 | 18, 12, 26, 18, 26, 27, |
116 | 12, 0, 24, 12, 24, 26, |
117 | |
118 | // corners |
119 | 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, |
120 | 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7, |
121 | 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13, |
122 | 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23, |
123 | |
124 | // edges |
125 | 0, 5, 11, 0, 11, 6, |
126 | 6, 7, 19, 6, 19, 18, |
127 | 18, 23, 17, 18, 17, 12, |
128 | 12, 13, 1, 12, 1, 0, |
129 | |
130 | // fill quad |
131 | // we place this at the end so that we can skip these indices when rendering as stroked |
132 | 0, 6, 18, 0, 18, 12, |
133 | // clang-format on |
134 | }; |
135 | |
136 | // overstroke count |
137 | static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6; |
138 | // simple stroke count skips overstroke indices |
139 | static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4; |
140 | // fill count adds final quad to stroke count |
141 | static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6; |
142 | static const int kVertsPerStrokeRRect = 24; |
143 | static const int kVertsPerOverstrokeRRect = 28; |
144 | static const int kVertsPerFillRRect = 24; |
145 | |
146 | enum RRectType { |
147 | kFill_RRectType, |
148 | kStroke_RRectType, |
149 | kOverstroke_RRectType, |
150 | }; |
151 | |
152 | static int rrect_type_to_vert_count(RRectType type) { |
153 | switch (type) { |
154 | case kFill_RRectType: |
155 | return kVertsPerFillRRect; |
156 | case kStroke_RRectType: |
157 | return kVertsPerStrokeRRect; |
158 | case kOverstroke_RRectType: |
159 | return kVertsPerOverstrokeRRect; |
160 | } |
161 | SK_ABORT("Invalid type" ); |
162 | } |
163 | |
164 | static int rrect_type_to_index_count(RRectType type) { |
165 | switch (type) { |
166 | case kFill_RRectType: |
167 | return kIndicesPerFillRRect; |
168 | case kStroke_RRectType: |
169 | return kIndicesPerStrokeRRect; |
170 | case kOverstroke_RRectType: |
171 | return kIndicesPerOverstrokeRRect; |
172 | } |
173 | SK_ABORT("Invalid type" ); |
174 | } |
175 | |
176 | static const uint16_t* rrect_type_to_indices(RRectType type) { |
177 | switch (type) { |
178 | case kFill_RRectType: |
179 | case kStroke_RRectType: |
180 | return gRRectIndices + 6*4; |
181 | case kOverstroke_RRectType: |
182 | return gRRectIndices; |
183 | } |
184 | SK_ABORT("Invalid type" ); |
185 | } |
186 | |
187 | /////////////////////////////////////////////////////////////////////////////// |
188 | namespace { |
189 | |
190 | class ShadowCircularRRectOp final : public GrMeshDrawOp { |
191 | public: |
192 | DEFINE_OP_CLASS_ID |
193 | |
194 | // An insetWidth > 1/2 rect width or height indicates a simple fill. |
195 | ShadowCircularRRectOp(GrColor color, const SkRect& devRect, |
196 | float devRadius, bool isCircle, float blurRadius, float insetWidth, |
197 | GrSurfaceProxyView falloffView) |
198 | : INHERITED(ClassID()) |
199 | , fFalloffView(std::move(falloffView)) { |
200 | SkRect bounds = devRect; |
201 | SkASSERT(insetWidth > 0); |
202 | SkScalar innerRadius = 0.0f; |
203 | SkScalar outerRadius = devRadius; |
204 | SkScalar umbraInset; |
205 | |
206 | RRectType type = kFill_RRectType; |
207 | if (isCircle) { |
208 | umbraInset = 0; |
209 | } else { |
210 | umbraInset = std::max(outerRadius, blurRadius); |
211 | } |
212 | |
213 | // If stroke is greater than width or height, this is still a fill, |
214 | // otherwise we compute stroke params. |
215 | if (isCircle) { |
216 | innerRadius = devRadius - insetWidth; |
217 | type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType; |
218 | } else { |
219 | if (insetWidth <= 0.5f*std::min(devRect.width(), devRect.height())) { |
220 | // We don't worry about a real inner radius, we just need to know if we |
221 | // need to create overstroke vertices. |
222 | innerRadius = std::max(insetWidth - umbraInset, 0.0f); |
223 | type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType; |
224 | } |
225 | } |
226 | |
227 | this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo); |
228 | |
229 | fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius, |
230 | blurRadius, bounds, type, isCircle}); |
231 | if (isCircle) { |
232 | fVertCount = circle_type_to_vert_count(kStroke_RRectType == type); |
233 | fIndexCount = circle_type_to_index_count(kStroke_RRectType == type); |
234 | } else { |
235 | fVertCount = rrect_type_to_vert_count(type); |
236 | fIndexCount = rrect_type_to_index_count(type); |
237 | } |
238 | } |
239 | |
240 | const char* name() const override { return "ShadowCircularRRectOp" ; } |
241 | |
242 | FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } |
243 | |
244 | GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, |
245 | bool hasMixedSampledCoverage, GrClampType) override { |
246 | return GrProcessorSet::EmptySetAnalysis(); |
247 | } |
248 | |
249 | private: |
250 | struct Geometry { |
251 | GrColor fColor; |
252 | SkScalar fOuterRadius; |
253 | SkScalar fUmbraInset; |
254 | SkScalar fInnerRadius; |
255 | SkScalar fBlurRadius; |
256 | SkRect fDevBounds; |
257 | RRectType fType; |
258 | bool fIsCircle; |
259 | }; |
260 | |
261 | struct CircleVertex { |
262 | SkPoint fPos; |
263 | GrColor fColor; |
264 | SkPoint fOffset; |
265 | SkScalar fDistanceCorrection; |
266 | }; |
267 | |
268 | void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const { |
269 | |
270 | GrColor color = args.fColor; |
271 | SkScalar outerRadius = args.fOuterRadius; |
272 | SkScalar innerRadius = args.fInnerRadius; |
273 | SkScalar blurRadius = args.fBlurRadius; |
274 | SkScalar distanceCorrection = outerRadius / blurRadius; |
275 | |
276 | const SkRect& bounds = args.fDevBounds; |
277 | |
278 | // The inner radius in the vertex data must be specified in normalized space. |
279 | innerRadius = innerRadius / outerRadius; |
280 | |
281 | SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); |
282 | SkScalar halfWidth = 0.5f * bounds.width(); |
283 | SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 |
284 | |
285 | (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); |
286 | (*verts)->fColor = color; |
287 | (*verts)->fOffset = SkPoint::Make(-octOffset, -1); |
288 | (*verts)->fDistanceCorrection = distanceCorrection; |
289 | (*verts)++; |
290 | |
291 | (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); |
292 | (*verts)->fColor = color; |
293 | (*verts)->fOffset = SkPoint::Make(octOffset, -1); |
294 | (*verts)->fDistanceCorrection = distanceCorrection; |
295 | (*verts)++; |
296 | |
297 | (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); |
298 | (*verts)->fColor = color; |
299 | (*verts)->fOffset = SkPoint::Make(1, -octOffset); |
300 | (*verts)->fDistanceCorrection = distanceCorrection; |
301 | (*verts)++; |
302 | |
303 | (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); |
304 | (*verts)->fColor = color; |
305 | (*verts)->fOffset = SkPoint::Make(1, octOffset); |
306 | (*verts)->fDistanceCorrection = distanceCorrection; |
307 | (*verts)++; |
308 | |
309 | (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth); |
310 | (*verts)->fColor = color; |
311 | (*verts)->fOffset = SkPoint::Make(octOffset, 1); |
312 | (*verts)->fDistanceCorrection = distanceCorrection; |
313 | (*verts)++; |
314 | |
315 | (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth); |
316 | (*verts)->fColor = color; |
317 | (*verts)->fOffset = SkPoint::Make(-octOffset, 1); |
318 | (*verts)->fDistanceCorrection = distanceCorrection; |
319 | (*verts)++; |
320 | |
321 | (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth); |
322 | (*verts)->fColor = color; |
323 | (*verts)->fOffset = SkPoint::Make(-1, octOffset); |
324 | (*verts)->fDistanceCorrection = distanceCorrection; |
325 | (*verts)++; |
326 | |
327 | (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); |
328 | (*verts)->fColor = color; |
329 | (*verts)->fOffset = SkPoint::Make(-1, -octOffset); |
330 | (*verts)->fDistanceCorrection = distanceCorrection; |
331 | (*verts)++; |
332 | |
333 | if (isStroked) { |
334 | // compute the inner ring |
335 | |
336 | // cosine and sine of pi/8 |
337 | SkScalar c = 0.923579533f; |
338 | SkScalar s = 0.382683432f; |
339 | SkScalar r = args.fInnerRadius; |
340 | |
341 | (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r); |
342 | (*verts)->fColor = color; |
343 | (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius); |
344 | (*verts)->fDistanceCorrection = distanceCorrection; |
345 | (*verts)++; |
346 | |
347 | (*verts)->fPos = center + SkPoint::Make(s * r, -c * r); |
348 | (*verts)->fColor = color; |
349 | (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius); |
350 | (*verts)->fDistanceCorrection = distanceCorrection; |
351 | (*verts)++; |
352 | |
353 | (*verts)->fPos = center + SkPoint::Make(c * r, -s * r); |
354 | (*verts)->fColor = color; |
355 | (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius); |
356 | (*verts)->fDistanceCorrection = distanceCorrection; |
357 | (*verts)++; |
358 | |
359 | (*verts)->fPos = center + SkPoint::Make(c * r, s * r); |
360 | (*verts)->fColor = color; |
361 | (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius); |
362 | (*verts)->fDistanceCorrection = distanceCorrection; |
363 | (*verts)++; |
364 | |
365 | (*verts)->fPos = center + SkPoint::Make(s * r, c * r); |
366 | (*verts)->fColor = color; |
367 | (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius); |
368 | (*verts)->fDistanceCorrection = distanceCorrection; |
369 | (*verts)++; |
370 | |
371 | (*verts)->fPos = center + SkPoint::Make(-s * r, c * r); |
372 | (*verts)->fColor = color; |
373 | (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius); |
374 | (*verts)->fDistanceCorrection = distanceCorrection; |
375 | (*verts)++; |
376 | |
377 | (*verts)->fPos = center + SkPoint::Make(-c * r, s * r); |
378 | (*verts)->fColor = color; |
379 | (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius); |
380 | (*verts)->fDistanceCorrection = distanceCorrection; |
381 | (*verts)++; |
382 | |
383 | (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r); |
384 | (*verts)->fColor = color; |
385 | (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); |
386 | (*verts)->fDistanceCorrection = distanceCorrection; |
387 | (*verts)++; |
388 | } else { |
389 | // filled |
390 | (*verts)->fPos = center; |
391 | (*verts)->fColor = color; |
392 | (*verts)->fOffset = SkPoint::Make(0, 0); |
393 | (*verts)->fDistanceCorrection = distanceCorrection; |
394 | (*verts)++; |
395 | } |
396 | } |
397 | |
398 | void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const { |
399 | GrColor color = args.fColor; |
400 | SkScalar outerRadius = args.fOuterRadius; |
401 | |
402 | const SkRect& bounds = args.fDevBounds; |
403 | |
404 | SkScalar umbraInset = args.fUmbraInset; |
405 | SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height()); |
406 | if (umbraInset > minDim) { |
407 | umbraInset = minDim; |
408 | } |
409 | |
410 | SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset, |
411 | bounds.fLeft + umbraInset, bounds.fRight - umbraInset }; |
412 | SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius, |
413 | bounds.fLeft + outerRadius, bounds.fRight - outerRadius }; |
414 | SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight, |
415 | bounds.fLeft, bounds.fRight }; |
416 | SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset, |
417 | bounds.fBottom - umbraInset, bounds.fBottom - umbraInset }; |
418 | SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius, |
419 | bounds.fBottom - outerRadius, bounds.fBottom - outerRadius }; |
420 | SkScalar yOuter[4] = { bounds.fTop, bounds.fTop, |
421 | bounds.fBottom, bounds.fBottom }; |
422 | |
423 | SkScalar blurRadius = args.fBlurRadius; |
424 | |
425 | // In the case where we have to inset more for the umbra, our two triangles in the |
426 | // corner get skewed to a diamond rather than a square. To correct for that, |
427 | // we also skew the vectors we send to the shader that help define the circle. |
428 | // By doing so, we end up with a quarter circle in the corner rather than the |
429 | // elliptical curve. |
430 | |
431 | // This is a bit magical, but it gives us the correct results at extrema: |
432 | // a) umbraInset == outerRadius produces an orthogonal vector |
433 | // b) outerRadius == 0 produces a diagonal vector |
434 | // And visually the corner looks correct. |
435 | SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset); |
436 | outerVec.normalize(); |
437 | // We want the circle edge to fall fractionally along the diagonal at |
438 | // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset |
439 | // |
440 | // Setting the components of the diagonal offset to the following value will give us that. |
441 | SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius); |
442 | SkVector diagVec = SkVector::Make(diagVal, diagVal); |
443 | SkScalar distanceCorrection = umbraInset / blurRadius; |
444 | |
445 | // build corner by corner |
446 | for (int i = 0; i < 4; ++i) { |
447 | // inner point |
448 | (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]); |
449 | (*verts)->fColor = color; |
450 | (*verts)->fOffset = SkVector::Make(0, 0); |
451 | (*verts)->fDistanceCorrection = distanceCorrection; |
452 | (*verts)++; |
453 | |
454 | // outer points |
455 | (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]); |
456 | (*verts)->fColor = color; |
457 | (*verts)->fOffset = SkVector::Make(0, -1); |
458 | (*verts)->fDistanceCorrection = distanceCorrection; |
459 | (*verts)++; |
460 | |
461 | (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]); |
462 | (*verts)->fColor = color; |
463 | (*verts)->fOffset = outerVec; |
464 | (*verts)->fDistanceCorrection = distanceCorrection; |
465 | (*verts)++; |
466 | |
467 | (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]); |
468 | (*verts)->fColor = color; |
469 | (*verts)->fOffset = diagVec; |
470 | (*verts)->fDistanceCorrection = distanceCorrection; |
471 | (*verts)++; |
472 | |
473 | (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]); |
474 | (*verts)->fColor = color; |
475 | (*verts)->fOffset = outerVec; |
476 | (*verts)->fDistanceCorrection = distanceCorrection; |
477 | (*verts)++; |
478 | |
479 | (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]); |
480 | (*verts)->fColor = color; |
481 | (*verts)->fOffset = SkVector::Make(0, -1); |
482 | (*verts)->fDistanceCorrection = distanceCorrection; |
483 | (*verts)++; |
484 | } |
485 | |
486 | // Add the additional vertices for overstroked rrects. |
487 | // Effectively this is an additional stroked rrect, with its |
488 | // parameters equal to those in the center of the 9-patch. This will |
489 | // give constant values across this inner ring. |
490 | if (kOverstroke_RRectType == args.fType) { |
491 | SkASSERT(args.fInnerRadius > 0.0f); |
492 | |
493 | SkScalar inset = umbraInset + args.fInnerRadius; |
494 | |
495 | // TL |
496 | (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset); |
497 | (*verts)->fColor = color; |
498 | (*verts)->fOffset = SkPoint::Make(0, 0); |
499 | (*verts)->fDistanceCorrection = distanceCorrection; |
500 | (*verts)++; |
501 | |
502 | // TR |
503 | (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset); |
504 | (*verts)->fColor = color; |
505 | (*verts)->fOffset = SkPoint::Make(0, 0); |
506 | (*verts)->fDistanceCorrection = distanceCorrection; |
507 | (*verts)++; |
508 | |
509 | // BL |
510 | (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset); |
511 | (*verts)->fColor = color; |
512 | (*verts)->fOffset = SkPoint::Make(0, 0); |
513 | (*verts)->fDistanceCorrection = distanceCorrection; |
514 | (*verts)++; |
515 | |
516 | // BR |
517 | (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset); |
518 | (*verts)->fColor = color; |
519 | (*verts)->fOffset = SkPoint::Make(0, 0); |
520 | (*verts)->fDistanceCorrection = distanceCorrection; |
521 | (*verts)++; |
522 | } |
523 | |
524 | } |
525 | |
526 | GrProgramInfo* programInfo() override { return fProgramInfo; } |
527 | |
528 | void onCreateProgramInfo(const GrCaps* caps, |
529 | SkArenaAlloc* arena, |
530 | const GrSurfaceProxyView* writeView, |
531 | GrAppliedClip&& appliedClip, |
532 | const GrXferProcessor::DstProxyView& dstProxyView) override { |
533 | GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView); |
534 | SkASSERT(sizeof(CircleVertex) == gp->vertexStride()); |
535 | |
536 | fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView, |
537 | std::move(appliedClip), |
538 | dstProxyView, gp, |
539 | GrProcessorSet::MakeEmptySet(), |
540 | GrPrimitiveType::kTriangles, |
541 | GrPipeline::InputFlags::kNone, |
542 | &GrUserStencilSettings::kUnused); |
543 | } |
544 | |
545 | void onPrepareDraws(Target* target) override { |
546 | int instanceCount = fGeoData.count(); |
547 | |
548 | sk_sp<const GrBuffer> vertexBuffer; |
549 | int firstVertex; |
550 | CircleVertex* verts = (CircleVertex*)target->makeVertexSpace( |
551 | sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex); |
552 | if (!verts) { |
553 | SkDebugf("Could not allocate vertices\n" ); |
554 | return; |
555 | } |
556 | |
557 | sk_sp<const GrBuffer> indexBuffer; |
558 | int firstIndex = 0; |
559 | uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); |
560 | if (!indices) { |
561 | SkDebugf("Could not allocate indices\n" ); |
562 | return; |
563 | } |
564 | |
565 | int currStartVertex = 0; |
566 | for (int i = 0; i < instanceCount; i++) { |
567 | const Geometry& args = fGeoData[i]; |
568 | |
569 | if (args.fIsCircle) { |
570 | bool isStroked = SkToBool(kStroke_RRectType == args.fType); |
571 | this->fillInCircleVerts(args, isStroked, &verts); |
572 | |
573 | const uint16_t* primIndices = circle_type_to_indices(isStroked); |
574 | const int primIndexCount = circle_type_to_index_count(isStroked); |
575 | for (int i = 0; i < primIndexCount; ++i) { |
576 | *indices++ = primIndices[i] + currStartVertex; |
577 | } |
578 | |
579 | currStartVertex += circle_type_to_vert_count(isStroked); |
580 | |
581 | } else { |
582 | this->fillInRRectVerts(args, &verts); |
583 | |
584 | const uint16_t* primIndices = rrect_type_to_indices(args.fType); |
585 | const int primIndexCount = rrect_type_to_index_count(args.fType); |
586 | for (int i = 0; i < primIndexCount; ++i) { |
587 | *indices++ = primIndices[i] + currStartVertex; |
588 | } |
589 | |
590 | currStartVertex += rrect_type_to_vert_count(args.fType); |
591 | } |
592 | } |
593 | |
594 | fMesh = target->allocMesh(); |
595 | fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1, |
596 | GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex); |
597 | } |
598 | |
599 | void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { |
600 | if (!fProgramInfo) { |
601 | this->createProgramInfo(flushState); |
602 | } |
603 | |
604 | if (!fProgramInfo || !fMesh) { |
605 | return; |
606 | } |
607 | |
608 | flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); |
609 | flushState->bindTextures(fProgramInfo->primProc(), *fFalloffView.proxy(), |
610 | fProgramInfo->pipeline()); |
611 | flushState->drawMesh(*fMesh); |
612 | } |
613 | |
614 | CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*, |
615 | const GrCaps& caps) override { |
616 | ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>(); |
617 | fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
618 | fVertCount += that->fVertCount; |
619 | fIndexCount += that->fIndexCount; |
620 | return CombineResult::kMerged; |
621 | } |
622 | |
623 | #if GR_TEST_UTILS |
624 | SkString onDumpInfo() const override { |
625 | SkString string; |
626 | for (int i = 0; i < fGeoData.count(); ++i) { |
627 | string.appendf( |
628 | "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," |
629 | "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n" , |
630 | fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop, |
631 | fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom, |
632 | fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset, |
633 | fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius); |
634 | } |
635 | return string; |
636 | } |
637 | #endif |
638 | |
639 | void visitProxies(const VisitProxyFunc& func) const override { |
640 | func(fFalloffView.proxy(), GrMipmapped(false)); |
641 | if (fProgramInfo) { |
642 | fProgramInfo->visitFPProxies(func); |
643 | } |
644 | } |
645 | |
646 | SkSTArray<1, Geometry, true> fGeoData; |
647 | int fVertCount; |
648 | int fIndexCount; |
649 | GrSurfaceProxyView fFalloffView; |
650 | |
651 | GrSimpleMesh* fMesh = nullptr; |
652 | GrProgramInfo* fProgramInfo = nullptr; |
653 | |
654 | typedef GrMeshDrawOp INHERITED; |
655 | }; |
656 | |
657 | } // anonymous namespace |
658 | |
659 | /////////////////////////////////////////////////////////////////////////////// |
660 | |
661 | namespace GrShadowRRectOp { |
662 | |
663 | static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* context) { |
664 | static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
665 | GrUniqueKey key; |
666 | GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff" ); |
667 | builder.finish(); |
668 | |
669 | GrProxyProvider* proxyProvider = context->priv().proxyProvider(); |
670 | |
671 | if (sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(key)) { |
672 | GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(), |
673 | GrColorType::kAlpha_8); |
674 | return {std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle}; |
675 | } |
676 | |
677 | static const int kWidth = 128; |
678 | static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8); |
679 | SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1); |
680 | |
681 | SkBitmap bitmap; |
682 | bitmap.allocPixels(ii, kRowBytes); |
683 | |
684 | unsigned char* values = (unsigned char*)bitmap.getPixels(); |
685 | for (int i = 0; i < 128; ++i) { |
686 | SkScalar d = SK_Scalar1 - i / SkIntToScalar(127); |
687 | values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255); |
688 | } |
689 | bitmap.setImmutable(); |
690 | |
691 | GrBitmapTextureMaker maker(context, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted); |
692 | auto view = maker.view(GrMipmapped::kNo); |
693 | SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin); |
694 | |
695 | if (view) { |
696 | proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy()); |
697 | } |
698 | return view; |
699 | } |
700 | |
701 | |
702 | std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context, |
703 | GrColor color, |
704 | const SkMatrix& viewMatrix, |
705 | const SkRRect& rrect, |
706 | SkScalar blurWidth, |
707 | SkScalar insetWidth) { |
708 | // Shadow rrect ops only handle simple circular rrects. |
709 | SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect)); |
710 | |
711 | GrSurfaceProxyView falloffView = create_falloff_texture(context); |
712 | if (!falloffView) { |
713 | return nullptr; |
714 | } |
715 | |
716 | // Do any matrix crunching before we reset the draw state for device coords. |
717 | const SkRect& rrectBounds = rrect.getBounds(); |
718 | SkRect bounds; |
719 | viewMatrix.mapRect(&bounds, rrectBounds); |
720 | |
721 | // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic. |
722 | SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX; |
723 | SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX]; |
724 | SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor); |
725 | SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor); |
726 | |
727 | if (scaledInsetWidth <= 0) { |
728 | return nullptr; |
729 | } |
730 | |
731 | GrOpMemoryPool* pool = context->priv().opMemoryPool(); |
732 | |
733 | return pool->allocate<ShadowCircularRRectOp>(color, bounds, |
734 | scaledRadius, |
735 | rrect.isOval(), |
736 | blurWidth, |
737 | scaledInsetWidth, |
738 | std::move(falloffView)); |
739 | } |
740 | } // namespace GrShadowRRectOp |
741 | |
742 | /////////////////////////////////////////////////////////////////////////////// |
743 | |
744 | #if GR_TEST_UTILS |
745 | |
746 | GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) { |
747 | // We may choose matrix and inset values that cause the factory to fail. We loop until we find |
748 | // an acceptable combination. |
749 | do { |
750 | // create a similarity matrix |
751 | SkScalar rotate = random->nextSScalar1() * 360.f; |
752 | SkScalar translateX = random->nextSScalar1() * 1000.f; |
753 | SkScalar translateY = random->nextSScalar1() * 1000.f; |
754 | SkScalar scale = random->nextSScalar1() * 100.f; |
755 | SkMatrix viewMatrix; |
756 | viewMatrix.setRotate(rotate); |
757 | viewMatrix.postTranslate(translateX, translateY); |
758 | viewMatrix.postScale(scale, scale); |
759 | SkScalar insetWidth = random->nextSScalar1() * 72.f; |
760 | SkScalar blurWidth = random->nextSScalar1() * 72.f; |
761 | bool isCircle = random->nextBool(); |
762 | // This op doesn't use a full GrPaint, just a color. |
763 | GrColor color = paint.getColor4f().toBytes_RGBA(); |
764 | if (isCircle) { |
765 | SkRect circle = GrTest::TestSquare(random); |
766 | SkRRect rrect = SkRRect::MakeOval(circle); |
767 | if (auto op = GrShadowRRectOp::Make( |
768 | context, color, viewMatrix, rrect, blurWidth, insetWidth)) { |
769 | return op; |
770 | } |
771 | } else { |
772 | SkRRect rrect; |
773 | do { |
774 | // This may return a rrect with elliptical corners, which will cause an assert. |
775 | rrect = GrTest::TestRRectSimple(random); |
776 | } while (!SkRRectPriv::IsSimpleCircular(rrect)); |
777 | if (auto op = GrShadowRRectOp::Make( |
778 | context, color, viewMatrix, rrect, blurWidth, insetWidth)) { |
779 | return op; |
780 | } |
781 | } |
782 | } while (true); |
783 | } |
784 | |
785 | #endif |
786 | |