1/*
2 * Copyright 2015 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 "SampleCode.h"
9#include "SkCanvas.h"
10#include "SkInterpolator.h"
11#include "SkPath.h"
12#include "SkRRect.h"
13#include "SkTime.h"
14
15// This slide tests out the match up between BW clipping and rendering. It can
16// draw a large rect through some clip geometry and draw the same geometry
17// normally. Which one is drawn first can be toggled. The pair of objects is translated
18// fractionally (via an animator) to expose snapping bugs. The key bindings are:
19//      1-9: the different geometries
20//      t:   toggle which is drawn first the clip or the normal geometry
21//      f:   flip-flops which corner the bottom AA clip rect occupies in the complex clip cases
22
23// The possible geometric combinations to test
24enum Geometry {
25    kRect_Geometry,
26    kRRect_Geometry,
27    kCircle_Geometry,
28    kConvexPath_Geometry,
29    kConcavePath_Geometry,
30    kRectAndRect_Geometry,
31    kRectAndRRect_Geometry,
32    kRectAndConvex_Geometry,
33    kRectAndConcave_Geometry
34};
35
36// The basic rect used is [kMin,kMin]..[kMax,kMax]
37static const float kMin = 100.5f;
38static const float kMid = 200.0f;
39static const float kMax = 299.5f;
40
41// The translation applied to the base AA rect in the combination cases
42// (i.e., kRectAndRect through kRectAndConcave)
43static const float kXlate = 100.0f;
44
45SkRect create_rect(const SkPoint& offset) {
46    SkRect r = SkRect::MakeLTRB(kMin, kMin, kMax, kMax);
47    r.offset(offset);
48    return r;
49}
50
51SkRRect create_rrect(const SkPoint& offset) {
52    SkRRect rrect;
53    rrect.setRectXY(create_rect(offset), 10, 10);
54    return rrect;
55}
56
57SkRRect create_circle(const SkPoint& offset) {
58    SkRRect circle;
59    circle.setOval(create_rect(offset));
60    return circle;
61}
62
63SkPath create_convex_path(const SkPoint& offset) {
64    SkPath convexPath;
65    convexPath.moveTo(kMin, kMin);
66    convexPath.lineTo(kMax, kMax);
67    convexPath.lineTo(kMin, kMax);
68    convexPath.close();
69    convexPath.offset(offset.fX, offset.fY);
70    return convexPath;
71}
72
73SkPath create_concave_path(const SkPoint& offset) {
74    SkPath concavePath;
75    concavePath.moveTo(kMin, kMin);
76    concavePath.lineTo(kMid, 105.0f);
77    concavePath.lineTo(kMax, kMin);
78    concavePath.lineTo(295.0f, kMid);
79    concavePath.lineTo(kMax, kMax);
80    concavePath.lineTo(kMid, 295.0f);
81    concavePath.lineTo(kMin, kMax);
82    concavePath.lineTo(105.0f, kMid);
83    concavePath.close();
84
85    concavePath.offset(offset.fX, offset.fY);
86    return concavePath;
87}
88
89static void draw_normal_geom(SkCanvas* canvas, const SkPoint& offset, int geom, bool useAA) {
90    SkPaint p;
91    p.setAntiAlias(useAA);
92    p.setColor(SK_ColorBLACK);
93
94    switch (geom) {
95    case kRect_Geometry:                // fall thru
96    case kRectAndRect_Geometry:
97        canvas->drawRect(create_rect(offset), p);
98        break;
99    case kRRect_Geometry:               // fall thru
100    case kRectAndRRect_Geometry:
101        canvas->drawRRect(create_rrect(offset), p);
102        break;
103    case kCircle_Geometry:
104        canvas->drawRRect(create_circle(offset), p);
105        break;
106    case kConvexPath_Geometry:          // fall thru
107    case kRectAndConvex_Geometry:
108        canvas->drawPath(create_convex_path(offset), p);
109        break;
110    case kConcavePath_Geometry:         // fall thru
111    case kRectAndConcave_Geometry:
112        canvas->drawPath(create_concave_path(offset), p);
113        break;
114    }
115}
116
117class ClipDrawMatchView : public SampleView {
118public:
119    ClipDrawMatchView() : fTrans(2, 5), fGeom(kRect_Geometry), fClipFirst(true), fSign(1) {
120        SkScalar values[2];
121
122        fTrans.setRepeatCount(999);
123        values[0] = values[1] = 0;
124        fTrans.setKeyFrame(0, SkTime::GetMSecs() + 1000, values);
125        values[1] = 1;
126        fTrans.setKeyFrame(1, SkTime::GetMSecs() + 2000, values);
127        values[0] = values[1] = 1;
128        fTrans.setKeyFrame(2, SkTime::GetMSecs() + 3000, values);
129        values[1] = 0;
130        fTrans.setKeyFrame(3, SkTime::GetMSecs() + 4000, values);
131        values[0] = 0;
132        fTrans.setKeyFrame(4, SkTime::GetMSecs() + 5000, values);
133    }
134
135protected:
136    bool onQuery(SkEvent* evt) override {
137        if (SampleCode::TitleQ(*evt)) {
138            SampleCode::TitleR(evt, "ClipDrawMatch");
139            return true;
140        }
141        SkUnichar uni;
142        if (SampleCode::CharQ(*evt, &uni)) {
143            switch (uni) {
144                case '1': fGeom = kRect_Geometry; this->inval(nullptr); return true;
145                case '2': fGeom = kRRect_Geometry; this->inval(nullptr); return true;
146                case '3': fGeom = kCircle_Geometry; this->inval(nullptr); return true;
147                case '4': fGeom = kConvexPath_Geometry; this->inval(nullptr); return true;
148                case '5': fGeom = kConcavePath_Geometry; this->inval(nullptr); return true;
149                case '6': fGeom = kRectAndRect_Geometry; this->inval(nullptr); return true;
150                case '7': fGeom = kRectAndRRect_Geometry; this->inval(nullptr); return true;
151                case '8': fGeom = kRectAndConvex_Geometry; this->inval(nullptr); return true;
152                case '9': fGeom = kRectAndConcave_Geometry; this->inval(nullptr); return true;
153                case 'f': fSign = -fSign; this->inval(nullptr); return true;
154                case 't': fClipFirst = !fClipFirst; this->inval(nullptr); return true;
155                default: break;
156            }
157        }
158        return this->INHERITED::onQuery(evt);
159    }
160
161    void drawClippedGeom(SkCanvas* canvas, const SkPoint& offset, bool useAA) {
162
163        int count = canvas->save();
164
165        switch (fGeom) {
166        case kRect_Geometry:
167            canvas->clipRect(create_rect(offset), SkRegion::kReplace_Op, useAA);
168            break;
169        case kRRect_Geometry:
170            canvas->clipRRect(create_rrect(offset), SkRegion::kReplace_Op, useAA);
171            break;
172        case kCircle_Geometry:
173            canvas->clipRRect(create_circle(offset), SkRegion::kReplace_Op, useAA);
174            break;
175        case kConvexPath_Geometry:
176            canvas->clipPath(create_convex_path(offset), SkRegion::kReplace_Op, useAA);
177            break;
178        case kConcavePath_Geometry:
179            canvas->clipPath(create_concave_path(offset), SkRegion::kReplace_Op, useAA);
180            break;
181        case kRectAndRect_Geometry: {
182            SkRect r = create_rect(offset);
183            r.offset(fSign * kXlate, fSign * kXlate);
184            canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips
185            canvas->clipRect(create_rect(offset), SkRegion::kIntersect_Op, useAA);
186            } break;
187        case kRectAndRRect_Geometry: {
188            SkRect r = create_rect(offset);
189            r.offset(fSign * kXlate, fSign * kXlate);
190            canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips
191            canvas->clipRRect(create_rrect(offset), SkRegion::kIntersect_Op, useAA);
192            } break;
193        case kRectAndConvex_Geometry: {
194            SkRect r = create_rect(offset);
195            r.offset(fSign * kXlate, fSign * kXlate);
196            canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips
197            canvas->clipPath(create_convex_path(offset), SkRegion::kIntersect_Op, useAA);
198            } break;
199        case kRectAndConcave_Geometry: {
200            SkRect r = create_rect(offset);
201            r.offset(fSign * kXlate, fSign * kXlate);
202            canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips
203            canvas->clipPath(create_concave_path(offset), SkRegion::kIntersect_Op, useAA);
204            } break;
205        }
206
207        SkISize size = canvas->getDeviceSize();
208        SkRect bigR = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height()));
209
210        SkPaint p;
211        p.setColor(SK_ColorRED);
212
213        canvas->drawRect(bigR, p);
214        canvas->restoreToCount(count);
215    }
216
217    // Draw a big red rect through some clip geometry and also draw that same
218    // geometry in black. The order in which they are drawn can be swapped.
219    // This tests whether the clip and normally drawn geometry match up.
220    void drawGeometry(SkCanvas* canvas, const SkPoint& offset, bool useAA) {
221        if (fClipFirst) {
222            this->drawClippedGeom(canvas, offset, useAA);
223        }
224
225        draw_normal_geom(canvas, offset, fGeom, useAA);
226
227        if (!fClipFirst) {
228            this->drawClippedGeom(canvas, offset, useAA);
229        }
230    }
231
232    void onDrawContent(SkCanvas* canvas) override {
233        SkScalar trans[2];
234        fTrans.timeToValues(SkTime::GetMSecs(), trans);
235
236        SkPoint offset;
237        offset.set(trans[0], trans[1]);
238
239        int saveCount = canvas->save();
240        this->drawGeometry(canvas, offset, false);
241        canvas->restoreToCount(saveCount);
242
243        this->inval(nullptr);
244    }
245
246private:
247    SkInterpolator  fTrans;
248    Geometry        fGeom;
249    bool            fClipFirst;
250    int             fSign;
251
252    typedef SampleView INHERITED;
253};
254
255//////////////////////////////////////////////////////////////////////////////
256
257static SkView* MyFactory() { return new ClipDrawMatchView; }
258static SkViewRegister reg(MyFactory);
259