blob: 1f14a79dd12d2cff88f499acd9e05f1be00ab6aa [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
8#include "SkCurve.h"
9
10#include "SkRandom.h"
11#include "SkReflected.h"
12
Brian Osman8b6283f2019-02-14 16:55:21 -050013static SkScalar eval_cubic(const SkScalar* pts, SkScalar x) {
14 SkScalar ix = (1 - x);
15 return pts[0]*ix*ix*ix + pts[1]*3*ix*ix*x + pts[2]*3*ix*x*x + pts[3]*x*x*x;
Brian Osman7c979f52019-02-12 13:27:51 -050016}
17
Brian Osman125daa42019-02-20 12:25:20 -050018static 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
22static SkColor4f eval_cubic(const SkColor4f* pts, SkScalar x) {
23 SkScalar ix = (1 - x);
24 return pts[0]*(ix*ix*ix) + pts[1]*(3*ix*ix*x) + pts[2]*(3*ix*x*x) + pts[3]*(x*x*x);
25}
26
Brian Osman1b20cd82019-02-25 14:15:02 -050027SkScalar SkCurveSegment::eval(SkScalar x, SkScalar t, bool negate) const {
Brian Osman8b6283f2019-02-14 16:55:21 -050028 SkScalar result = fConstant ? fMin[0] : eval_cubic(fMin, x);
29 if (fRanged) {
Brian Osman1b20cd82019-02-25 14:15:02 -050030 result += ((fConstant ? fMax[0] : eval_cubic(fMax, x)) - result) * t;
Brian Osman8b6283f2019-02-14 16:55:21 -050031 }
Brian Osman1b20cd82019-02-25 14:15:02 -050032 if (fBidirectional && negate) {
Brian Osman8b6283f2019-02-14 16:55:21 -050033 result = -result;
34 }
35 return result;
36}
37
38void SkCurveSegment::visitFields(SkFieldVisitor* v) {
39 v->visit("Constant", fConstant);
Brian Osman7c979f52019-02-12 13:27:51 -050040 v->visit("Ranged", fRanged);
Brian Osman8b6283f2019-02-14 16:55:21 -050041 v->visit("Bidirectional", fBidirectional);
Brian Osman7c979f52019-02-12 13:27:51 -050042 v->visit("A0", fMin[0]);
43 v->visit("B0", fMin[1]);
44 v->visit("C0", fMin[2]);
45 v->visit("D0", fMin[3]);
46 v->visit("A1", fMax[0]);
47 v->visit("B1", fMax[1]);
48 v->visit("C1", fMax[2]);
49 v->visit("D1", fMax[3]);
50}
51
Brian Osman8b6283f2019-02-14 16:55:21 -050052SkScalar SkCurve::eval(SkScalar x, SkRandom& random) const {
53 SkASSERT(fSegments.count() == fXValues.count() + 1);
54
55 int i = 0;
56 for (; i < fXValues.count(); ++i) {
57 if (x <= fXValues[i]) {
58 break;
59 }
60 }
61
62 SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
63 SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
64 SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
Brian Osman112aa2d2019-02-15 10:45:56 -050065 if (!SkScalarIsFinite(segmentX)) {
66 segmentX = rangeMin;
67 }
Brian Osman8b6283f2019-02-14 16:55:21 -050068 SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
Brian Osman1b20cd82019-02-25 14:15:02 -050069
70 // Always pull t and negate here, so that the stable generator behaves consistently, even if
71 // our segments use an inconsistent feature-set.
72 SkScalar t = random.nextF();
73 bool negate = random.nextBool();
74 return fSegments[i].eval(segmentX, t, negate);
Brian Osman7c979f52019-02-12 13:27:51 -050075}
76
Brian Osman8b6283f2019-02-14 16:55:21 -050077void SkCurve::visitFields(SkFieldVisitor* v) {
78 v->visit("XValues", fXValues);
79 v->visit("Segments", fSegments);
80
81 // Validate and fixup
82 if (fSegments.empty()) {
83 fSegments.push_back().setConstant(0.0f);
84 }
85 fXValues.resize_back(fSegments.count() - 1);
86 for (int i = 0; i < fXValues.count(); ++i) {
87 fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
88 }
Brian Osman7c979f52019-02-12 13:27:51 -050089}
Brian Osman112aa2d2019-02-15 10:45:56 -050090
Brian Osmane12e4992019-02-19 15:39:18 -050091// TODO: This implementation is extremely conservative, because it uses the position of the control
92// points as the actual range. The curve typically doesn't reach that far. Evaluating the curve at
93// each of [0, 1/3, 2/3, 1] would be tighter, but can be too tight in some cases.
Brian Osman112aa2d2019-02-15 10:45:56 -050094void SkCurve::getExtents(SkScalar extents[2]) const {
95 extents[0] = INFINITY;
96 extents[1] = -INFINITY;
97 auto extend = [=](SkScalar y) {
98 extents[0] = SkTMin(extents[0], y);
99 extents[1] = SkTMax(extents[1], y);
100 };
101 for (const auto& segment : fSegments) {
102 for (int i = 0; i < (segment.fConstant ? 1 : 4); ++i) {
103 extend(segment.fMin[i]);
104 if (segment.fRanged) {
105 extend(segment.fMax[i]);
106 }
107 }
108 }
109}
Brian Osman125daa42019-02-20 12:25:20 -0500110
111SkColor4f SkColorCurveSegment::eval(SkScalar x, SkRandom& random) const {
112 SkColor4f result = fConstant ? fMin[0] : eval_cubic(fMin, x);
113 if (fRanged) {
114 result = result +
115 ((fConstant ? fMax[0] : eval_cubic(fMax, x)) + (result * -1)) * random.nextF();
116 }
117 return result;
118}
119
120void SkColorCurveSegment::visitFields(SkFieldVisitor* v) {
121 v->visit("Constant", fConstant);
122 v->visit("Ranged", fRanged);
123 v->visit("A0", fMin[0]);
124 v->visit("B0", fMin[1]);
125 v->visit("C0", fMin[2]);
126 v->visit("D0", fMin[3]);
127 v->visit("A1", fMax[0]);
128 v->visit("B1", fMax[1]);
129 v->visit("C1", fMax[2]);
130 v->visit("D1", fMax[3]);
131}
132
133SkColor4f SkColorCurve::eval(SkScalar x, SkRandom& random) const {
134 SkASSERT(fSegments.count() == fXValues.count() + 1);
135
136 int i = 0;
137 for (; i < fXValues.count(); ++i) {
138 if (x <= fXValues[i]) {
139 break;
140 }
141 }
142
143 SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
144 SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
145 SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
146 if (!SkScalarIsFinite(segmentX)) {
147 segmentX = rangeMin;
148 }
149 SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
150 return fSegments[i].eval(segmentX, random);
151}
152
153void SkColorCurve::visitFields(SkFieldVisitor* v) {
154 v->visit("XValues", fXValues);
155 v->visit("Segments", fSegments);
156
157 // Validate and fixup
158 if (fSegments.empty()) {
159 fSegments.push_back().setConstant(SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f });
160 }
161 fXValues.resize_back(fSegments.count() - 1);
162 for (int i = 0; i < fXValues.count(); ++i) {
163 fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
164 }
165}