blob: d33fbac8682836ee11bcd15af181e940000629e6 [file] [log] [blame]
rileya@google.com589708b2012-07-26 20:04:23 +00001/*
2 * Copyright 2012 Google Inc.
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 "SkTwoPointConicalGradient.h"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00009
Florin Malitaa66ef2d2017-06-28 10:02:40 -040010#include "SkRasterPipeline.h"
11#include "../../jumper/SkJumper.h"
12
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000013struct TwoPtRadialContext {
14 const TwoPtRadial& fRec;
15 float fRelX, fRelY;
16 const float fIncX, fIncY;
17 float fB;
18 const float fDB;
19
20 TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
21 SkScalar dfx, SkScalar dfy);
22 SkFixed nextT();
23};
24
rileya@google.com589708b2012-07-26 20:04:23 +000025static int valid_divide(float numer, float denom, float* ratio) {
26 SkASSERT(ratio);
27 if (0 == denom) {
28 return 0;
29 }
30 *ratio = numer / denom;
31 return 1;
32}
33
34// Return the number of distinct real roots, and write them into roots[] in
35// ascending order
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000036static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) {
rileya@google.com589708b2012-07-26 20:04:23 +000037 SkASSERT(roots);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000038
rileya@google.com589708b2012-07-26 20:04:23 +000039 if (A == 0) {
40 return valid_divide(-C, B, roots);
41 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000042
rileya@google.com589708b2012-07-26 20:04:23 +000043 float R = B*B - 4*A*C;
44 if (R < 0) {
45 return 0;
46 }
47 R = sk_float_sqrt(R);
48
49#if 1
50 float Q = B;
51 if (Q < 0) {
52 Q -= R;
53 } else {
54 Q += R;
55 }
56#else
57 // on 10.6 this was much slower than the above branch :(
58 float Q = B + copysignf(R, B);
59#endif
60 Q *= -0.5f;
61 if (0 == Q) {
62 roots[0] = 0;
63 return 1;
64 }
65
66 float r0 = Q / A;
67 float r1 = C / Q;
68 roots[0] = r0 < r1 ? r0 : r1;
69 roots[1] = r0 > r1 ? r0 : r1;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000070 if (descendingOrder) {
71 SkTSwap(roots[0], roots[1]);
72 }
rileya@google.com589708b2012-07-26 20:04:23 +000073 return 2;
74}
75
76static float lerp(float x, float dx, float t) {
77 return x + t * dx;
78}
79
80static float sqr(float x) { return x * x; }
81
82void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000083 const SkPoint& center1, SkScalar rad1,
84 bool flipped) {
rileya@google.com589708b2012-07-26 20:04:23 +000085 fCenterX = SkScalarToFloat(center0.fX);
86 fCenterY = SkScalarToFloat(center0.fY);
87 fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
88 fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
89 fRadius = SkScalarToFloat(rad0);
90 fDRadius = SkScalarToFloat(rad1) - fRadius;
91
92 fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
93 fRadius2 = sqr(fRadius);
94 fRDR = fRadius * fDRadius;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000095
96 fFlipped = flipped;
rileya@google.com589708b2012-07-26 20:04:23 +000097}
98
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000099TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
100 SkScalar dfx, SkScalar dfy)
101 : fRec(rec)
102 , fRelX(SkScalarToFloat(fx) - rec.fCenterX)
103 , fRelY(SkScalarToFloat(fy) - rec.fCenterY)
104 , fIncX(SkScalarToFloat(dfx))
105 , fIncY(SkScalarToFloat(dfy))
106 , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR))
107 , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {}
rileya@google.com589708b2012-07-26 20:04:23 +0000108
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000109SkFixed TwoPtRadialContext::nextT() {
rileya@google.com589708b2012-07-26 20:04:23 +0000110 float roots[2];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000111
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000112 float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
113 int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped);
rileya@google.com589708b2012-07-26 20:04:23 +0000114
115 fRelX += fIncX;
116 fRelY += fIncY;
117 fB += fDB;
118
119 if (0 == countRoots) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000120 return TwoPtRadial::kDontDrawT;
rileya@google.com589708b2012-07-26 20:04:23 +0000121 }
122
123 // Prefer the bigger t value if both give a radius(t) > 0
124 // find_quad_roots returns the values sorted, so we start with the last
125 float t = roots[countRoots - 1];
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000126 float r = lerp(fRec.fRadius, fRec.fDRadius, t);
fmalita5edf82e2016-03-03 06:41:54 -0800127 if (r < 0) {
rileya@google.com589708b2012-07-26 20:04:23 +0000128 t = roots[0]; // might be the same as roots[countRoots-1]
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000129 r = lerp(fRec.fRadius, fRec.fDRadius, t);
fmalita5edf82e2016-03-03 06:41:54 -0800130 if (r < 0) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000131 return TwoPtRadial::kDontDrawT;
rileya@google.com589708b2012-07-26 20:04:23 +0000132 }
133 }
fmalitae7365062016-04-07 19:27:45 -0700134 return SkFloatToFixed(t);
rileya@google.com589708b2012-07-26 20:04:23 +0000135}
136
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000137typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC,
reed@google.com60040292013-02-04 18:21:23 +0000138 const SkPMColor* cache, int toggle, int count);
rileya@google.com589708b2012-07-26 20:04:23 +0000139
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000140static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
reed@google.com60040292013-02-04 18:21:23 +0000141 const SkPMColor* SK_RESTRICT cache, int toggle,
142 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000143 for (; count > 0; --count) {
144 SkFixed t = rec->nextT();
145 if (TwoPtRadial::DontDrawT(t)) {
146 *dstC++ = 0;
147 } else {
148 SkFixed index = SkClampMax(t, 0xFFFF);
149 SkASSERT(index <= 0xFFFF);
reed@google.com60040292013-02-04 18:21:23 +0000150 *dstC++ = cache[toggle +
151 (index >> SkGradientShaderBase::kCache32Shift)];
rileya@google.com589708b2012-07-26 20:04:23 +0000152 }
reed@google.com60040292013-02-04 18:21:23 +0000153 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000154 }
155}
156
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000157static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
reed@google.com60040292013-02-04 18:21:23 +0000158 const SkPMColor* SK_RESTRICT cache, int toggle,
159 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000160 for (; count > 0; --count) {
161 SkFixed t = rec->nextT();
162 if (TwoPtRadial::DontDrawT(t)) {
163 *dstC++ = 0;
164 } else {
165 SkFixed index = repeat_tileproc(t);
166 SkASSERT(index <= 0xFFFF);
reed@google.com60040292013-02-04 18:21:23 +0000167 *dstC++ = cache[toggle +
168 (index >> SkGradientShaderBase::kCache32Shift)];
rileya@google.com589708b2012-07-26 20:04:23 +0000169 }
reed@google.com60040292013-02-04 18:21:23 +0000170 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000171 }
172}
173
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000174static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
reed@google.com60040292013-02-04 18:21:23 +0000175 const SkPMColor* SK_RESTRICT cache, int toggle,
176 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000177 for (; count > 0; --count) {
178 SkFixed t = rec->nextT();
179 if (TwoPtRadial::DontDrawT(t)) {
180 *dstC++ = 0;
181 } else {
182 SkFixed index = mirror_tileproc(t);
183 SkASSERT(index <= 0xFFFF);
reed@google.com60040292013-02-04 18:21:23 +0000184 *dstC++ = cache[toggle +
185 (index >> SkGradientShaderBase::kCache32Shift)];
rileya@google.com589708b2012-07-26 20:04:23 +0000186 }
reed@google.com60040292013-02-04 18:21:23 +0000187 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000188 }
189}
190
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000191/////////////////////////////////////////////////////////////////////
192
rileya@google.com589708b2012-07-26 20:04:23 +0000193SkTwoPointConicalGradient::SkTwoPointConicalGradient(
reed@google.com3d3a8602013-05-24 14:58:44 +0000194 const SkPoint& start, SkScalar startRadius,
195 const SkPoint& end, SkScalar endRadius,
reedaddf2ed2014-08-11 08:28:24 -0700196 bool flippedGrad, const Descriptor& desc)
mtkleincc695fe2014-12-10 10:29:19 -0800197 : SkGradientShaderBase(desc, SkMatrix::I())
reedaddf2ed2014-08-11 08:28:24 -0700198 , fCenter1(start)
199 , fCenter2(end)
200 , fRadius1(startRadius)
201 , fRadius2(endRadius)
202 , fFlippedGrad(flippedGrad)
203{
rileya@google.com589708b2012-07-26 20:04:23 +0000204 // this is degenerate, and should be caught by our caller
205 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
mtkleincc695fe2014-12-10 10:29:19 -0800206 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000207}
208
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +0000209bool SkTwoPointConicalGradient::isOpaque() const {
robertphillips@google.comcb6d97c2013-07-09 13:50:09 +0000210 // Because areas outside the cone are left untouched, we cannot treat the
211 // shader as opaque even if the gradient itself is opaque.
212 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
213 return false;
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +0000214}
215
Florin Malita4aed1382017-05-25 10:38:07 -0400216SkShaderBase::Context* SkTwoPointConicalGradient::onMakeContext(
Herb Derby83e939b2017-02-07 14:25:11 -0500217 const ContextRec& rec, SkArenaAlloc* alloc) const {
218 return CheckedMakeContext<TwoPointConicalGradientContext>(alloc, *this, rec);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000219}
220
221SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000222 const SkTwoPointConicalGradient& shader, const ContextRec& rec)
223 : INHERITED(shader, rec)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000224{
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000225 // in general, we might discard based on computed-radius, so clear
226 // this flag (todo: sometimes we can detect that we never discard...)
227 fFlags &= ~kOpaqueAlpha_Flag;
228}
229
230void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
231 int x, int y, SkPMColor* dstCParam, int count) {
232 const SkTwoPointConicalGradient& twoPointConicalGradient =
233 static_cast<const SkTwoPointConicalGradient&>(fShader);
234
reed@google.com60040292013-02-04 18:21:23 +0000235 int toggle = init_dither_toggle(x, y);
reed@google.com60040292013-02-04 18:21:23 +0000236
rileya@google.com589708b2012-07-26 20:04:23 +0000237 SkASSERT(count > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000238
rileya@google.com589708b2012-07-26 20:04:23 +0000239 SkPMColor* SK_RESTRICT dstC = dstCParam;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000240
rileya@google.com589708b2012-07-26 20:04:23 +0000241 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
bsalomon@google.com100abf42012-09-05 17:40:04 +0000242
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000243 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
rileya@google.com589708b2012-07-26 20:04:23 +0000244
reed@google.com60040292013-02-04 18:21:23 +0000245 TwoPointConicalProc shadeProc = twopoint_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000246 if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000247 shadeProc = twopoint_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000248 } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000249 shadeProc = twopoint_mirror;
250 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000251 SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000252 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000253
rileya@google.com589708b2012-07-26 20:04:23 +0000254 if (fDstToIndexClass != kPerspective_MatrixClass) {
255 SkPoint srcPt;
256 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
257 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
258 SkScalar dx, fx = srcPt.fX;
259 SkScalar dy, fy = srcPt.fY;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000260
rileya@google.com589708b2012-07-26 20:04:23 +0000261 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
benjaminwagner8e175562016-02-16 10:09:40 -0800262 const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
263 dx = step.fX;
264 dy = step.fY;
rileya@google.com589708b2012-07-26 20:04:23 +0000265 } else {
266 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
267 dx = fDstToIndex.getScaleX();
268 dy = fDstToIndex.getSkewY();
269 }
270
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000271 TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
272 (*shadeProc)(&rec, dstC, cache, toggle, count);
rileya@google.com589708b2012-07-26 20:04:23 +0000273 } else { // perspective case
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000274 SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
275 SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
rileya@google.com589708b2012-07-26 20:04:23 +0000276 for (; count > 0; --count) {
277 SkPoint srcPt;
278 dstProc(fDstToIndex, dstX, dstY, &srcPt);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000279 TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
280 (*shadeProc)(&rec, dstC, cache, toggle, 1);
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000281
282 dstX += SK_Scalar1;
reed@google.com60040292013-02-04 18:21:23 +0000283 toggle = next_dither_toggle(toggle);
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000284 dstC += 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000285 }
286 }
287}
288
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000289// Returns the original non-sorted version of the gradient
rileya@google.com589708b2012-07-26 20:04:23 +0000290SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
291 GradientInfo* info) const {
292 if (info) {
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000293 commonAsAGradient(info, fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000294 info->fPoint[0] = fCenter1;
295 info->fPoint[1] = fCenter2;
296 info->fRadius[0] = fRadius1;
297 info->fRadius[1] = fRadius2;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000298 if (fFlippedGrad) {
299 SkTSwap(info->fPoint[0], info->fPoint[1]);
300 SkTSwap(info->fRadius[0], info->fRadius[1]);
301 }
rileya@google.com589708b2012-07-26 20:04:23 +0000302 }
303 return kConical_GradientType;
304}
305
reed60c9b582016-04-03 09:11:13 -0700306sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -0700307 DescriptorScope desc;
308 if (!desc.unflatten(buffer)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700309 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700310 }
311 SkPoint c1 = buffer.readPoint();
312 SkPoint c2 = buffer.readPoint();
313 SkScalar r1 = buffer.readScalar();
314 SkScalar r2 = buffer.readScalar();
315
316 if (buffer.readBool()) { // flipped
317 SkTSwap(c1, c2);
318 SkTSwap(r1, r2);
319
brianosmane25d71c2016-09-28 11:27:28 -0700320 SkColor4f* colors = desc.mutableColors();
reed9fa60da2014-08-21 07:59:51 -0700321 SkScalar* pos = desc.mutablePos();
322 const int last = desc.fCount - 1;
323 const int half = desc.fCount >> 1;
324 for (int i = 0; i < half; ++i) {
325 SkTSwap(colors[i], colors[last - i]);
326 if (pos) {
327 SkScalar tmp = pos[i];
328 pos[i] = SK_Scalar1 - pos[last - i];
329 pos[last - i] = SK_Scalar1 - tmp;
330 }
331 }
332 if (pos) {
333 if (desc.fCount & 1) {
334 pos[half] = SK_Scalar1 - pos[half];
335 }
336 }
337 }
338
brianosmane25d71c2016-09-28 11:27:28 -0700339 return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
340 std::move(desc.fColorSpace), desc.fPos,
reed8a21c9f2016-03-08 18:50:00 -0800341 desc.fCount, desc.fTileMode, desc.fGradFlags,
reed60c9b582016-04-03 09:11:13 -0700342 desc.fLocalMatrix);
reed9fa60da2014-08-21 07:59:51 -0700343}
344
345void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000346 this->INHERITED::flatten(buffer);
347 buffer.writePoint(fCenter1);
348 buffer.writePoint(fCenter2);
349 buffer.writeScalar(fRadius1);
350 buffer.writeScalar(fRadius2);
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000351 buffer.writeBool(fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000352}
353
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000354#if SK_SUPPORT_GPU
355
dandov9de5b512014-06-10 14:38:28 -0700356#include "SkGr.h"
brianosman9557c272016-09-15 06:59:15 -0700357#include "SkTwoPointConicalGradient_gpu.h"
dandov9de5b512014-06-10 14:38:28 -0700358
bungeman06ca8ec2016-06-09 08:01:03 -0700359sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
brianosman1638c0d2016-07-25 05:12:53 -0700360 const AsFPArgs& args) const {
brianosman839345d2016-07-22 11:04:53 -0700361 SkASSERT(args.fContext);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000362 SkASSERT(fPtsToUnit.isIdentity());
brianosmanb9c51372016-09-15 11:09:45 -0700363 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
364 args.fDstColorSpace);
brianosman9557c272016-09-15 06:59:15 -0700365 sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
brianosmanb9c51372016-09-15 11:09:45 -0700366 GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
367 std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
Brian Salomon6af27012017-06-09 08:21:42 -0400368 if (!inner) {
369 return nullptr;
370 }
bungeman06ca8ec2016-06-09 08:01:03 -0700371 return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000372}
373
twiz@google.coma5e65ec2012-08-02 15:15:16 +0000374#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000375
Matt Sarett6cc6ae752017-04-18 18:29:12 -0400376sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
377 SkSTArray<8, SkColor> origColorsStorage(fColorCount);
378 SkSTArray<8, SkScalar> origPosStorage(fColorCount);
379 SkSTArray<8, SkColor> xformedColorsStorage(fColorCount);
380 SkColor* origColors = origColorsStorage.begin();
381 SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr;
382 SkColor* xformedColors = xformedColorsStorage.begin();
383
384 // Flip if necessary
385 SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1;
386 SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2;
387 SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1;
388 SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2;
389 for (int i = 0; i < fColorCount; i++) {
390 origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i];
391 if (origPos) {
392 origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i];
393 }
394 }
395
396 xformer->apply(xformedColors, origColors, fColorCount);
397 return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors,
398 origPos, fColorCount, fTileMode, fGradFlags,
399 &this->getLocalMatrix());
400}
401
402
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000403#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000404void SkTwoPointConicalGradient::toString(SkString* str) const {
405 str->append("SkTwoPointConicalGradient: (");
406
407 str->append("center1: (");
408 str->appendScalar(fCenter1.fX);
409 str->append(", ");
410 str->appendScalar(fCenter1.fY);
411 str->append(") radius1: ");
412 str->appendScalar(fRadius1);
413 str->append(" ");
414
415 str->append("center2: (");
416 str->appendScalar(fCenter2.fX);
417 str->append(", ");
418 str->appendScalar(fCenter2.fY);
419 str->append(") radius2: ");
420 str->appendScalar(fRadius2);
421 str->append(" ");
422
423 this->INHERITED::toString(str);
424
425 str->append(")");
426}
427#endif
Florin Malita0bb04112017-06-27 14:35:50 -0400428
429bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
430 SkMatrix* matrix,
431 SkRasterPipeline* p) const {
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400432 const auto dCenter = (fCenter1 - fCenter2).length();
433 const auto dRadius = fRadius2 - fRadius1;
434 SkASSERT(dRadius >= 0);
435
Florin Malita0bb04112017-06-27 14:35:50 -0400436 // When the two circles are concentric, we can pretend we're radial (with a tiny *twist).
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400437 if (SkScalarNearlyZero(dCenter)) {
Florin Malita0bb04112017-06-27 14:35:50 -0400438 matrix->postTranslate(-fCenter1.fX, -fCenter1.fY);
439 matrix->postScale(1 / fRadius2, 1 / fRadius2);
440 p->append(SkRasterPipeline::xy_to_radius);
441
442 // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400443 auto scale = fRadius2 / dRadius;
444 auto bias = -fRadius1 / dRadius;
Florin Malita0bb04112017-06-27 14:35:50 -0400445
446 auto mad_matrix = SkMatrix::Concat(SkMatrix::MakeTrans(bias, 0),
447 SkMatrix::MakeScale(scale, 1));
448 auto m = alloc->makeArrayDefault<float>(6);
449 SkAssertResult(mad_matrix.asAffine(m));
450 p->append(SkRasterPipeline::matrix_2x3, m);
451
452 return true;
453 }
454
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400455 if (dCenter + fRadius1 > fRadius2) {
456 // We only handle well behaved cases for now.
457 return false;
458 }
459
460 // To simplify the stage math, we transform the universe (translate/scale/rotate)
461 // such that fCenter1 -> (0, 0) and fCenter2 -> (1, 0).
462 SkMatrix map_to_unit_vector;
463 const SkPoint centers[2] = { fCenter1, fCenter2 };
464 const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
465 SkAssertResult(map_to_unit_vector.setPolyToPoly(centers, unitvec, 2));
466 matrix->postConcat(map_to_unit_vector);
467
468 // Since we've squashed the centers into a unit vector, we must also scale
469 // all the coefficient variables by (1 / dCenter).
470 const auto coeffA = 1 - dRadius * dRadius / (dCenter * dCenter);
471 if (SkScalarNearlyZero(coeffA)) {
472 // We only handle well behaved quadratic cases for now.
473 return false;
474 }
475
476 auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
477 ctx->fCoeffA = coeffA;
478 ctx->fInvCoeffA = 1 / coeffA;
479 ctx->fR0 = fRadius1 / dCenter;
480 ctx->fDR = dRadius / dCenter;
481
482 p->append(SkRasterPipeline::xy_to_2pt_conical, ctx);
483 return true;
Florin Malita0bb04112017-06-27 14:35:50 -0400484}