Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/effects/SkCornerPathEffect.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "include/effects/SkCornerPathEffect.h"
9
10
#include "include/core/SkFlattenable.h"
11
#include "include/core/SkPath.h"
12
#include "include/core/SkPathEffect.h"
13
#include "include/core/SkPoint.h"
14
#include "include/core/SkRefCnt.h"
15
#include "include/core/SkScalar.h"
16
#include "include/core/SkTypes.h"
17
#include "include/private/base/SkFloatingPoint.h"
18
#include "src/core/SkPathEffectBase.h"
19
#include "src/core/SkReadBuffer.h"
20
#include "src/core/SkWriteBuffer.h"
21
22
class SkMatrix;
23
class SkStrokeRec;
24
struct SkRect;
25
26
static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius,
27
2.32M
                        SkPoint* step) {
28
2.32M
    SkScalar dist = SkPoint::Distance(a, b);
29
30
2.32M
    *step = b - a;
31
2.32M
    if (dist <= radius * 2) {
32
1.61M
        *step *= SK_ScalarHalf;
33
1.61M
        return false;
34
1.61M
    } else {
35
709k
        *step *= radius / dist;
36
709k
        return true;
37
709k
    }
38
2.32M
}
39
40
class SkCornerPathEffectImpl : public SkPathEffectBase {
41
public:
42
1.05M
    explicit SkCornerPathEffectImpl(SkScalar radius) : fRadius(radius) {
43
1.05M
        SkASSERT(radius > 0);
44
1.05M
    }
45
46
    bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
47
1.06M
                      const SkMatrix&) const override {
48
1.06M
        if (fRadius <= 0) {
49
0
            return false;
50
0
        }
51
52
1.06M
        SkPath::Iter    iter(src, false);
53
1.06M
        SkPath::Verb    verb, prevVerb = SkPath::kDone_Verb;
54
1.06M
        SkPoint         pts[4];
55
56
1.06M
        bool        closed;
57
1.06M
        SkPoint     moveTo, lastCorner;
58
1.06M
        SkVector    firstStep, step;
59
1.06M
        bool        prevIsValid = true;
60
61
        // to avoid warnings
62
1.06M
        step.set(0, 0);
63
1.06M
        moveTo.set(0, 0);
64
1.06M
        firstStep.set(0, 0);
65
1.06M
        lastCorner.set(0, 0);
66
67
9.83M
        for (;;) {
68
9.83M
            switch (verb = iter.next(pts)) {
69
2.13M
                case SkPath::kMove_Verb:
70
                    // close out the previous (open) contour
71
2.13M
                    if (SkPath::kLine_Verb == prevVerb) {
72
275k
                        dst->lineTo(lastCorner);
73
275k
                    }
74
2.13M
                    closed = iter.isClosedContour();
75
2.13M
                    if (closed) {
76
432k
                        moveTo = pts[0];
77
432k
                        prevIsValid = false;
78
1.70M
                    } else {
79
1.70M
                        dst->moveTo(pts[0]);
80
1.70M
                        prevIsValid = true;
81
1.70M
                    }
82
2.13M
                    break;
83
2.32M
                case SkPath::kLine_Verb: {
84
2.32M
                    bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
85
                    // prev corner
86
2.32M
                    if (!prevIsValid) {
87
115k
                        dst->moveTo(moveTo + step);
88
115k
                        prevIsValid = true;
89
2.20M
                    } else {
90
2.20M
                        dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX,
91
2.20M
                                    pts[0].fY + step.fY);
92
2.20M
                    }
93
2.32M
                    if (drawSegment) {
94
709k
                        dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
95
709k
                    }
96
2.32M
                    lastCorner = pts[1];
97
2.32M
                    prevIsValid = true;
98
2.32M
                    break;
99
0
                }
100
2.61M
                case SkPath::kQuad_Verb:
101
                    // TBD - just replicate the curve for now
102
2.61M
                    if (!prevIsValid) {
103
82.6k
                        dst->moveTo(pts[0]);
104
82.6k
                        prevIsValid = true;
105
82.6k
                    }
106
2.61M
                    dst->quadTo(pts[1], pts[2]);
107
2.61M
                    lastCorner = pts[2];
108
2.61M
                    firstStep.set(0, 0);
109
2.61M
                    break;
110
957k
                case SkPath::kConic_Verb:
111
                    // TBD - just replicate the curve for now
112
957k
                    if (!prevIsValid) {
113
87.8k
                        dst->moveTo(pts[0]);
114
87.8k
                        prevIsValid = true;
115
87.8k
                    }
116
957k
                    dst->conicTo(pts[1], pts[2], iter.conicWeight());
117
957k
                    lastCorner = pts[2];
118
957k
                    firstStep.set(0, 0);
119
957k
                    break;
120
355k
                case SkPath::kCubic_Verb:
121
355k
                    if (!prevIsValid) {
122
62.2k
                        dst->moveTo(pts[0]);
123
62.2k
                        prevIsValid = true;
124
62.2k
                    }
125
                    // TBD - just replicate the curve for now
126
355k
                    dst->cubicTo(pts[1], pts[2], pts[3]);
127
355k
                    lastCorner = pts[3];
128
355k
                    firstStep.set(0, 0);
129
355k
                    break;
130
383k
                case SkPath::kClose_Verb:
131
383k
                    if (firstStep.fX || firstStep.fY) {
132
71.8k
                        dst->quadTo(lastCorner.fX, lastCorner.fY,
133
71.8k
                                    lastCorner.fX + firstStep.fX,
134
71.8k
                                    lastCorner.fY + firstStep.fY);
135
71.8k
                    }
136
383k
                    dst->close();
137
383k
                    prevIsValid = false;
138
383k
                    break;
139
1.06M
                case SkPath::kDone_Verb:
140
1.06M
                    if (prevIsValid) {
141
944k
                        dst->lineTo(lastCorner);
142
944k
                    }
143
1.06M
                    return true;
144
0
                default:
145
0
                    SkDEBUGFAIL("default should not be reached");
146
0
                    return false;
147
9.83M
            }
148
149
8.76M
            if (SkPath::kMove_Verb == prevVerb) {
150
2.13M
                firstStep = step;
151
2.13M
            }
152
8.76M
            prevVerb = verb;
153
8.76M
        }
