1/*
2 * Copyright 2006 The Android Open Source Project
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
9#include "include/core/SkPath.h"
10#include "include/core/SkPoint.h"
11#include "include/effects/SkCornerPathEffect.h"
12#include "src/core/SkReadBuffer.h"
13#include "src/core/SkWriteBuffer.h"
14
15SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) {
16 // check with ! to catch NaNs
17 if (!(radius > 0)) {
18 radius = 0;
19 }
20 fRadius = radius;
21}
22SkCornerPathEffect::~SkCornerPathEffect() {}
23
24static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius,
25 SkPoint* step) {
26 SkScalar dist = SkPoint::Distance(a, b);
27
28 *step = b - a;
29 if (dist <= radius * 2) {
30 *step *= SK_ScalarHalf;
31 return false;
32 } else {
33 *step *= radius / dist;
34 return true;
35 }
36}
37
38bool SkCornerPathEffect::onFilterPath(SkPath* dst, const SkPath& src,
39 SkStrokeRec*, const SkRect*) const {
40 if (fRadius <= 0) {
41 return false;
42 }
43
44 SkPath::Iter iter(src, false);
45 SkPath::Verb verb, prevVerb = SkPath::kDone_Verb;
46 SkPoint pts[4];
47
48 bool closed;
49 SkPoint moveTo, lastCorner;
50 SkVector firstStep, step;
51 bool prevIsValid = true;
52
53 // to avoid warnings
54 step.set(0, 0);
55 moveTo.set(0, 0);
56 firstStep.set(0, 0);
57 lastCorner.set(0, 0);
58
59 for (;;) {
60 switch (verb = iter.next(pts)) {
61 case SkPath::kMove_Verb:
62 // close out the previous (open) contour
63 if (SkPath::kLine_Verb == prevVerb) {
64 dst->lineTo(lastCorner);
65 }
66 closed = iter.isClosedContour();
67 if (closed) {
68 moveTo = pts[0];
69 prevIsValid = false;
70 } else {
71 dst->moveTo(pts[0]);
72 prevIsValid = true;
73 }
74 break;
75 case SkPath::kLine_Verb: {
76 bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
77 // prev corner
78 if (!prevIsValid) {
79 dst->moveTo(moveTo + step);
80 prevIsValid = true;
81 } else {
82 dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX,
83 pts[0].fY + step.fY);
84 }
85 if (drawSegment) {
86 dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
87 }
88 lastCorner = pts[1];
89 prevIsValid = true;
90 break;
91 }
92 case SkPath::kQuad_Verb:
93 // TBD - just replicate the curve for now
94 if (!prevIsValid) {
95 dst->moveTo(pts[0]);
96 prevIsValid = true;
97 }
98 dst->quadTo(pts[1], pts[2]);
99 lastCorner = pts[2];
100 firstStep.set(0, 0);
101 break;
102 case SkPath::kConic_Verb:
103 // TBD - just replicate the curve for now
104 if (!prevIsValid) {
105 dst->moveTo(pts[0]);
106 prevIsValid = true;
107 }
108 dst->conicTo(pts[1], pts[2], iter.conicWeight());
109 lastCorner = pts[2];
110 firstStep.set(0, 0);
111 break;
112 case SkPath::kCubic_Verb:
113 if (!prevIsValid) {
114 dst->moveTo(pts[0]);
115 prevIsValid = true;
116 }
117 // TBD - just replicate the curve for now
118 dst->cubicTo(pts[1], pts[2], pts[3]);
119 lastCorner = pts[3];
120 firstStep.set(0, 0);
121 break;
122 case SkPath::kClose_Verb:
123 if (firstStep.fX || firstStep.fY) {
124 dst->quadTo(lastCorner.fX, lastCorner.fY,
125 lastCorner.fX + firstStep.fX,
126 lastCorner.fY + firstStep.fY);
127 }
128 dst->close();
129 prevIsValid = false;
130 break;
131 case SkPath::kDone_Verb:
132 if (prevIsValid) {
133 dst->lineTo(lastCorner);
134 }
135 return true;
136 default:
137 SkDEBUGFAIL("default should not be reached");
138 return false;
139 }
140
141 if (SkPath::kMove_Verb == prevVerb) {
142 firstStep = step;
143 }
144 prevVerb = verb;
145 }
146
147 return true;
148}
149
150sk_sp<SkFlattenable> SkCornerPathEffect::CreateProc(SkReadBuffer& buffer) {
151 return SkCornerPathEffect::Make(buffer.readScalar());
152}
153
154void SkCornerPathEffect::flatten(SkWriteBuffer& buffer) const {
155 buffer.writeScalar(fRadius);
156}
157