blob: e0728c2663acfc452aca3bb89d00d69cb2a7f528 [file] [log] [blame]
Brian Osman7c979f52019-02-12 13:27:51 -05001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "modules/particles/include/SkCurve.h"
Brian Osman7c979f52019-02-12 13:27:51 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/utils/SkRandom.h"
11#include "modules/particles/include/SkParticleData.h"
12#include "modules/particles/include/SkReflected.h"
Brian Osman7c979f52019-02-12 13:27:51 -050013
Brian Osman34d13312019-02-27 11:19:19 -050014constexpr SkFieldVisitor::EnumStringMapping gCurveSegmentTypeMapping[] = {
15 { kConstant_SegmentType, "Constant" },
16 { kLinear_SegmentType, "Linear" },
17 { kCubic_SegmentType, "Cubic" },
18};
Brian Osman7c979f52019-02-12 13:27:51 -050019
Brian Osman125daa42019-02-20 12:25:20 -050020static 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
Brian Osman34d13312019-02-27 11:19:19 -050024static 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
28template <typename T>
Brian Osmane1cb9ac2019-06-24 10:44:02 -040029static T eval_cubic(const T* pts, float x) {
30 float ix = (1 - x);
Brian Osman125daa42019-02-20 12:25:20 -050031 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
Brian Osman34d13312019-02-27 11:19:19 -050034template <typename T>
Brian Osmane1cb9ac2019-06-24 10:44:02 -040035static T eval_segment(const T* pts, float x, int type) {
Brian Osman34d13312019-02-27 11:19:19 -050036 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
Brian Osmane1cb9ac2019-06-24 10:44:02 -040047float SkCurveSegment::eval(float x, float t, bool negate) const {
48 float result = eval_segment(fMin, x, fType);
Brian Osman8b6283f2019-02-14 16:55:21 -050049 if (fRanged) {
Brian Osman34d13312019-02-27 11:19:19 -050050 result += (eval_segment(fMax, x, fType) - result) * t;
Brian Osman8b6283f2019-02-14 16:55:21 -050051 }
Brian Osman1b20cd82019-02-25 14:15:02 -050052 if (fBidirectional && negate) {
Brian Osman8b6283f2019-02-14 16:55:21 -050053 result = -result;
54 }
55 return result;
56}
57
58void SkCurveSegment::visitFields(SkFieldVisitor* v) {
Brian Osman34d13312019-02-27 11:19:19 -050059 v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
Brian Osman7c979f52019-02-12 13:27:51 -050060 v->visit("Ranged", fRanged);
Brian Osman8b6283f2019-02-14 16:55:21 -050061 v->visit("Bidirectional", fBidirectional);
Brian Osman7c979f52019-02-12 13:27:51 -050062 v->visit("A0", fMin[0]);
Brian Osman34d13312019-02-27 11:19:19 -050063 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 }
Brian Osman7c979f52019-02-12 13:27:51 -050080}
81
Brian Osmane1cb9ac2019-06-24 10:44:02 -040082float SkCurve::eval(const SkParticleUpdateParams& params, SkParticleState& ps) const {
Brian Osman8b6283f2019-02-14 16:55:21 -050083 SkASSERT(fSegments.count() == fXValues.count() + 1);
84
Brian Osmanbdcdf1a2019-03-04 10:55:22 -050085 float x = fInput.eval(params, ps);
86
Brian Osman8b6283f2019-02-14 16:55:21 -050087 int i = 0;
88 for (; i < fXValues.count(); ++i) {
89 if (x <= fXValues[i]) {
90 break;
91 }
92 }
93
Brian Osmane1cb9ac2019-06-24 10:44:02 -040094 float rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
95 float rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
96 float segmentX = (x - rangeMin) / (rangeMax - rangeMin);
97 if (!sk_float_isfinite(segmentX)) {
Brian Osman112aa2d2019-02-15 10:45:56 -050098 segmentX = rangeMin;
99 }
Brian Osman8b6283f2019-02-14 16:55:21 -0500100 SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
Brian Osman1b20cd82019-02-25 14:15:02 -0500101
102 // Always pull t and negate here, so that the stable generator behaves consistently, even if
103 // our segments use an inconsistent feature-set.
Brian Osmane1cb9ac2019-06-24 10:44:02 -0400104 float t = ps.fRandom.nextF();
Brian Osmanbdcdf1a2019-03-04 10:55:22 -0500105 bool negate = ps.fRandom.nextBool();
Brian Osman1b20cd82019-02-25 14:15:02 -0500106 return fSegments[i].eval(segmentX, t, negate);
Brian Osman7c979f52019-02-12 13:27:51 -0500107}
108
Brian Osman8b6283f2019-02-14 16:55:21 -0500109void SkCurve::visitFields(SkFieldVisitor* v) {
Brian Osmanbdcdf1a2019-03-04 10:55:22 -0500110 v->visit("Input", fInput);
Brian Osman8b6283f2019-02-14 16:55:21 -0500111 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 }
Brian Osman7c979f52019-02-12 13:27:51 -0500122}
Brian Osman112aa2d2019-02-15 10:45:56 -0500123
Brian Osmane1cb9ac2019-06-24 10:44:02 -0400124SkColor4f SkColorCurveSegment::eval(float x, float t) const {
Brian Osman34d13312019-02-27 11:19:19 -0500125 SkColor4f result = eval_segment(fMin, x, fType);
Brian Osman125daa42019-02-20 12:25:20 -0500126 if (fRanged) {
Brian Osmanbdcdf1a2019-03-04 10:55:22 -0500127 result = result + (eval_segment(fMax, x, fType) - result) * t;
Brian Osman125daa42019-02-20 12:25:20 -0500128 }
129 return result;
130}
131
132void SkColorCurveSegment::visitFields(SkFieldVisitor* v) {
Brian Osman34d13312019-02-27 11:19:19 -0500133 v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
Brian Osman125daa42019-02-20 12:25:20 -0500134 v->visit("Ranged", fRanged);
135 v->visit("A0", fMin[0]);
Brian Osman34d13312019-02-27 11:19:19 -0500136 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 }
Brian Osman125daa42019-02-20 12:25:20 -0500153}
154
Brian Osmanbdcdf1a2019-03-04 10:55:22 -0500155SkColor4f SkColorCurve::eval(const SkParticleUpdateParams& params, SkParticleState& ps) const {
Brian Osman125daa42019-02-20 12:25:20 -0500156 SkASSERT(fSegments.count() == fXValues.count() + 1);
157
Brian Osmanbdcdf1a2019-03-04 10:55:22 -0500158 float x = fInput.eval(params, ps);
159
Brian Osman125daa42019-02-20 12:25:20 -0500160 int i = 0;
161 for (; i < fXValues.count(); ++i) {
162 if (x <= fXValues[i]) {
163 break;
164 }
165 }
166
Brian Osmane1cb9ac2019-06-24 10:44:02 -0400167 float rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
168 float rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
169 float segmentX = (x - rangeMin) / (rangeMax - rangeMin);
170 if (!sk_float_isfinite(segmentX)) {
Brian Osman125daa42019-02-20 12:25:20 -0500171 segmentX = rangeMin;
172 }
173 SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
Brian Osmanbdcdf1a2019-03-04 10:55:22 -0500174 return fSegments[i].eval(segmentX, ps.fRandom.nextF());
Brian Osman125daa42019-02-20 12:25:20 -0500175}
176
177void SkColorCurve::visitFields(SkFieldVisitor* v) {
Brian Osmanbdcdf1a2019-03-04 10:55:22 -0500178 v->visit("Input", fInput);
Brian Osman125daa42019-02-20 12:25:20 -0500179 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}