154
1.06M
    }
155
156
11.9k
    bool computeFastBounds(SkRect*) const override {
157
        // Rounding sharp corners within a path produces a new path that is still contained within
158
        // the original's bounds, so leave 'bounds' unmodified.
159
11.9k
        return true;
160
11.9k
    }
161
162
12
    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
163
12
        return SkCornerPathEffect::Make(buffer.readScalar());
164
12
    }
165
166
1.69k
    void flatten(SkWriteBuffer& buffer) const override {
167
1.69k
        buffer.writeScalar(fRadius);
168
1.69k
    }
169
170
1.69k
    Factory getFactory() const override { return CreateProc; }
171
1.69k
    const char* getTypeName() const override { return "SkCornerPathEffect"; }
172
173
private:
174
    const SkScalar fRadius;
175
176
    using INHERITED = SkPathEffectBase;
177
};
178
179
//////////////////////////////////////////////////////////////////////////////////////////////////
180
181
1.29M
sk_sp<SkPathEffect> SkCornerPathEffect::Make(SkScalar radius) {
182
1.29M
    return SkIsFinite(radius) && (radius > 0) ?
183
1.05M
            sk_sp<SkPathEffect>(new SkCornerPathEffectImpl(radius)) : nullptr;
184
1.29M
}
185
186
3
void SkCornerPathEffect::RegisterFlattenables() {
187
3
    SkFlattenable::Register("SkCornerPathEffect", SkCornerPathEffectImpl::CreateProc);
188
3
}