• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright 2019 Google LLC
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 "SkCurve.h"
9 
10 #include "SkParticleData.h"
11 #include "SkRandom.h"
12 #include "SkReflected.h"
13 
14 constexpr SkFieldVisitor::EnumStringMapping gCurveSegmentTypeMapping[] = {
15     { kConstant_SegmentType, "Constant" },
16     { kLinear_SegmentType,   "Linear" },
17     { kCubic_SegmentType,    "Cubic" },
18 };
19 
operator +(SkColor4f c1,SkColor4f c2)20 static SkColor4f operator+(SkColor4f c1, SkColor4f c2) {
21     return { c1.fR + c2.fR, c1.fG + c2.fG, c1.fB + c2.fB, c1.fA + c2.fA };
22 }
23 
operator -(SkColor4f c1,SkColor4f c2)24 static SkColor4f operator-(SkColor4f c1, SkColor4f c2) {
25     return { c1.fR - c2.fR, c1.fG - c2.fG, c1.fB - c2.fB, c1.fA - c2.fA };
26 }
27 
28 template <typename T>
eval_cubic(const T * pts,SkScalar x)29 static T eval_cubic(const T* pts, SkScalar x) {
30     SkScalar ix = (1 - x);
31     return pts[0]*(ix*ix*ix) + pts[1]*(3*ix*ix*x) + pts[2]*(3*ix*x*x) + pts[3]*(x*x*x);
32 }
33 
34 template <typename T>
eval_segment(const T * pts,SkScalar x,int type)35 static T eval_segment(const T* pts, SkScalar x, int type) {
36     switch (type) {
37         case kLinear_SegmentType:
38             return pts[0] + (pts[3] - pts[0]) * x;
39         case kCubic_SegmentType:
40             return eval_cubic(pts, x);
41         case kConstant_SegmentType:
42         default:
43             return pts[0];
44     }
45 }
46 
eval(SkScalar x,SkScalar t,bool negate) const47 SkScalar SkCurveSegment::eval(SkScalar x, SkScalar t, bool negate) const {
48     SkScalar result = eval_segment(fMin, x, fType);
49     if (fRanged) {
50         result += (eval_segment(fMax, x, fType) - result) * t;
51     }
52     if (fBidirectional && negate) {
53         result = -result;
54     }
55     return result;
56 }
57 
visitFields(SkFieldVisitor * v)58 void SkCurveSegment::visitFields(SkFieldVisitor* v) {
59     v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
60     v->visit("Ranged", fRanged);
61     v->visit("Bidirectional", fBidirectional);
62     v->visit("A0", fMin[0]);
63     if (fType == kCubic_SegmentType) {
64         v->visit("B0", fMin[1]);
65         v->visit("C0", fMin[2]);
66     }
67     if (fType != kConstant_SegmentType) {
68         v->visit("D0", fMin[3]);
69     }
70     if (fRanged) {
71         v->visit("A1", fMax[0]);
72         if (fType == kCubic_SegmentType) {
73             v->visit("B1", fMax[1]);
74             v->visit("C1", fMax[2]);
75         }
76         if (fType != kConstant_SegmentType) {
77             v->visit("D1", fMax[3]);
78         }
79     }
80 }
81 
eval(const SkParticleUpdateParams & params,SkParticleState & ps) const82 SkScalar SkCurve::eval(const SkParticleUpdateParams& params, SkParticleState& ps) const {
83     SkASSERT(fSegments.count() == fXValues.count() + 1);
84 
85     float x = fInput.eval(params, ps);
86 
87     int i = 0;
88     for (; i < fXValues.count(); ++i) {
89         if (x <= fXValues[i]) {
90             break;
91         }
92     }
93 
94     SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
95     SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
96     SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
97     if (!SkScalarIsFinite(segmentX)) {
98         segmentX = rangeMin;
99     }
100     SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
101 
102     // Always pull t and negate here, so that the stable generator behaves consistently, even if
103     // our segments use an inconsistent feature-set.
104     SkScalar t = ps.fRandom.nextF();
105     bool negate = ps.fRandom.nextBool();
106     return fSegments[i].eval(segmentX, t, negate);
107 }
108 
visitFields(SkFieldVisitor * v)109 void SkCurve::visitFields(SkFieldVisitor* v) {
110     v->visit("Input", fInput);
111     v->visit("XValues", fXValues);
112     v->visit("Segments", fSegments);
113 
114     // Validate and fixup
115     if (fSegments.empty()) {
116         fSegments.push_back().setConstant(0.0f);
117     }
118     fXValues.resize_back(fSegments.count() - 1);
119     for (int i = 0; i < fXValues.count(); ++i) {
120         fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
121     }
122 }
123 
eval(SkScalar x,SkScalar t) const124 SkColor4f SkColorCurveSegment::eval(SkScalar x, SkScalar t) const {
125     SkColor4f result = eval_segment(fMin, x, fType);
126     if (fRanged) {
127         result = result + (eval_segment(fMax, x, fType) - result) * t;
128     }
129     return result;
130 }
131 
visitFields(SkFieldVisitor * v)132 void SkColorCurveSegment::visitFields(SkFieldVisitor* v) {
133     v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
134     v->visit("Ranged", fRanged);
135     v->visit("A0", fMin[0]);
136     if (fType == kCubic_SegmentType) {
137         v->visit("B0", fMin[1]);
138         v->visit("C0", fMin[2]);
139     }
140     if (fType != kConstant_SegmentType) {
141         v->visit("D0", fMin[3]);
142     }
143     if (fRanged) {
144         v->visit("A1", fMax[0]);
145         if (fType == kCubic_SegmentType) {
146             v->visit("B1", fMax[1]);
147             v->visit("C1", fMax[2]);
148         }
149         if (fType != kConstant_SegmentType) {
150             v->visit("D1", fMax[3]);
151         }
152     }
153 }
154 
eval(const SkParticleUpdateParams & params,SkParticleState & ps) const155 SkColor4f SkColorCurve::eval(const SkParticleUpdateParams& params, SkParticleState& ps) const {
156     SkASSERT(fSegments.count() == fXValues.count() + 1);
157 
158     float x = fInput.eval(params, ps);
159 
160     int i = 0;
161     for (; i < fXValues.count(); ++i) {
162         if (x <= fXValues[i]) {
163             break;
164         }
165     }
166 
167     SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
168     SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
169     SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
170     if (!SkScalarIsFinite(segmentX)) {
171         segmentX = rangeMin;
172     }
173     SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
174     return fSegments[i].eval(segmentX, ps.fRandom.nextF());
175 }
176 
visitFields(SkFieldVisitor * v)177 void SkColorCurve::visitFields(SkFieldVisitor* v) {
178     v->visit("Input", fInput);
179     v->visit("XValues", fXValues);
180     v->visit("Segments", fSegments);
181 
182     // Validate and fixup
183     if (fSegments.empty()) {
184         fSegments.push_back().setConstant(SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f });
185     }
186     fXValues.resize_back(fSegments.count() - 1);
187     for (int i = 0; i < fXValues.count(); ++i) {
188         fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
189     }
190 }
191