blob: dbd754b52ee7983b05311bcc5daddf86d3ab758c [file] [log] [blame]
/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "modules/particles/include/SkCurve.h"
#include "include/utils/SkRandom.h"
#include "modules/particles/include/SkParticleData.h"
#include "modules/particles/include/SkReflected.h"
constexpr SkFieldVisitor::EnumStringMapping gCurveSegmentTypeMapping[] = {
{ kConstant_SegmentType, "Constant" },
{ kLinear_SegmentType, "Linear" },
{ kCubic_SegmentType, "Cubic" },
};
static SkColor4f operator+(SkColor4f c1, SkColor4f c2) {
return { c1.fR + c2.fR, c1.fG + c2.fG, c1.fB + c2.fB, c1.fA + c2.fA };
}
static SkColor4f operator-(SkColor4f c1, SkColor4f c2) {
return { c1.fR - c2.fR, c1.fG - c2.fG, c1.fB - c2.fB, c1.fA - c2.fA };
}
template <typename T>
static T eval_cubic(const T* pts, SkScalar x) {
SkScalar ix = (1 - x);
return pts[0]*(ix*ix*ix) + pts[1]*(3*ix*ix*x) + pts[2]*(3*ix*x*x) + pts[3]*(x*x*x);
}
template <typename T>
static T eval_segment(const T* pts, SkScalar x, int type) {
switch (type) {
case kLinear_SegmentType:
return pts[0] + (pts[3] - pts[0]) * x;
case kCubic_SegmentType:
return eval_cubic(pts, x);
case kConstant_SegmentType:
default:
return pts[0];
}
}
SkScalar SkCurveSegment::eval(SkScalar x, SkScalar t, bool negate) const {
SkScalar result = eval_segment(fMin, x, fType);
if (fRanged) {
result += (eval_segment(fMax, x, fType) - result) * t;
}
if (fBidirectional && negate) {
result = -result;
}
return result;
}
void SkCurveSegment::visitFields(SkFieldVisitor* v) {
v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
v->visit("Ranged", fRanged);
v->visit("Bidirectional", fBidirectional);
v->visit("A0", fMin[0]);
if (fType == kCubic_SegmentType) {
v->visit("B0", fMin[1]);
v->visit("C0", fMin[2]);
}
if (fType != kConstant_SegmentType) {
v->visit("D0", fMin[3]);
}
if (fRanged) {
v->visit("A1", fMax[0]);
if (fType == kCubic_SegmentType) {
v->visit("B1", fMax[1]);
v->visit("C1", fMax[2]);
}
if (fType != kConstant_SegmentType) {
v->visit("D1", fMax[3]);
}
}
}
SkScalar SkCurve::eval(const SkParticleUpdateParams& params, SkParticleState& ps) const {
SkASSERT(fSegments.count() == fXValues.count() + 1);
float x = fInput.eval(params, ps);
int i = 0;
for (; i < fXValues.count(); ++i) {
if (x <= fXValues[i]) {
break;
}
}
SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
if (!SkScalarIsFinite(segmentX)) {
segmentX = rangeMin;
}
SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
// Always pull t and negate here, so that the stable generator behaves consistently, even if
// our segments use an inconsistent feature-set.
SkScalar t = ps.fRandom.nextF();
bool negate = ps.fRandom.nextBool();
return fSegments[i].eval(segmentX, t, negate);
}
void SkCurve::visitFields(SkFieldVisitor* v) {
v->visit("Input", fInput);
v->visit("XValues", fXValues);
v->visit("Segments", fSegments);
// Validate and fixup
if (fSegments.empty()) {
fSegments.push_back().setConstant(0.0f);
}
fXValues.resize_back(fSegments.count() - 1);
for (int i = 0; i < fXValues.count(); ++i) {
fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
}
}
SkColor4f SkColorCurveSegment::eval(SkScalar x, SkScalar t) const {
SkColor4f result = eval_segment(fMin, x, fType);
if (fRanged) {
result = result + (eval_segment(fMax, x, fType) - result) * t;
}
return result;
}
void SkColorCurveSegment::visitFields(SkFieldVisitor* v) {
v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
v->visit("Ranged", fRanged);
v->visit("A0", fMin[0]);
if (fType == kCubic_SegmentType) {
v->visit("B0", fMin[1]);
v->visit("C0", fMin[2]);
}
if (fType != kConstant_SegmentType) {
v->visit("D0", fMin[3]);
}
if (fRanged) {
v->visit("A1", fMax[0]);
if (fType == kCubic_SegmentType) {
v->visit("B1", fMax[1]);
v->visit("C1", fMax[2]);
}
if (fType != kConstant_SegmentType) {
v->visit("D1", fMax[3]);
}
}
}
SkColor4f SkColorCurve::eval(const SkParticleUpdateParams& params, SkParticleState& ps) const {
SkASSERT(fSegments.count() == fXValues.count() + 1);
float x = fInput.eval(params, ps);
int i = 0;
for (; i < fXValues.count(); ++i) {
if (x <= fXValues[i]) {
break;
}
}
SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
if (!SkScalarIsFinite(segmentX)) {
segmentX = rangeMin;
}
SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
return fSegments[i].eval(segmentX, ps.fRandom.nextF());
}
void SkColorCurve::visitFields(SkFieldVisitor* v) {
v->visit("Input", fInput);
v->visit("XValues", fXValues);
v->visit("Segments", fSegments);
// Validate and fixup
if (fSegments.empty()) {
fSegments.push_back().setConstant(SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f });
}
fXValues.resize_back(fSegments.count() - 1);
for (int i = 0; i < fXValues.count(); ++i) {
fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
}
}