blob: 3dc7819febf690bfad64227bec7a7846007b6479 [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 Osman34d13312019-02-27 11:19:19 -050013constexpr SkFieldVisitor::EnumStringMapping gCurveSegmentTypeMapping[] = {
14 { kConstant_SegmentType, "Constant" },
15 { kLinear_SegmentType, "Linear" },
16 { kCubic_SegmentType, "Cubic" },
17};
Brian Osman7c979f52019-02-12 13:27:51 -050018
Brian Osman125daa42019-02-20 12:25:20 -050019static SkColor4f operator+(SkColor4f c1, SkColor4f c2) {
20 return { c1.fR + c2.fR, c1.fG + c2.fG, c1.fB + c2.fB, c1.fA + c2.fA };
21}
22
Brian Osman34d13312019-02-27 11:19:19 -050023static SkColor4f operator-(SkColor4f c1, SkColor4f c2) {
24 return { c1.fR - c2.fR, c1.fG - c2.fG, c1.fB - c2.fB, c1.fA - c2.fA };
25}
26
27template <typename T>
28static T eval_cubic(const T* pts, SkScalar x) {
Brian Osman125daa42019-02-20 12:25:20 -050029 SkScalar ix = (1 - x);
30 return pts[0]*(ix*ix*ix) + pts[1]*(3*ix*ix*x) + pts[2]*(3*ix*x*x) + pts[3]*(x*x*x);
31}
32
Brian Osman34d13312019-02-27 11:19:19 -050033template <typename T>
34static T eval_segment(const T* pts, SkScalar x, int type) {
35 switch (type) {
36 case kLinear_SegmentType:
37 return pts[0] + (pts[3] - pts[0]) * x;
38 case kCubic_SegmentType:
39 return eval_cubic(pts, x);
40 case kConstant_SegmentType:
41 default:
42 return pts[0];
43 }
44}
45
Brian Osman1b20cd82019-02-25 14:15:02 -050046SkScalar SkCurveSegment::eval(SkScalar x, SkScalar t, bool negate) const {
Brian Osman34d13312019-02-27 11:19:19 -050047 SkScalar result = eval_segment(fMin, x, fType);
Brian Osman8b6283f2019-02-14 16:55:21 -050048 if (fRanged) {
Brian Osman34d13312019-02-27 11:19:19 -050049 result += (eval_segment(fMax, x, fType) - result) * t;
Brian Osman8b6283f2019-02-14 16:55:21 -050050 }
Brian Osman1b20cd82019-02-25 14:15:02 -050051 if (fBidirectional && negate) {
Brian Osman8b6283f2019-02-14 16:55:21 -050052 result = -result;
53 }
54 return result;
55}
56
57void SkCurveSegment::visitFields(SkFieldVisitor* v) {
Brian Osman34d13312019-02-27 11:19:19 -050058 v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
Brian Osman7c979f52019-02-12 13:27:51 -050059 v->visit("Ranged", fRanged);
Brian Osman8b6283f2019-02-14 16:55:21 -050060 v->visit("Bidirectional", fBidirectional);
Brian Osman7c979f52019-02-12 13:27:51 -050061 v->visit("A0", fMin[0]);
Brian Osman34d13312019-02-27 11:19:19 -050062 if (fType == kCubic_SegmentType) {
63 v->visit("B0", fMin[1]);
64 v->visit("C0", fMin[2]);
65 }
66 if (fType != kConstant_SegmentType) {
67 v->visit("D0", fMin[3]);
68 }
69 if (fRanged) {
70 v->visit("A1", fMax[0]);
71 if (fType == kCubic_SegmentType) {
72 v->visit("B1", fMax[1]);
73 v->visit("C1", fMax[2]);
74 }
75 if (fType != kConstant_SegmentType) {
76 v->visit("D1", fMax[3]);
77 }
78 }
Brian Osman7c979f52019-02-12 13:27:51 -050079}
80
Brian Osman8b6283f2019-02-14 16:55:21 -050081SkScalar SkCurve::eval(SkScalar x, SkRandom& random) const {
82 SkASSERT(fSegments.count() == fXValues.count() + 1);
83
84 int i = 0;
85 for (; i < fXValues.count(); ++i) {
86 if (x <= fXValues[i]) {
87 break;
88 }
89 }
90
91 SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
92 SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
93 SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
Brian Osman112aa2d2019-02-15 10:45:56 -050094 if (!SkScalarIsFinite(segmentX)) {
95 segmentX = rangeMin;
96 }
Brian Osman8b6283f2019-02-14 16:55:21 -050097 SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
Brian Osman1b20cd82019-02-25 14:15:02 -050098
99 // Always pull t and negate here, so that the stable generator behaves consistently, even if
100 // our segments use an inconsistent feature-set.
101 SkScalar t = random.nextF();
102 bool negate = random.nextBool();
103 return fSegments[i].eval(segmentX, t, negate);
Brian Osman7c979f52019-02-12 13:27:51 -0500104}
105
Brian Osman8b6283f2019-02-14 16:55:21 -0500106void SkCurve::visitFields(SkFieldVisitor* v) {
107 v->visit("XValues", fXValues);
108 v->visit("Segments", fSegments);
109
110 // Validate and fixup
111 if (fSegments.empty()) {
112 fSegments.push_back().setConstant(0.0f);
113 }
114 fXValues.resize_back(fSegments.count() - 1);
115 for (int i = 0; i < fXValues.count(); ++i) {
116 fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
117 }
Brian Osman7c979f52019-02-12 13:27:51 -0500118}
Brian Osman112aa2d2019-02-15 10:45:56 -0500119
Brian Osman125daa42019-02-20 12:25:20 -0500120SkColor4f SkColorCurveSegment::eval(SkScalar x, SkRandom& random) const {
Brian Osman34d13312019-02-27 11:19:19 -0500121 SkColor4f result = eval_segment(fMin, x, fType);
Brian Osman125daa42019-02-20 12:25:20 -0500122 if (fRanged) {
Brian Osman34d13312019-02-27 11:19:19 -0500123 result = result + (eval_segment(fMax, x, fType) - result) * random.nextF();
Brian Osman125daa42019-02-20 12:25:20 -0500124 }
125 return result;
126}
127
128void SkColorCurveSegment::visitFields(SkFieldVisitor* v) {
Brian Osman34d13312019-02-27 11:19:19 -0500129 v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping));
Brian Osman125daa42019-02-20 12:25:20 -0500130 v->visit("Ranged", fRanged);
131 v->visit("A0", fMin[0]);
Brian Osman34d13312019-02-27 11:19:19 -0500132 if (fType == kCubic_SegmentType) {
133 v->visit("B0", fMin[1]);
134 v->visit("C0", fMin[2]);
135 }
136 if (fType != kConstant_SegmentType) {
137 v->visit("D0", fMin[3]);
138 }
139 if (fRanged) {
140 v->visit("A1", fMax[0]);
141 if (fType == kCubic_SegmentType) {
142 v->visit("B1", fMax[1]);
143 v->visit("C1", fMax[2]);
144 }
145 if (fType != kConstant_SegmentType) {
146 v->visit("D1", fMax[3]);
147 }
148 }
Brian Osman125daa42019-02-20 12:25:20 -0500149}
150
151SkColor4f SkColorCurve::eval(SkScalar x, SkRandom& random) const {
152 SkASSERT(fSegments.count() == fXValues.count() + 1);
153
154 int i = 0;
155 for (; i < fXValues.count(); ++i) {
156 if (x <= fXValues[i]) {
157 break;
158 }
159 }
160
161 SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
162 SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
163 SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
164 if (!SkScalarIsFinite(segmentX)) {
165 segmentX = rangeMin;
166 }
167 SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
168 return fSegments[i].eval(segmentX, random);
169}
170
171void SkColorCurve::visitFields(SkFieldVisitor* v) {
172 v->visit("XValues", fXValues);
173 v->visit("Segments", fSegments);
174
175 // Validate and fixup
176 if (fSegments.empty()) {
177 fSegments.push_back().setConstant(SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f });
178 }
179 fXValues.resize_back(fSegments.count() - 1);
180 for (int i = 0; i < fXValues.count(); ++i) {
181 fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
182 }
183}