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
24 enum 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]
37 static const float kMin = 100.5f;
38 static const float kMid = 200.0f;
39 static 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)
43 static const float kXlate = 100.0f;
44 
create_rect(const SkPoint & offset)45 SkRect create_rect(const SkPoint& offset) {
46     SkRect r = SkRect::MakeLTRB(kMin, kMin, kMax, kMax);
47     r.offset(offset);
48     return r;
49 }
50 
create_rrect(const SkPoint & offset)51 SkRRect create_rrect(const SkPoint& offset) {
52     SkRRect rrect;
53     rrect.setRectXY(create_rect(offset), 10, 10);
54     return rrect;
55 }
56 
create_circle(const SkPoint & offset)57 SkRRect create_circle(const SkPoint& offset) {
58     SkRRect circle;
59     circle.setOval(create_rect(offset));
60     return circle;
61 }
62 
create_convex_path(const SkPoint & offset)63 SkPath 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 
create_concave_path(const SkPoint & offset)73 SkPath 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 
draw_normal_geom(SkCanvas * canvas,const SkPoint & offset,int geom,bool useAA)89 static 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 
117 class ClipDrawMatchView : public SampleView {
118 public:
ClipDrawMatchView()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 
135 protected:
onQuery(SkEvent * evt)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 
drawClippedGeom(SkCanvas * canvas,const SkPoint & offset,bool useAA)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.
drawGeometry(SkCanvas * canvas,const SkPoint & offset,bool useAA)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 
onDrawContent(SkCanvas * canvas)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 
246 private:
247     SkInterpolator  fTrans;
248     Geometry        fGeom;
249     bool            fClipFirst;
250     int             fSign;
251 
252     typedef SampleView INHERITED;
253 };
254 
255 //////////////////////////////////////////////////////////////////////////////
256 
MyFactory()257 static SkView* MyFactory() { return new ClipDrawMatchView; }
258 static SkViewRegister reg(MyFactory);
259