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