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 "gm.h"
9#include "sk_tool_utils.h"
10#include "SkBlurMaskFilter.h"
11#include "SkRRect.h"
12
13static SkRect offset_center_to(const SkIRect& src, SkScalar x, SkScalar y) {
14    SkScalar halfW = 0.5f * src.width();
15    SkScalar halfH = 0.5f * src.height();
16
17    return SkRect::MakeLTRB(x - halfW, y - halfH, x + halfW, y + halfH);
18}
19
20static void draw_rrect(SkCanvas* canvas, const SkRRect& rr, const SkRRect& occRR) {
21    const SkScalar kBlurSigma = 5.0f;
22
23    SkRect occRect;
24    SkColor strokeColor;
25
26    {
27        SkRect occRect1 = sk_tool_utils::compute_central_occluder(occRR);
28        SkRect occRect2 = sk_tool_utils::compute_widest_occluder(occRR);
29        SkRect occRect3 = sk_tool_utils::compute_tallest_occluder(occRR);
30
31        SkScalar area1 = occRect1.width() * occRect1.height();
32        SkScalar area2 = occRect2.width() * occRect2.height();
33        SkScalar area3 = occRect3.width() * occRect3.height();
34
35        if (area1 >= area2 && area1 >= area3) {
36            strokeColor = SK_ColorRED;
37            occRect = occRect1;
38        } else if (area2 > area3) {
39            strokeColor = SK_ColorYELLOW;
40            occRect = occRect2;
41        } else {
42            strokeColor = SK_ColorCYAN;
43            occRect = occRect3;
44        }
45    }
46
47    // draw the blur
48    SkPaint paint;
49    paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, kBlurSigma, occRect));
50    canvas->drawRRect(rr, paint);
51
52    // draw the stroked geometry of the full occluder
53    SkPaint stroke;
54    stroke.setStyle(SkPaint::kStroke_Style);
55    stroke.setColor(SK_ColorBLUE);
56    canvas->drawRRect(occRR, stroke);
57
58    // draw the geometry of the occluding rect
59    stroke.setColor(strokeColor);
60    canvas->drawRect(occRect, stroke);
61}
62
63static void draw_45(SkCanvas* canvas, SkRRect::Corner corner,
64                    SkScalar dist, const SkPoint& center) {
65    SkRRect::Corner left = SkRRect::kUpperLeft_Corner, right = SkRRect::kUpperLeft_Corner;
66    SkVector dir = { 0, 0 };
67
68    constexpr SkScalar kSize = 64.0f / SK_ScalarSqrt2;
69
70    switch (corner) {
71    case SkRRect::kUpperLeft_Corner:
72        left = SkRRect::kUpperRight_Corner;
73        right = SkRRect::kLowerLeft_Corner;
74
75        dir.set(-SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2);
76        break;
77    case SkRRect::kUpperRight_Corner:
78        left = SkRRect::kUpperLeft_Corner;
79        right = SkRRect::kLowerRight_Corner;
80        dir.set(SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2);
81        break;
82    case SkRRect::kLowerRight_Corner:
83        left = SkRRect::kLowerLeft_Corner;
84        right = SkRRect::kUpperRight_Corner;
85        dir.set(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
86        break;
87    case SkRRect::kLowerLeft_Corner:
88        left = SkRRect::kLowerRight_Corner;
89        right = SkRRect::kUpperLeft_Corner;
90        dir.set(-SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
91        break;
92    default:
93        SkFAIL("Invalid shape.");
94    }
95
96    SkRect r = SkRect::MakeWH(kSize, kSize);
97    // UL, UR, LR, LL
98    SkVector radii[4] = { { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f } };
99    radii[left] = SkVector::Make(kSize, kSize);
100    radii[right] = SkVector::Make(kSize, kSize);
101    SkRRect rr;
102    rr.setRectRadii(
103            offset_center_to(r.roundOut(), center.fX + dist*dir.fX, center.fY + dist*dir.fY),
104            radii);
105
106    SkRRect occRR;
107    dist -= 10.0f;
108    occRR.setRectRadii(
109            offset_center_to(r.roundOut(), center.fX + dist*dir.fX, center.fY + dist*dir.fY),
110            radii);
111
112    draw_rrect(canvas, rr, occRR);
113}
114
115static void draw_45_simple(SkCanvas* canvas, const SkVector& v,
116                           SkScalar dist, const SkPoint& center) {
117    SkIRect r = SkIRect::MakeWH(64, 64);
118    SkRRect rr = SkRRect::MakeRectXY(
119                            offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY),
120                            8, 8);
121
122    dist -= 10.0f;
123    SkRRect occRR = SkRRect::MakeRectXY(
124                            offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY),
125                            8, 8);
126
127    draw_rrect(canvas, rr, occRR);
128}
129
130static void draw_90(SkCanvas* canvas, const SkVector& v, SkScalar dist, const SkPoint& center) {
131    constexpr int kWidth = 25;
132
133    SkIRect r;
134    if (fabs(v.fX) < fabs(v.fY)) {
135        r = SkIRect::MakeWH(kWidth, 64);
136    } else {
137        r = SkIRect::MakeWH(64, kWidth);
138    }
139    SkRRect rr = SkRRect::MakeOval(
140                            offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY));
141
142    dist -= 10.0f;
143    SkRRect occRR = SkRRect::MakeOval(
144                            offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY));
145
146    draw_rrect(canvas, rr, occRR);
147}
148
149static void draw_90_simple(SkCanvas* canvas, const SkVector& v,
150                           SkScalar dist, const SkPoint& center) {
151    constexpr int kLength = 128;
152    // The width needs to be larger than 2*3*blurRadii+2*cornerRadius for the analytic
153    // RRect blur to kick in
154    constexpr int kWidth = 47;
155
156    SkIRect r;
157    if (fabs(v.fX) < fabs(v.fY)) {
158        r = SkIRect::MakeWH(kLength, kWidth);
159    } else {
160        r = SkIRect::MakeWH(kWidth, kLength);
161    }
162    SkRRect rr = SkRRect::MakeRectXY(
163                            offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY),
164                            8, 8);
165
166    dist -= 10.0f;
167    SkRRect occRR = SkRRect::MakeRectXY(
168                            offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY),
169                            8, 8);
170
171    draw_rrect(canvas, rr, occRR);
172}
173
174static void draw_30_60(SkCanvas* canvas, SkRRect::Corner corner, const SkVector& v,
175                       SkScalar dist, const SkPoint& center) {
176    SkRRect::Corner left = SkRRect::kUpperLeft_Corner, right = SkRRect::kUpperLeft_Corner;
177
178    constexpr int kLength = 64;
179    constexpr int kWidth = 30;
180
181    switch (corner) {
182    case SkRRect::kUpperLeft_Corner:
183        left = SkRRect::kUpperRight_Corner;
184        right = SkRRect::kLowerLeft_Corner;
185        break;
186    case SkRRect::kUpperRight_Corner:
187        left = SkRRect::kUpperLeft_Corner;
188        right = SkRRect::kLowerRight_Corner;
189        break;
190    case SkRRect::kLowerRight_Corner:
191        left = SkRRect::kLowerLeft_Corner;
192        right = SkRRect::kUpperRight_Corner;
193        break;
194    case SkRRect::kLowerLeft_Corner:
195        left = SkRRect::kLowerRight_Corner;
196        right = SkRRect::kUpperLeft_Corner;
197        break;
198    default:
199        SkFAIL("Invalid shape.");
200    }
201
202    SkIRect r;
203    if (fabs(v.fX) < fabs(v.fY)) {
204        r = SkIRect::MakeWH(kLength, kWidth);
205    } else {
206        r = SkIRect::MakeWH(kWidth, kLength);
207    }
208    // UL, UR, LR, LL
209    SkVector radii[4] = { { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f } };
210    radii[left] = SkVector::Make(SkIntToScalar(kWidth), SkIntToScalar(kWidth));
211    radii[right] = SkVector::Make(SkIntToScalar(kWidth), SkIntToScalar(kWidth));
212    SkRRect rr;
213    rr.setRectRadii(offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), radii);
214
215    dist -= 10.0f;
216    SkRRect occRR;
217    occRR.setRectRadii(offset_center_to(r, center.fX + dist*v.fX, center.fY + dist*v.fY), radii);
218    draw_rrect(canvas, rr, occRR);
219}
220
221namespace skiagm {
222
223class OccludedRRectBlurGM : public GM {
224public:
225    OccludedRRectBlurGM() {
226        this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
227    }
228
229protected:
230
231    SkString onShortName() override {
232        return SkString("occludedrrectblur");
233    }
234
235    SkISize onISize() override {
236        return SkISize::Make(kWidth, kHeight);
237    }
238
239    void onDraw(SkCanvas* canvas) override {
240        const SkPoint center = SkPoint::Make(kWidth/2, kHeight/2);
241
242        // outer-most big RR
243        {
244            SkIRect r = SkIRect::MakeWH(420, 420);
245            SkRRect rr = SkRRect::MakeRectXY(offset_center_to(r, center.fX, center.fY), 64, 64);
246            draw_rrect(canvas, rr, rr);
247
248#if 1
249            // TODO: remove this. Until we actually start skipping the middle draw we need this
250            // to provide contrast
251            SkPaint temp;
252            temp.setColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
253            r.inset(32, 32);
254            canvas->drawRect(offset_center_to(r, center.fX, center.fY), temp);
255#endif
256        }
257
258        // center circle
259        {
260            SkIRect r = SkIRect::MakeWH(32, 32);
261            SkRRect rr = SkRRect::MakeOval(offset_center_to(r, center.fX, center.fY));
262            draw_rrect(canvas, rr, rr);
263        }
264
265        draw_45(canvas, SkRRect::kUpperLeft_Corner, 64, center);
266        draw_45(canvas, SkRRect::kUpperRight_Corner, 64, center);
267        draw_45(canvas, SkRRect::kLowerRight_Corner, 64, center);
268        draw_45(canvas, SkRRect::kLowerLeft_Corner, 64, center);
269
270        draw_90(canvas, SkVector::Make(-1.0f, 0.0f), 64, center);
271        draw_90(canvas, SkVector::Make(0.0f, -1.0f), 64, center);
272        draw_90(canvas, SkVector::Make(1.0f, 0.0f), 64, center);
273        draw_90(canvas, SkVector::Make(0.0f, 1.0f), 64, center);
274
275        constexpr SkScalar kRoot3Over2 = 0.8660254037844386f;
276
277        draw_30_60(canvas, SkRRect::kLowerLeft_Corner,
278                   SkVector::Make(0.5f, kRoot3Over2), 120, center);
279        draw_30_60(canvas, SkRRect::kUpperRight_Corner,
280                   SkVector::Make(kRoot3Over2, 0.5f), 120, center);
281
282        draw_30_60(canvas, SkRRect::kUpperLeft_Corner,
283                   SkVector::Make(-0.5f, kRoot3Over2), 120, center);
284        draw_30_60(canvas, SkRRect::kLowerRight_Corner,
285                   SkVector::Make(-kRoot3Over2, 0.5f), 120, center);
286
287        draw_30_60(canvas, SkRRect::kLowerLeft_Corner,
288                   SkVector::Make(-0.5f, -kRoot3Over2), 120, center);
289        draw_30_60(canvas, SkRRect::kUpperRight_Corner,
290                   SkVector::Make(-kRoot3Over2, -0.5f), 120, center);
291
292        draw_30_60(canvas, SkRRect::kUpperLeft_Corner,
293                   SkVector::Make(0.5f, -kRoot3Over2), 120, center);
294        draw_30_60(canvas, SkRRect::kLowerRight_Corner,
295                   SkVector::Make(kRoot3Over2, -0.5f), 120, center);
296
297        draw_45_simple(canvas, SkVector::Make(-SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2),
298                       210, center);
299        draw_45_simple(canvas, SkVector::Make(SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2),
300                       210, center);
301        draw_45_simple(canvas, SkVector::Make(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2),
302                       210, center);
303        draw_45_simple(canvas, SkVector::Make(-SK_ScalarRoot2Over2, SK_ScalarRoot2Over2),
304                       210, center);
305
306        draw_90_simple(canvas, SkVector::Make(-1.0f, 0.0f), 160, center);
307        draw_90_simple(canvas, SkVector::Make(0.0f, -1.0f), 160, center);
308        draw_90_simple(canvas, SkVector::Make(1.0f, 0.0f), 160, center);
309        draw_90_simple(canvas, SkVector::Make(0.0f, 1.0f), 160, center);
310    }
311
312private:
313    static constexpr int kWidth = 440;
314    static constexpr int kHeight = 440;
315
316    typedef GM INHERITED;
317};
318
319//////////////////////////////////////////////////////////////////////////////
320
321DEF_GM(return new OccludedRRectBlurGM;)
322}
323