xref: /aosp_15_r20/external/skia/tools/viewer/PathClipSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 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 "include/core/SkCanvas.h"
9 #include "include/core/SkColorFilter.h"
10 #include "include/core/SkColorPriv.h"
11 #include "include/core/SkGraphics.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkRegion.h"
14 #include "include/core/SkShader.h"
15 #include "include/core/SkTypeface.h"
16 #include "include/effects/SkGradientShader.h"
17 #include "include/private/base/SkTo.h"
18 #include "src/base/SkTime.h"
19 #include "src/base/SkUTF.h"
20 #include "tools/viewer/ClickHandlerSlide.h"
21 
22 #include <utility>
23 
24 class PathClipSlide : public ClickHandlerSlide {
25 public:
26     SkRect fOval;
27     SkPoint fCenter;
28 
PathClipSlide()29     PathClipSlide() : fOval(SkRect::MakeWH(200, 50)), fCenter(SkPoint::Make(250, 250)) {
30         fName = "PathClip";
31     }
32 
draw(SkCanvas * canvas)33     void draw(SkCanvas* canvas) override {
34         const SkRect oval = fOval.makeOffset(fCenter.fX - fOval.centerX(),
35                                              fCenter.fY - fOval.centerY());
36 
37         SkPaint p;
38         p.setAntiAlias(true);
39 
40         p.setStyle(SkPaint::kStroke_Style);
41         canvas->drawOval(oval, p);
42 
43         const SkRect r = SkRect::MakeLTRB(200, 200, 300, 300);
44         canvas->clipRect(r);
45 
46         p.setStyle(SkPaint::kFill_Style);
47         p.setColor(SK_ColorRED);
48         canvas->drawRect(r, p);
49 
50         p.setColor(0x800000FF);
51         canvas->drawOval(oval, p);
52     }
53 
54 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey)55     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
56         return new Click([&](Click* c) {
57             fCenter = c->fCurr;
58             return false;
59         });
60     }
61 
onClick(ClickHandlerSlide::Click *)62     bool onClick(ClickHandlerSlide::Click *) override { return false; }
63 };
DEF_SLIDE(return new PathClipSlide;)64 DEF_SLIDE( return new PathClipSlide; )
65 
66 //////////////////////////////////////////////////////////////////////////////
67 
68 static int clip_line(const SkRect& bounds, SkPoint p0, SkPoint p1, SkPoint edges[]) {
69     SkPoint* edgesStart = edges;
70 
71     if (p0.fY == p1.fY) {
72         return 0;
73     }
74 
75     if (p0.fY > p1.fY) {
76         using std::swap;
77         swap(p0, p1);
78     }
79     // now we're monotonic in Y: p0 <= p1
80     if (p1.fY <= bounds.top() || p0.fY >= bounds.bottom()) {
81         return 0;
82     }
83 
84     double dxdy = (double)(p1.fX - p0.fX) / (p1.fY - p0.fY);
85     if (p0.fY < bounds.top()) {
86         p0.fX = SkDoubleToScalar(p0.fX + dxdy * (bounds.top() - p0.fY));
87         p0.fY = bounds.top();
88     }
89     if (p1.fY > bounds.bottom()) {
90         p1.fX = SkDoubleToScalar(p1.fX + dxdy * (bounds.bottom() - p1.fY));
91         p1.fY = bounds.bottom();
92     }
93 
94     // Now p0...p1 is strictly inside bounds vertically, so we just need to clip horizontally
95 
96     if (p0.fX > p1.fX) {
97         using std::swap;
98         swap(p0, p1);
99     }
100     // now we're left-to-right: p0 .. p1
101 
102     if (p1.fX <= bounds.left()) {   // entirely to the left
103         p0.fX = p1.fX = bounds.left();
104         *edges++ = p0;
105         *edges++ = p1;
106         return 2;
107     }
108     if (p0.fX >= bounds.right()) {  // entirely to the right
109         p0.fX = p1.fX = bounds.right();
110         *edges++ = p0;
111         *edges++ = p1;
112         return 2;
113     }
114 
115     if (p0.fX < bounds.left()) {
116         float y = SkDoubleToScalar(p0.fY + (bounds.left() - p0.fX) / dxdy);
117         *edges++ = SkPoint::Make(bounds.left(), p0.fY);
118         *edges++ = SkPoint::Make(bounds.left(), y);
119         p0.set(bounds.left(), y);
120     }
121     if (p1.fX > bounds.right()) {
122         float y = SkDoubleToScalar(p0.fY + (bounds.right() - p0.fX) / dxdy);
123         *edges++ = p0;
124         *edges++ = SkPoint::Make(bounds.right(), y);
125         *edges++ = SkPoint::Make(bounds.right(), p1.fY);
126     } else {
127         *edges++ = p0;
128         *edges++ = p1;
129     }
130     return SkToInt(edges - edgesStart);
131 }
132 
draw_clipped_line(SkCanvas * canvas,const SkRect & bounds,SkPoint p0,SkPoint p1,const SkPaint & paint)133 static void draw_clipped_line(SkCanvas* canvas, const SkRect& bounds,
134                               SkPoint p0, SkPoint p1, const SkPaint& paint) {
135     SkPoint verts[6];
136     int count = clip_line(bounds, p0, p1, verts);
137 
138     SkPath path;
139     path.addPoly(verts, count, false);
140     canvas->drawPath(path, paint);
141 }
142 
143 // Demonstrate edge-clipping that is used in the scan converter
144 //
145 class EdgeClipSlide : public ClickHandlerSlide {
146     enum {
147         N = 3
148     };
149     SkPoint fPoly[N];
150     SkRect  fClip;
151     SkColor fEdgeColor[N];
152 
153 public:
EdgeClipSlide()154     EdgeClipSlide() : fClip(SkRect::MakeLTRB(150, 150, 550, 450)) {
155         fPoly[0].set(300, 40);
156         fPoly[1].set(550, 250);
157         fPoly[2].set(40, 450);
158 
159         fEdgeColor[0] = 0xFFFF0000;
160         fEdgeColor[1] = 0xFF00FF00;
161         fEdgeColor[2] = 0xFF0000FF;
162         fName = "EdgeClip";
163     }
164 
snap(SkScalar x)165     static SkScalar snap(SkScalar x) {
166         return SkScalarRoundToScalar(x * 0.5f) * 2;
167     }
snap(const SkPoint & pt)168     static SkPoint snap(const SkPoint& pt) {
169         return SkPoint::Make(snap(pt.x()), snap(pt.y()));
170     }
snap(SkPoint dst[],const SkPoint src[],int count)171     static void snap(SkPoint dst[], const SkPoint src[], int count) {
172         for (int i = 0; i < count; ++i) {
173             dst[i] = snap(src[i]);
174         }
175     }
176 
draw(SkCanvas * canvas)177     void draw(SkCanvas* canvas) override {
178         SkPath path;
179         path.addPoly(fPoly, N, true);
180 
181         // Draw the full triangle, stroked and filled
182         SkPaint p;
183         p.setAntiAlias(true);
184         p.setColor(0xFFE0E0E0);
185         canvas->drawPath(path, p);
186         p.setStyle(SkPaint::kStroke_Style);
187         p.setStrokeWidth(2);
188         for (int i = 0; i < N; ++i) {
189             const int j = (i + 1) % N;
190             p.setColor(fEdgeColor[i]);
191             p.setAlpha(0x88);
192             canvas->drawLine(fPoly[i], fPoly[j], p);
193         }
194         p.setStyle(SkPaint::kFill_Style);
195 
196         // Draw the clip itself
197         p.setColor(0xFF8888CC);
198         canvas->drawRect(fClip, p);
199 
200         // Draw the filled triangle through the clip
201         p.setColor(0xFF88CC88);
202         canvas->save();
203         canvas->clipRect(fClip);
204         canvas->drawPath(path, p);
205         canvas->restore();
206 
207         p.setStyle(SkPaint::kStroke_Style);
208         p.setStrokeWidth(6);
209 
210         // Draw each of the "Edges" that survived the clipping
211         // We use a layer, so we can PLUS the different edge-colors, showing where two edges
212         // canceled each other out.
213         canvas->saveLayer(nullptr, nullptr);
214         p.setBlendMode(SkBlendMode::kPlus);
215         for (int i = 0; i < N; ++i) {
216             const int j = (i + 1) % N;
217             p.setColor(fEdgeColor[i]);
218             draw_clipped_line(canvas, fClip, fPoly[i], fPoly[j], p);
219         }
220         canvas->restore();
221     }
222 
223     class MyClick : public Click {
224     public:
MyClick()225         MyClick() {}
226         virtual void handleMove() = 0;
227     };
228 
229     class VertClick : public MyClick {
230         SkPoint* fPt;
231     public:
VertClick(SkPoint * pt)232         VertClick(SkPoint* pt) : fPt(pt) {}
handleMove()233         void handleMove() override { *fPt = snap(fCurr); }
234     };
235 
236     class DragRectClick : public MyClick {
237         SkRect* fRect;
238     public:
DragRectClick(SkRect * rect)239         DragRectClick(SkRect* rect) : fRect(rect) {}
handleMove()240         void handleMove() override { fRect->offset(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()); }
241     };
242 
243     class DragPolyClick : public MyClick {
244         SkPoint fSrc[100];
245         SkPoint* fPoly;
246         int fCount;
247     public:
DragPolyClick(SkPoint poly[],int count)248         DragPolyClick(SkPoint poly[], int count) : fPoly(poly), fCount(count)
249         {
250             SkASSERT((size_t)count <= std::size(fSrc));
251             memcpy(fSrc, poly, count * sizeof(SkPoint));
252         }
handleMove()253         void handleMove() override {
254             const SkScalar dx = fCurr.x() - fOrig.x();
255             const SkScalar dy = fCurr.y() - fOrig.y();
256             for (int i = 0; i < fCount; ++i) {
257                 fPoly[i].set(snap(fSrc[i].x() + dx), snap(fSrc[i].y() + dy));
258             }
259         }
260     };
261 
262     class DoNothingClick : public MyClick {
263     public:
DoNothingClick()264         DoNothingClick() {}
handleMove()265         void handleMove() override {}
266     };
267 
hit_test(const SkPoint & pt,SkScalar x,SkScalar y)268     static bool hit_test(const SkPoint& pt, SkScalar x, SkScalar y) {
269         const SkScalar rad = 8;
270         const SkScalar dx = pt.x() - x;
271         const SkScalar dy = pt.y() - y;
272         return dx*dx + dy*dy <= rad*rad;
273     }
274 
275 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey)276     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
277         for (int i = 0; i < N; ++i) {
278             if (hit_test(fPoly[i], x, y)) {
279                 return new VertClick(&fPoly[i]);
280             }
281         }
282 
283         SkPath path;
284         path.addPoly(fPoly, N, true);
285         if (path.contains(x, y)) {
286             return new DragPolyClick(fPoly, N);
287         }
288 
289         if (fClip.intersects(SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1))) {
290             return new DragRectClick(&fClip);
291         }
292         return new DoNothingClick();
293     }
294 
onClick(Click * click)295     bool onClick(Click* click) override {
296         ((MyClick*)click)->handleMove();
297         return true;
298     }
299 };
300 
301 DEF_SLIDE( return new EdgeClipSlide; )
302