2 * Copyright 2011 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
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/SkTime.h"
16 #include "include/core/SkTypeface.h"
17 #include "include/effects/SkGradientShader.h"
18 #include "include/private/SkTo.h"
19 #include "samplecode/Sample.h"
20 #include "src/utils/SkUTF.h"
24 class PathClipView : public Sample {
29 PathClipView() : fOval(SkRect::MakeWH(200, 50)), fCenter(SkPoint::Make(250, 250)) {}
32 SkString name() override { return SkString("PathClip"); }
34 void onDrawContent(SkCanvas* canvas) override {
35 const SkRect oval = fOval.makeOffset(fCenter.fX - fOval.centerX(),
36 fCenter.fY - fOval.centerY());
41 p.setStyle(SkPaint::kStroke_Style);
42 canvas->drawOval(oval, p);
44 const SkRect r = SkRect::MakeLTRB(200, 200, 300, 300);
47 p.setStyle(SkPaint::kFill_Style);
48 p.setColor(SK_ColorRED);
49 canvas->drawRect(r, p);
51 p.setColor(0x800000FF);
52 canvas->drawOval(oval, p);
55 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
56 return new Click([&](Click* c) {
63 using INHERITED = Sample;
65 DEF_SAMPLE( return new PathClipView; )
67 //////////////////////////////////////////////////////////////////////////////
69 static int clip_line(const SkRect& bounds, SkPoint p0, SkPoint p1, SkPoint edges[]) {
70 SkPoint* edgesStart = edges;
80 // now we're monotonic in Y: p0 <= p1
81 if (p1.fY <= bounds.top() || p0.fY >= bounds.bottom()) {
85 double dxdy = (double)(p1.fX - p0.fX) / (p1.fY - p0.fY);
86 if (p0.fY < bounds.top()) {
87 p0.fX = SkDoubleToScalar(p0.fX + dxdy * (bounds.top() - p0.fY));
90 if (p1.fY > bounds.bottom()) {
91 p1.fX = SkDoubleToScalar(p1.fX + dxdy * (bounds.bottom() - p1.fY));
92 p1.fY = bounds.bottom();
95 // Now p0...p1 is strictly inside bounds vertically, so we just need to clip horizontally
101 // now we're left-to-right: p0 .. p1
103 if (p1.fX <= bounds.left()) { // entirely to the left
104 p0.fX = p1.fX = bounds.left();
109 if (p0.fX >= bounds.right()) { // entirely to the right
110 p0.fX = p1.fX = bounds.right();
116 if (p0.fX < bounds.left()) {
117 float y = SkDoubleToScalar(p0.fY + (bounds.left() - p0.fX) / dxdy);
118 *edges++ = SkPoint::Make(bounds.left(), p0.fY);
119 *edges++ = SkPoint::Make(bounds.left(), y);
120 p0.set(bounds.left(), y);
122 if (p1.fX > bounds.right()) {
123 float y = SkDoubleToScalar(p0.fY + (bounds.right() - p0.fX) / dxdy);
125 *edges++ = SkPoint::Make(bounds.right(), y);
126 *edges++ = SkPoint::Make(bounds.right(), p1.fY);
131 return SkToInt(edges - edgesStart);
134 static void draw_clipped_line(SkCanvas* canvas, const SkRect& bounds,
135 SkPoint p0, SkPoint p1, const SkPaint& paint) {
137 int count = clip_line(bounds, p0, p1, verts);
140 path.addPoly(verts, count, false);
141 canvas->drawPath(path, paint);
144 // Demonstrate edge-clipping that is used in the scan converter
146 class EdgeClipView : public Sample {
153 SkColor fEdgeColor[N];
155 EdgeClipView() : fClip(SkRect::MakeLTRB(150, 150, 550, 450)) {
156 fPoly[0].set(300, 40);
157 fPoly[1].set(550, 250);
158 fPoly[2].set(40, 450);
160 fEdgeColor[0] = 0xFFFF0000;
161 fEdgeColor[1] = 0xFF00FF00;
162 fEdgeColor[2] = 0xFF0000FF;
166 SkString name() override { return SkString("EdgeClip"); }
168 static SkScalar snap(SkScalar x) {
169 return SkScalarRoundToScalar(x * 0.5f) * 2;
171 static SkPoint snap(const SkPoint& pt) {
172 return SkPoint::Make(snap(pt.x()), snap(pt.y()));
174 static void snap(SkPoint dst[], const SkPoint src[], int count) {
175 for (int i = 0; i < count; ++i) {
176 dst[i] = snap(src[i]);
180 void onDrawContent(SkCanvas* canvas) override {
182 path.addPoly(fPoly, N, true);
184 // Draw the full triangle, stroked and filled
186 p.setAntiAlias(true);
187 p.setColor(0xFFE0E0E0);
188 canvas->drawPath(path, p);
189 p.setStyle(SkPaint::kStroke_Style);
191 for (int i = 0; i < N; ++i) {
192 const int j = (i + 1) % N;
193 p.setColor(fEdgeColor[i]);
195 canvas->drawLine(fPoly[i], fPoly[j], p);
197 p.setStyle(SkPaint::kFill_Style);
199 // Draw the clip itself
200 p.setColor(0xFF8888CC);
201 canvas->drawRect(fClip, p);
203 // Draw the filled triangle through the clip
204 p.setColor(0xFF88CC88);
206 canvas->clipRect(fClip);
207 canvas->drawPath(path, p);
210 p.setStyle(SkPaint::kStroke_Style);
213 // Draw each of the "Edges" that survived the clipping
214 // We use a layer, so we can PLUS the different edge-colors, showing where two edges
215 // canceled each other out.
216 canvas->saveLayer(nullptr, nullptr);
217 p.setBlendMode(SkBlendMode::kPlus);
218 for (int i = 0; i < N; ++i) {
219 const int j = (i + 1) % N;
220 p.setColor(fEdgeColor[i]);
221 draw_clipped_line(canvas, fClip, fPoly[i], fPoly[j], p);
226 class MyClick : public Click {
229 virtual void handleMove() = 0;
232 class VertClick : public MyClick {
235 VertClick(SkPoint* pt) : fPt(pt) {}
236 void handleMove() override { *fPt = snap(fCurr); }
239 class DragRectClick : public MyClick {
242 DragRectClick(SkRect* rect) : fRect(rect) {}
243 void handleMove() override { fRect->offset(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()); }
246 class DragPolyClick : public MyClick {
251 DragPolyClick(SkPoint poly[], int count) : fPoly(poly), fCount(count)
253 SkASSERT((size_t)count <= SK_ARRAY_COUNT(fSrc));
254 memcpy(fSrc, poly, count * sizeof(SkPoint));
256 void handleMove() override {
257 const SkScalar dx = fCurr.x() - fOrig.x();
258 const SkScalar dy = fCurr.y() - fOrig.y();
259 for (int i = 0; i < fCount; ++i) {
260 fPoly[i].set(snap(fSrc[i].x() + dx), snap(fSrc[i].y() + dy));
265 class DoNothingClick : public MyClick {
268 void handleMove() override {}
271 static bool hit_test(const SkPoint& pt, SkScalar x, SkScalar y) {
272 const SkScalar rad = 8;
273 const SkScalar dx = pt.x() - x;
274 const SkScalar dy = pt.y() - y;
275 return dx*dx + dy*dy <= rad*rad;
278 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
279 for (int i = 0; i < N; ++i) {
280 if (hit_test(fPoly[i], x, y)) {
281 return new VertClick(&fPoly[i]);
286 path.addPoly(fPoly, N, true);
287 if (path.contains(x, y)) {
288 return new DragPolyClick(fPoly, N);
291 if (fClip.intersects(SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1))) {
292 return new DragRectClick(&fClip);
294 return new DoNothingClick();
297 bool onClick(Click* click) override {
298 ((MyClick*)click)->handleMove();
303 using INHERITED = Sample;
306 DEF_SAMPLE( return new EdgeClipView; )