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/private/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 | #ifdef SK_DEBUG |
243 | SkString dumpInfo() const override { |
244 | SkString string; |
245 | for (int i = 0; i < fGeoData.count(); ++i) { |
246 | string.appendf( |
247 | "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," |
248 | "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n" , |
249 | fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop, |
250 | fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom, |
251 | fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset, |
252 | fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius); |
253 | } |
254 | string.append(INHERITED::dumpInfo()); |
255 | return string; |
256 | } |
257 | #endif |
258 | |
259 | FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } |
260 | |
261 | GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, |
262 | bool hasMixedSampledCoverage, GrClampType) override { |
263 | return GrProcessorSet::EmptySetAnalysis(); |
264 | } |
265 | |
266 | private: |
267 | struct Geometry { |
268 | GrColor fColor; |
269 | SkScalar fOuterRadius; |
270 | SkScalar fUmbraInset; |
271 | SkScalar fInnerRadius; |
272 | SkScalar fBlurRadius; |
273 | SkRect fDevBounds; |
274 | RRectType fType; |
275 | bool fIsCircle; |
276 | }; |
277 | |
278 | struct CircleVertex { |
279 | SkPoint fPos; |
280 | GrColor fColor; |
281 | SkPoint fOffset; |
282 | SkScalar fDistanceCorrection; |
283 | }; |
284 | |
285 | void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const { |
286 | |
287 | GrColor color = args.fColor; |
288 | SkScalar outerRadius = args.fOuterRadius; |
289 | SkScalar innerRadius = args.fInnerRadius; |
290 | SkScalar blurRadius = args.fBlurRadius; |
291 | SkScalar distanceCorrection = outerRadius / blurRadius; |
292 | |
293 | const SkRect& bounds = args.fDevBounds; |
294 | |
295 | // The inner radius in the vertex data must be specified in normalized space. |
296 | innerRadius = innerRadius / outerRadius; |
297 | |
298 | SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); |
299 | SkScalar halfWidth = 0.5f * bounds.width(); |
300 | SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 |
301 | |
302 | (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); |
303 | (*verts)->fColor = color; |
304 | (*verts)->fOffset = SkPoint::Make(-octOffset, -1); |
305 | (*verts)->fDistanceCorrection = distanceCorrection; |
306 | (*verts)++; |
307 | |
308 | (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); |
309 | (*verts)->fColor = color; |
310 | (*verts)->fOffset = SkPoint::Make(octOffset, -1); |
311 | (*verts)->fDistanceCorrection = distanceCorrection; |
312 | (*verts)++; |
313 | |
314 | (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); |
315 | (*verts)->fColor = color; |
316 | (*verts)->fOffset = SkPoint::Make(1, -octOffset); |
317 | (*verts)->fDistanceCorrection = distanceCorrection; |
318 | (*verts)++; |
319 | |
320 | (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); |
321 | (*verts)->fColor = color; |
322 | (*verts)->fOffset = SkPoint::Make(1, octOffset); |
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(-octOffset * halfWidth, halfWidth); |
333 | (*verts)->fColor = color; |
334 | (*verts)->fOffset = SkPoint::Make(-octOffset, 1); |
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 | (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); |
345 | (*verts)->fColor = color; |
346 | (*verts)->fOffset = SkPoint::Make(-1, -octOffset); |
347 | (*verts)->fDistanceCorrection = distanceCorrection; |
348 | (*verts)++; |
349 | |
350 | if (isStroked) { |
351 | // compute the inner ring |
352 | |
353 | // cosine and sine of pi/8 |
354 | SkScalar c = 0.923579533f; |
355 | SkScalar s = 0.382683432f; |
356 | SkScalar r = args.fInnerRadius; |
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(s * r, -c * r); |
365 | (*verts)->fColor = color; |
366 | (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * 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(c * r, s * r); |
377 | (*verts)->fColor = color; |
378 | (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * 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(-s * r, c * r); |
389 | (*verts)->fColor = color; |
390 | (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * 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 | |
400 | (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r); |
401 | (*verts)->fColor = color; |
402 | (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); |
403 | (*verts)->fDistanceCorrection = distanceCorrection; |
404 | (*verts)++; |
405 | } else { |
406 | // filled |
407 | (*verts)->fPos = center; |
408 | (*verts)->fColor = color; |
409 | (*verts)->fOffset = SkPoint::Make(0, 0); |
410 | (*verts)->fDistanceCorrection = distanceCorrection; |
411 | (*verts)++; |
412 | } |
413 | } |
414 | |
415 | void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const { |
416 | GrColor color = args.fColor; |
417 | SkScalar outerRadius = args.fOuterRadius; |
418 | |
419 | const SkRect& bounds = args.fDevBounds; |
420 | |
421 | SkScalar umbraInset = args.fUmbraInset; |
422 | SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height()); |
423 | if (umbraInset > minDim) { |
424 | umbraInset = minDim; |
425 | } |
426 | |
427 | SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset, |
428 | bounds.fLeft + umbraInset, bounds.fRight - umbraInset }; |
429 | SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius, |
430 | bounds.fLeft + outerRadius, bounds.fRight - outerRadius }; |
431 | SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight, |
432 | bounds.fLeft, bounds.fRight }; |
433 | SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset, |
434 | bounds.fBottom - umbraInset, bounds.fBottom - umbraInset }; |
435 | SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius, |
436 | bounds.fBottom - outerRadius, bounds.fBottom - outerRadius }; |
437 | SkScalar yOuter[4] = { bounds.fTop, bounds.fTop, |
438 | bounds.fBottom, bounds.fBottom }; |
439 | |
440 | SkScalar blurRadius = args.fBlurRadius; |
441 | |
442 | // In the case where we have to inset more for the umbra, our two triangles in the |
443 | // corner get skewed to a diamond rather than a square. To correct for that, |
444 | // we also skew the vectors we send to the shader that help define the circle. |
445 | // By doing so, we end up with a quarter circle in the corner rather than the |
446 | // elliptical curve. |
447 | |
448 | // This is a bit magical, but it gives us the correct results at extrema: |
449 | // a) umbraInset == outerRadius produces an orthogonal vector |
450 | // b) outerRadius == 0 produces a diagonal vector |
451 | // And visually the corner looks correct. |
452 | SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset); |
453 | outerVec.normalize(); |
454 | // We want the circle edge to fall fractionally along the diagonal at |
455 | // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset |
456 | // |
457 | // Setting the components of the diagonal offset to the following value will give us that. |
458 | SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius); |
459 | SkVector diagVec = SkVector::Make(diagVal, diagVal); |
460 | SkScalar distanceCorrection = umbraInset / blurRadius; |
461 | |
462 | // build corner by corner |
463 | for (int i = 0; i < 4; ++i) { |
464 | // inner point |
465 | (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]); |
466 | (*verts)->fColor = color; |
467 | (*verts)->fOffset = SkVector::Make(0, 0); |
468 | (*verts)->fDistanceCorrection = distanceCorrection; |
469 | (*verts)++; |
470 | |
471 | // outer points |
472 | (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]); |
473 | (*verts)->fColor = color; |
474 | (*verts)->fOffset = SkVector::Make(0, -1); |
475 | (*verts)->fDistanceCorrection = distanceCorrection; |
476 | (*verts)++; |
477 | |
478 | (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]); |
479 | (*verts)->fColor = color; |
480 | (*verts)->fOffset = outerVec; |
481 | (*verts)->fDistanceCorrection = distanceCorrection; |
482 | (*verts)++; |
483 | |
484 | (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]); |
485 | (*verts)->fColor = color; |
486 | (*verts)->fOffset = diagVec; |
487 | (*verts)->fDistanceCorrection = distanceCorrection; |
488 | (*verts)++; |
489 | |
490 | (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]); |
491 | (*verts)->fColor = color; |
492 | (*verts)->fOffset = outerVec; |
493 | (*verts)->fDistanceCorrection = distanceCorrection; |
494 | (*verts)++; |
495 | |
496 | (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]); |
497 | (*verts)->fColor = color; |
498 | (*verts)->fOffset = SkVector::Make(0, -1); |
499 | (*verts)->fDistanceCorrection = distanceCorrection; |
500 | (*verts)++; |
501 | } |
502 | |
503 | // Add the additional vertices for overstroked rrects. |
504 | // Effectively this is an additional stroked rrect, with its |
505 | // parameters equal to those in the center of the 9-patch. This will |
506 | // give constant values across this inner ring. |
507 | if (kOverstroke_RRectType == args.fType) { |
508 | SkASSERT(args.fInnerRadius > 0.0f); |
509 | |
510 | SkScalar inset = umbraInset + args.fInnerRadius; |
511 | |
512 | // TL |
513 | (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset); |
514 | (*verts)->fColor = color; |
515 | (*verts)->fOffset = SkPoint::Make(0, 0); |
516 | (*verts)->fDistanceCorrection = distanceCorrection; |
517 | (*verts)++; |
518 | |
519 | // TR |
520 | (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset); |
521 | (*verts)->fColor = color; |
522 | (*verts)->fOffset = SkPoint::Make(0, 0); |
523 | (*verts)->fDistanceCorrection = distanceCorrection; |
524 | (*verts)++; |
525 | |
526 | // BL |
527 | (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset); |
528 | (*verts)->fColor = color; |
529 | (*verts)->fOffset = SkPoint::Make(0, 0); |
530 | (*verts)->fDistanceCorrection = distanceCorrection; |
531 | (*verts)++; |
532 | |
533 | // BR |
534 | (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset); |
535 | (*verts)->fColor = color; |
536 | (*verts)->fOffset = SkPoint::Make(0, 0); |
537 | (*verts)->fDistanceCorrection = distanceCorrection; |
538 | (*verts)++; |
539 | } |
540 | |
541 | } |
542 | |
543 | GrProgramInfo* programInfo() override { return fProgramInfo; } |
544 | |
545 | void onCreateProgramInfo(const GrCaps* caps, |
546 | SkArenaAlloc* arena, |
547 | const GrSurfaceProxyView* writeView, |
548 | GrAppliedClip&& appliedClip, |
549 | const GrXferProcessor::DstProxyView& dstProxyView) override { |
550 | GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView); |
551 | SkASSERT(sizeof(CircleVertex) == gp->vertexStride()); |
552 | |
553 | fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView, |
554 | std::move(appliedClip), |
555 | dstProxyView, gp, |
556 | GrProcessorSet::MakeEmptySet(), |
557 | GrPrimitiveType::kTriangles, |
558 | GrPipeline::InputFlags::kNone, |
559 | &GrUserStencilSettings::kUnused); |
560 | } |
561 | |
562 | void onPrepareDraws(Target* target) override { |
563 | int instanceCount = fGeoData.count(); |
564 | |
565 | sk_sp<const GrBuffer> vertexBuffer; |
566 | int firstVertex; |
567 | CircleVertex* verts = (CircleVertex*)target->makeVertexSpace( |
568 | sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex); |
569 | if (!verts) { |
570 | SkDebugf("Could not allocate vertices\n" ); |
571 | return; |
572 | } |
573 | |
574 | sk_sp<const GrBuffer> indexBuffer; |
575 | int firstIndex = 0; |
576 | uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); |
577 | if (!indices) { |
578 | SkDebugf("Could not allocate indices\n" ); |
579 | return; |
580 | } |
581 | |
582 | int currStartVertex = 0; |
583 | for (int i = 0; i < instanceCount; i++) { |
584 | const Geometry& args = fGeoData[i]; |
585 | |
586 | if (args.fIsCircle) { |
587 | bool isStroked = SkToBool(kStroke_RRectType == args.fType); |
588 | this->fillInCircleVerts(args, isStroked, &verts); |
589 | |
590 | const uint16_t* primIndices = circle_type_to_indices(isStroked); |
591 | const int primIndexCount = circle_type_to_index_count(isStroked); |
592 | for (int i = 0; i < primIndexCount; ++i) { |
593 | *indices++ = primIndices[i] + currStartVertex; |
594 | } |
595 | |
596 | currStartVertex += circle_type_to_vert_count(isStroked); |
597 | |
598 | } else { |
599 | this->fillInRRectVerts(args, &verts); |
600 | |
601 | const uint16_t* primIndices = rrect_type_to_indices(args.fType); |
602 | const int primIndexCount = rrect_type_to_index_count(args.fType); |
603 | for (int i = 0; i < primIndexCount; ++i) { |
604 | *indices++ = primIndices[i] + currStartVertex; |
605 | } |
606 | |
607 | currStartVertex += rrect_type_to_vert_count(args.fType); |
608 | } |
609 | } |
610 | |
611 | fMesh = target->allocMesh(); |
612 | fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1, |
613 | GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex); |
614 | } |
615 | |
616 | void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { |
617 | if (!fProgramInfo) { |
618 | this->createProgramInfo(flushState); |
619 | } |
620 | |
621 | if (!fProgramInfo || !fMesh) { |
622 | return; |
623 | } |
624 | |
625 | flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); |
626 | flushState->bindTextures(fProgramInfo->primProc(), *fFalloffView.proxy(), |
627 | fProgramInfo->pipeline()); |
628 | flushState->drawMesh(*fMesh); |
629 | } |
630 | |
631 | CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*, |
632 | const GrCaps& caps) override { |
633 | ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>(); |
634 | fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
635 | fVertCount += that->fVertCount; |
636 | fIndexCount += that->fIndexCount; |
637 | return CombineResult::kMerged; |
638 | } |
639 | |
640 | void visitProxies(const VisitProxyFunc& func) const override { |
641 | func(fFalloffView.proxy(), GrMipMapped(false)); |
642 | if (fProgramInfo) { |
643 | fProgramInfo->visitFPProxies(func); |
644 | } |
645 | } |
646 | |
647 | SkSTArray<1, Geometry, true> fGeoData; |
648 | int fVertCount; |
649 | int fIndexCount; |
650 | GrSurfaceProxyView fFalloffView; |
651 | |
652 | GrSimpleMesh* fMesh = nullptr; |
653 | GrProgramInfo* fProgramInfo = nullptr; |
654 | |
655 | typedef GrMeshDrawOp INHERITED; |
656 | }; |
657 | |
658 | } // anonymous namespace |
659 | |
660 | /////////////////////////////////////////////////////////////////////////////// |
661 | |
662 | namespace GrShadowRRectOp { |
663 | |
664 | static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* context) { |
665 | static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
666 | GrUniqueKey key; |
667 | GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff" ); |
668 | builder.finish(); |
669 | |
670 | GrProxyProvider* proxyProvider = context->priv().proxyProvider(); |
671 | |
672 | if (sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(key)) { |
673 | GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(falloffTexture->backendFormat(), |
674 | GrColorType::kAlpha_8); |
675 | return {std::move(falloffTexture), kTopLeft_GrSurfaceOrigin, swizzle}; |
676 | } |
677 | |
678 | static const int kWidth = 128; |
679 | static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8); |
680 | SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1); |
681 | |
682 | SkBitmap bitmap; |
683 | bitmap.allocPixels(ii, kRowBytes); |
684 | |
685 | unsigned char* values = (unsigned char*)bitmap.getPixels(); |
686 | for (int i = 0; i < 128; ++i) { |
687 | SkScalar d = SK_Scalar1 - i / SkIntToScalar(127); |
688 | values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255); |
689 | } |
690 | bitmap.setImmutable(); |
691 | |
692 | GrBitmapTextureMaker maker(context, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted); |
693 | auto view = maker.view(GrMipMapped::kNo); |
694 | SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin); |
695 | |
696 | if (view) { |
697 | proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy()); |
698 | } |
699 | return view; |
700 | } |
701 | |
702 | |
703 | std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context, |
704 | GrColor color, |
705 | const SkMatrix& viewMatrix, |
706 | const SkRRect& rrect, |
707 | SkScalar blurWidth, |
708 | SkScalar insetWidth) { |
709 | // Shadow rrect ops only handle simple circular rrects. |
710 | SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect)); |
711 | |
712 | GrSurfaceProxyView falloffView = create_falloff_texture(context); |
713 | if (!falloffView) { |
714 | return nullptr; |
715 | } |
716 | |
717 | // Do any matrix crunching before we reset the draw state for device coords. |
718 | const SkRect& rrectBounds = rrect.getBounds(); |
719 | SkRect bounds; |
720 | viewMatrix.mapRect(&bounds, rrectBounds); |
721 | |
722 | // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic. |
723 | SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX; |
724 | SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX]; |
725 | SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor); |
726 | SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor); |
727 | |
728 | if (scaledInsetWidth <= 0) { |
729 | return nullptr; |
730 | } |
731 | |
732 | GrOpMemoryPool* pool = context->priv().opMemoryPool(); |
733 | |
734 | return pool->allocate<ShadowCircularRRectOp>(color, bounds, |
735 | scaledRadius, |
736 | rrect.isOval(), |
737 | blurWidth, |
738 | scaledInsetWidth, |
739 | std::move(falloffView)); |
740 | } |
741 | } |
742 | |
743 | /////////////////////////////////////////////////////////////////////////////// |
744 | |
745 | #if GR_TEST_UTILS |
746 | |
747 | GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) { |
748 | // We may choose matrix and inset values that cause the factory to fail. We loop until we find |
749 | // an acceptable combination. |
750 | do { |
751 | // create a similarity matrix |
752 | SkScalar rotate = random->nextSScalar1() * 360.f; |
753 | SkScalar translateX = random->nextSScalar1() * 1000.f; |
754 | SkScalar translateY = random->nextSScalar1() * 1000.f; |
755 | SkScalar scale = random->nextSScalar1() * 100.f; |
756 | SkMatrix viewMatrix; |
757 | viewMatrix.setRotate(rotate); |
758 | viewMatrix.postTranslate(translateX, translateY); |
759 | viewMatrix.postScale(scale, scale); |
760 | SkScalar insetWidth = random->nextSScalar1() * 72.f; |
761 | SkScalar blurWidth = random->nextSScalar1() * 72.f; |
762 | bool isCircle = random->nextBool(); |
763 | // This op doesn't use a full GrPaint, just a color. |
764 | GrColor color = paint.getColor4f().toBytes_RGBA(); |
765 | if (isCircle) { |
766 | SkRect circle = GrTest::TestSquare(random); |
767 | SkRRect rrect = SkRRect::MakeOval(circle); |
768 | if (auto op = GrShadowRRectOp::Make( |
769 | context, color, viewMatrix, rrect, blurWidth, insetWidth)) { |
770 | return op; |
771 | } |
772 | } else { |
773 | SkRRect rrect; |
774 | do { |
775 | // This may return a rrect with elliptical corners, which will cause an assert. |
776 | rrect = GrTest::TestRRectSimple(random); |
777 | } while (!SkRRectPriv::IsSimpleCircular(rrect)); |
778 | if (auto op = GrShadowRRectOp::Make( |
779 | context, color, viewMatrix, rrect, blurWidth, insetWidth)) { |
780 | return op; |
781 | } |
782 | } |
783 | } while (true); |
784 | } |
785 | |
786 | #endif |
787 | |