blob: 846b5f41d6b44bb27213545fbc0e403c87189d99 [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
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000010struct TwoPtRadialContext {
11 const TwoPtRadial& fRec;
12 float fRelX, fRelY;
13 const float fIncX, fIncY;
14 float fB;
15 const float fDB;
16
17 TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
18 SkScalar dfx, SkScalar dfy);
19 SkFixed nextT();
20};
21
rileya@google.com589708b2012-07-26 20:04:23 +000022static int valid_divide(float numer, float denom, float* ratio) {
23 SkASSERT(ratio);
24 if (0 == denom) {
25 return 0;
26 }
27 *ratio = numer / denom;
28 return 1;
29}
30
31// Return the number of distinct real roots, and write them into roots[] in
32// ascending order
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000033static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) {
rileya@google.com589708b2012-07-26 20:04:23 +000034 SkASSERT(roots);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000035
rileya@google.com589708b2012-07-26 20:04:23 +000036 if (A == 0) {
37 return valid_divide(-C, B, roots);
38 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000039
rileya@google.com589708b2012-07-26 20:04:23 +000040 float R = B*B - 4*A*C;
41 if (R < 0) {
42 return 0;
43 }
44 R = sk_float_sqrt(R);
45
46#if 1
47 float Q = B;
48 if (Q < 0) {
49 Q -= R;
50 } else {
51 Q += R;
52 }
53#else
54 // on 10.6 this was much slower than the above branch :(
55 float Q = B + copysignf(R, B);
56#endif
57 Q *= -0.5f;
58 if (0 == Q) {
59 roots[0] = 0;
60 return 1;
61 }
62
63 float r0 = Q / A;
64 float r1 = C / Q;
65 roots[0] = r0 < r1 ? r0 : r1;
66 roots[1] = r0 > r1 ? r0 : r1;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000067 if (descendingOrder) {
68 SkTSwap(roots[0], roots[1]);
69 }
rileya@google.com589708b2012-07-26 20:04:23 +000070 return 2;
71}
72
73static float lerp(float x, float dx, float t) {
74 return x + t * dx;
75}
76
77static float sqr(float x) { return x * x; }
78
79void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000080 const SkPoint& center1, SkScalar rad1,
81 bool flipped) {
rileya@google.com589708b2012-07-26 20:04:23 +000082 fCenterX = SkScalarToFloat(center0.fX);
83 fCenterY = SkScalarToFloat(center0.fY);
84 fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
85 fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
86 fRadius = SkScalarToFloat(rad0);
87 fDRadius = SkScalarToFloat(rad1) - fRadius;
88
89 fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
90 fRadius2 = sqr(fRadius);
91 fRDR = fRadius * fDRadius;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000092
93 fFlipped = flipped;
rileya@google.com589708b2012-07-26 20:04:23 +000094}
95
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000096TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
97 SkScalar dfx, SkScalar dfy)
98 : fRec(rec)
99 , fRelX(SkScalarToFloat(fx) - rec.fCenterX)
100 , fRelY(SkScalarToFloat(fy) - rec.fCenterY)
101 , fIncX(SkScalarToFloat(dfx))
102 , fIncY(SkScalarToFloat(dfy))
103 , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR))
104 , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {}
rileya@google.com589708b2012-07-26 20:04:23 +0000105
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000106SkFixed TwoPtRadialContext::nextT() {
rileya@google.com589708b2012-07-26 20:04:23 +0000107 float roots[2];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000108
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000109 float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
110 int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped);
rileya@google.com589708b2012-07-26 20:04:23 +0000111
112 fRelX += fIncX;
113 fRelY += fIncY;
114 fB += fDB;
115
116 if (0 == countRoots) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000117 return TwoPtRadial::kDontDrawT;
rileya@google.com589708b2012-07-26 20:04:23 +0000118 }
119
120 // Prefer the bigger t value if both give a radius(t) > 0
121 // find_quad_roots returns the values sorted, so we start with the last
122 float t = roots[countRoots - 1];
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000123 float r = lerp(fRec.fRadius, fRec.fDRadius, t);
fmalita5edf82e2016-03-03 06:41:54 -0800124 if (r < 0) {
rileya@google.com589708b2012-07-26 20:04:23 +0000125 t = roots[0]; // might be the same as roots[countRoots-1]
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000126 r = lerp(fRec.fRadius, fRec.fDRadius, t);
fmalita5edf82e2016-03-03 06:41:54 -0800127 if (r < 0) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000128 return TwoPtRadial::kDontDrawT;
rileya@google.com589708b2012-07-26 20:04:23 +0000129 }
130 }
fmalitae7365062016-04-07 19:27:45 -0700131 return SkFloatToFixed(t);
rileya@google.com589708b2012-07-26 20:04:23 +0000132}
133
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000134typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC,
reed@google.com60040292013-02-04 18:21:23 +0000135 const SkPMColor* cache, int toggle, int count);
rileya@google.com589708b2012-07-26 20:04:23 +0000136
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000137static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
reed@google.com60040292013-02-04 18:21:23 +0000138 const SkPMColor* SK_RESTRICT cache, int toggle,
139 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000140 for (; count > 0; --count) {
141 SkFixed t = rec->nextT();
142 if (TwoPtRadial::DontDrawT(t)) {
143 *dstC++ = 0;
144 } else {
145 SkFixed index = SkClampMax(t, 0xFFFF);
146 SkASSERT(index <= 0xFFFF);
reed@google.com60040292013-02-04 18:21:23 +0000147 *dstC++ = cache[toggle +
148 (index >> SkGradientShaderBase::kCache32Shift)];
rileya@google.com589708b2012-07-26 20:04:23 +0000149 }
reed@google.com60040292013-02-04 18:21:23 +0000150 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000151 }
152}
153
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000154static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
reed@google.com60040292013-02-04 18:21:23 +0000155 const SkPMColor* SK_RESTRICT cache, int toggle,
156 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000157 for (; count > 0; --count) {
158 SkFixed t = rec->nextT();
159 if (TwoPtRadial::DontDrawT(t)) {
160 *dstC++ = 0;
161 } else {
162 SkFixed index = repeat_tileproc(t);
163 SkASSERT(index <= 0xFFFF);
reed@google.com60040292013-02-04 18:21:23 +0000164 *dstC++ = cache[toggle +
165 (index >> SkGradientShaderBase::kCache32Shift)];
rileya@google.com589708b2012-07-26 20:04:23 +0000166 }
reed@google.com60040292013-02-04 18:21:23 +0000167 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000168 }
169}
170
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000171static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
reed@google.com60040292013-02-04 18:21:23 +0000172 const SkPMColor* SK_RESTRICT cache, int toggle,
173 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000174 for (; count > 0; --count) {
175 SkFixed t = rec->nextT();
176 if (TwoPtRadial::DontDrawT(t)) {
177 *dstC++ = 0;
178 } else {
179 SkFixed index = mirror_tileproc(t);
180 SkASSERT(index <= 0xFFFF);
reed@google.com60040292013-02-04 18:21:23 +0000181 *dstC++ = cache[toggle +
182 (index >> SkGradientShaderBase::kCache32Shift)];
rileya@google.com589708b2012-07-26 20:04:23 +0000183 }
reed@google.com60040292013-02-04 18:21:23 +0000184 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000185 }
186}
187
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000188/////////////////////////////////////////////////////////////////////
189
rileya@google.com589708b2012-07-26 20:04:23 +0000190SkTwoPointConicalGradient::SkTwoPointConicalGradient(
reed@google.com3d3a8602013-05-24 14:58:44 +0000191 const SkPoint& start, SkScalar startRadius,
192 const SkPoint& end, SkScalar endRadius,
reedaddf2ed2014-08-11 08:28:24 -0700193 bool flippedGrad, const Descriptor& desc)
mtkleincc695fe2014-12-10 10:29:19 -0800194 : SkGradientShaderBase(desc, SkMatrix::I())
reedaddf2ed2014-08-11 08:28:24 -0700195 , fCenter1(start)
196 , fCenter2(end)
197 , fRadius1(startRadius)
198 , fRadius2(endRadius)
199 , fFlippedGrad(flippedGrad)
200{
rileya@google.com589708b2012-07-26 20:04:23 +0000201 // this is degenerate, and should be caught by our caller
202 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
mtkleincc695fe2014-12-10 10:29:19 -0800203 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000204}
205
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +0000206bool SkTwoPointConicalGradient::isOpaque() const {
robertphillips@google.comcb6d97c2013-07-09 13:50:09 +0000207 // Because areas outside the cone are left untouched, we cannot treat the
208 // shader as opaque even if the gradient itself is opaque.
209 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
210 return false;
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +0000211}
212
Florin Malita4aed1382017-05-25 10:38:07 -0400213SkShaderBase::Context* SkTwoPointConicalGradient::onMakeContext(
Herb Derby83e939b2017-02-07 14:25:11 -0500214 const ContextRec& rec, SkArenaAlloc* alloc) const {
215 return CheckedMakeContext<TwoPointConicalGradientContext>(alloc, *this, rec);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000216}
217
218SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000219 const SkTwoPointConicalGradient& shader, const ContextRec& rec)
220 : INHERITED(shader, rec)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000221{
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000222 // in general, we might discard based on computed-radius, so clear
223 // this flag (todo: sometimes we can detect that we never discard...)
224 fFlags &= ~kOpaqueAlpha_Flag;
225}
226
227void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
228 int x, int y, SkPMColor* dstCParam, int count) {
229 const SkTwoPointConicalGradient& twoPointConicalGradient =
230 static_cast<const SkTwoPointConicalGradient&>(fShader);
231
reed@google.com60040292013-02-04 18:21:23 +0000232 int toggle = init_dither_toggle(x, y);
reed@google.com60040292013-02-04 18:21:23 +0000233
rileya@google.com589708b2012-07-26 20:04:23 +0000234 SkASSERT(count > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000235
rileya@google.com589708b2012-07-26 20:04:23 +0000236 SkPMColor* SK_RESTRICT dstC = dstCParam;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000237
rileya@google.com589708b2012-07-26 20:04:23 +0000238 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
bsalomon@google.com100abf42012-09-05 17:40:04 +0000239
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000240 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
rileya@google.com589708b2012-07-26 20:04:23 +0000241
reed@google.com60040292013-02-04 18:21:23 +0000242 TwoPointConicalProc shadeProc = twopoint_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000243 if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000244 shadeProc = twopoint_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000245 } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000246 shadeProc = twopoint_mirror;
247 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000248 SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000249 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000250
rileya@google.com589708b2012-07-26 20:04:23 +0000251 if (fDstToIndexClass != kPerspective_MatrixClass) {
252 SkPoint srcPt;
253 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
254 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
255 SkScalar dx, fx = srcPt.fX;
256 SkScalar dy, fy = srcPt.fY;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000257
rileya@google.com589708b2012-07-26 20:04:23 +0000258 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
benjaminwagner8e175562016-02-16 10:09:40 -0800259 const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
260 dx = step.fX;
261 dy = step.fY;
rileya@google.com589708b2012-07-26 20:04:23 +0000262 } else {
263 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
264 dx = fDstToIndex.getScaleX();
265 dy = fDstToIndex.getSkewY();
266 }
267
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000268 TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
269 (*shadeProc)(&rec, dstC, cache, toggle, count);
rileya@google.com589708b2012-07-26 20:04:23 +0000270 } else { // perspective case
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000271 SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
272 SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
rileya@google.com589708b2012-07-26 20:04:23 +0000273 for (; count > 0; --count) {
274 SkPoint srcPt;
275 dstProc(fDstToIndex, dstX, dstY, &srcPt);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000276 TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
277 (*shadeProc)(&rec, dstC, cache, toggle, 1);
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000278
279 dstX += SK_Scalar1;
reed@google.com60040292013-02-04 18:21:23 +0000280 toggle = next_dither_toggle(toggle);
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000281 dstC += 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000282 }
283 }
284}
285
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000286// Returns the original non-sorted version of the gradient
rileya@google.com589708b2012-07-26 20:04:23 +0000287SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
288 GradientInfo* info) const {
289 if (info) {
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000290 commonAsAGradient(info, fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000291 info->fPoint[0] = fCenter1;
292 info->fPoint[1] = fCenter2;
293 info->fRadius[0] = fRadius1;
294 info->fRadius[1] = fRadius2;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000295 if (fFlippedGrad) {
296 SkTSwap(info->fPoint[0], info->fPoint[1]);
297 SkTSwap(info->fRadius[0], info->fRadius[1]);
298 }
rileya@google.com589708b2012-07-26 20:04:23 +0000299 }
300 return kConical_GradientType;
301}
302
reed60c9b582016-04-03 09:11:13 -0700303sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -0700304 DescriptorScope desc;
305 if (!desc.unflatten(buffer)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700306 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700307 }
308 SkPoint c1 = buffer.readPoint();
309 SkPoint c2 = buffer.readPoint();
310 SkScalar r1 = buffer.readScalar();
311 SkScalar r2 = buffer.readScalar();
312
313 if (buffer.readBool()) { // flipped
314 SkTSwap(c1, c2);
315 SkTSwap(r1, r2);
316
brianosmane25d71c2016-09-28 11:27:28 -0700317 SkColor4f* colors = desc.mutableColors();
reed9fa60da2014-08-21 07:59:51 -0700318 SkScalar* pos = desc.mutablePos();
319 const int last = desc.fCount - 1;
320 const int half = desc.fCount >> 1;
321 for (int i = 0; i < half; ++i) {
322 SkTSwap(colors[i], colors[last - i]);
323 if (pos) {
324 SkScalar tmp = pos[i];
325 pos[i] = SK_Scalar1 - pos[last - i];
326 pos[last - i] = SK_Scalar1 - tmp;
327 }
328 }
329 if (pos) {
330 if (desc.fCount & 1) {
331 pos[half] = SK_Scalar1 - pos[half];
332 }
333 }
334 }
335
brianosmane25d71c2016-09-28 11:27:28 -0700336 return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
337 std::move(desc.fColorSpace), desc.fPos,
reed8a21c9f2016-03-08 18:50:00 -0800338 desc.fCount, desc.fTileMode, desc.fGradFlags,
reed60c9b582016-04-03 09:11:13 -0700339 desc.fLocalMatrix);
reed9fa60da2014-08-21 07:59:51 -0700340}
341
342void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000343 this->INHERITED::flatten(buffer);
344 buffer.writePoint(fCenter1);
345 buffer.writePoint(fCenter2);
346 buffer.writeScalar(fRadius1);
347 buffer.writeScalar(fRadius2);
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000348 buffer.writeBool(fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000349}
350
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000351#if SK_SUPPORT_GPU
352
dandov9de5b512014-06-10 14:38:28 -0700353#include "SkGr.h"
brianosman9557c272016-09-15 06:59:15 -0700354#include "SkTwoPointConicalGradient_gpu.h"
dandov9de5b512014-06-10 14:38:28 -0700355
bungeman06ca8ec2016-06-09 08:01:03 -0700356sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
brianosman1638c0d2016-07-25 05:12:53 -0700357 const AsFPArgs& args) const {
brianosman839345d2016-07-22 11:04:53 -0700358 SkASSERT(args.fContext);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000359 SkASSERT(fPtsToUnit.isIdentity());
brianosmanb9c51372016-09-15 11:09:45 -0700360 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
361 args.fDstColorSpace);
brianosman9557c272016-09-15 06:59:15 -0700362 sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
brianosmanb9c51372016-09-15 11:09:45 -0700363 GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
364 std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
Brian Salomon6af27012017-06-09 08:21:42 -0400365 if (!inner) {
366 return nullptr;
367 }
bungeman06ca8ec2016-06-09 08:01:03 -0700368 return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000369}
370
twiz@google.coma5e65ec2012-08-02 15:15:16 +0000371#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000372
Matt Sarett6cc6ae752017-04-18 18:29:12 -0400373sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
374 SkSTArray<8, SkColor> origColorsStorage(fColorCount);
375 SkSTArray<8, SkScalar> origPosStorage(fColorCount);
376 SkSTArray<8, SkColor> xformedColorsStorage(fColorCount);
377 SkColor* origColors = origColorsStorage.begin();
378 SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr;
379 SkColor* xformedColors = xformedColorsStorage.begin();
380
381 // Flip if necessary
382 SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1;
383 SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2;
384 SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1;
385 SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2;
386 for (int i = 0; i < fColorCount; i++) {
387 origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i];
388 if (origPos) {
389 origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i];
390 }
391 }
392
393 xformer->apply(xformedColors, origColors, fColorCount);
394 return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors,
395 origPos, fColorCount, fTileMode, fGradFlags,
396 &this->getLocalMatrix());
397}
398
399
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000400#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000401void SkTwoPointConicalGradient::toString(SkString* str) const {
402 str->append("SkTwoPointConicalGradient: (");
403
404 str->append("center1: (");
405 str->appendScalar(fCenter1.fX);
406 str->append(", ");
407 str->appendScalar(fCenter1.fY);
408 str->append(") radius1: ");
409 str->appendScalar(fRadius1);
410 str->append(" ");
411
412 str->append("center2: (");
413 str->appendScalar(fCenter2.fX);
414 str->append(", ");
415 str->appendScalar(fCenter2.fY);
416 str->append(") radius2: ");
417 str->appendScalar(fRadius2);
418 str->append(" ");
419
420 this->INHERITED::toString(str);
421
422 str->append(")");
423}
424#endif
Florin Malita0bb04112017-06-27 14:35:50 -0400425
426bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
427 SkMatrix* matrix,
428 SkRasterPipeline* p) const {
429 // When the two circles are concentric, we can pretend we're radial (with a tiny *twist).
430 if (SkScalarNearlyZero((fCenter1 - fCenter2).length())) {
431 matrix->postTranslate(-fCenter1.fX, -fCenter1.fY);
432 matrix->postScale(1 / fRadius2, 1 / fRadius2);
433 p->append(SkRasterPipeline::xy_to_radius);
434
435 // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
436 auto dR = fRadius2 - fRadius1;
437 SkASSERT(dR > 0);
438 auto scale = fRadius2 / dR;
439 auto bias = -fRadius1 / dR;
440
441 auto mad_matrix = SkMatrix::Concat(SkMatrix::MakeTrans(bias, 0),
442 SkMatrix::MakeScale(scale, 1));
443 auto m = alloc->makeArrayDefault<float>(6);
444 SkAssertResult(mad_matrix.asAffine(m));
445 p->append(SkRasterPipeline::matrix_2x3, m);
446
447 return true;
448 }
449
450 // TODO
451 return false;
452}