Line data Source code
1 : /*
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"
9 :
10 : struct 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 :
22 0 : static int valid_divide(float numer, float denom, float* ratio) {
23 0 : SkASSERT(ratio);
24 0 : if (0 == denom) {
25 0 : return 0;
26 : }
27 0 : *ratio = numer / denom;
28 0 : return 1;
29 : }
30 :
31 : // Return the number of distinct real roots, and write them into roots[] in
32 : // ascending order
33 0 : static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) {
34 0 : SkASSERT(roots);
35 :
36 0 : if (A == 0) {
37 0 : return valid_divide(-C, B, roots);
38 : }
39 :
40 0 : float R = B*B - 4*A*C;
41 0 : if (R < 0) {
42 0 : return 0;
43 : }
44 0 : R = sk_float_sqrt(R);
45 :
46 : #if 1
47 0 : float Q = B;
48 0 : if (Q < 0) {
49 0 : Q -= R;
50 : } else {
51 0 : 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 0 : Q *= -0.5f;
58 0 : if (0 == Q) {
59 0 : roots[0] = 0;
60 0 : return 1;
61 : }
62 :
63 0 : float r0 = Q / A;
64 0 : float r1 = C / Q;
65 0 : roots[0] = r0 < r1 ? r0 : r1;
66 0 : roots[1] = r0 > r1 ? r0 : r1;
67 0 : if (descendingOrder) {
68 0 : SkTSwap(roots[0], roots[1]);
69 : }
70 0 : return 2;
71 : }
72 :
73 0 : static float lerp(float x, float dx, float t) {
74 0 : return x + t * dx;
75 : }
76 :
77 0 : static float sqr(float x) { return x * x; }
78 :
79 0 : void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
80 : const SkPoint& center1, SkScalar rad1,
81 : bool flipped) {
82 0 : fCenterX = SkScalarToFloat(center0.fX);
83 0 : fCenterY = SkScalarToFloat(center0.fY);
84 0 : fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
85 0 : fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
86 0 : fRadius = SkScalarToFloat(rad0);
87 0 : fDRadius = SkScalarToFloat(rad1) - fRadius;
88 :
89 0 : fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
90 0 : fRadius2 = sqr(fRadius);
91 0 : fRDR = fRadius * fDRadius;
92 :
93 0 : fFlipped = flipped;
94 0 : }
95 :
96 0 : TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
97 0 : SkScalar dfx, SkScalar dfy)
98 : : fRec(rec)
99 0 : , fRelX(SkScalarToFloat(fx) - rec.fCenterX)
100 0 : , fRelY(SkScalarToFloat(fy) - rec.fCenterY)
101 : , fIncX(SkScalarToFloat(dfx))
102 : , fIncY(SkScalarToFloat(dfy))
103 0 : , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR))
104 0 : , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {}
105 :
106 0 : SkFixed TwoPtRadialContext::nextT() {
107 : float roots[2];
108 :
109 0 : float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
110 0 : int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped);
111 :
112 0 : fRelX += fIncX;
113 0 : fRelY += fIncY;
114 0 : fB += fDB;
115 :
116 0 : if (0 == countRoots) {
117 0 : return TwoPtRadial::kDontDrawT;
118 : }
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 0 : float t = roots[countRoots - 1];
123 0 : float r = lerp(fRec.fRadius, fRec.fDRadius, t);
124 0 : if (r < 0) {
125 0 : t = roots[0]; // might be the same as roots[countRoots-1]
126 0 : r = lerp(fRec.fRadius, fRec.fDRadius, t);
127 0 : if (r < 0) {
128 0 : return TwoPtRadial::kDontDrawT;
129 : }
130 : }
131 0 : return SkFloatToFixed(t);
132 : }
133 :
134 : typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC,
135 : const SkPMColor* cache, int toggle, int count);
136 :
137 0 : static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
138 : const SkPMColor* SK_RESTRICT cache, int toggle,
139 : int count) {
140 0 : for (; count > 0; --count) {
141 0 : SkFixed t = rec->nextT();
142 0 : if (TwoPtRadial::DontDrawT(t)) {
143 0 : *dstC++ = 0;
144 : } else {
145 0 : SkFixed index = SkClampMax(t, 0xFFFF);
146 0 : SkASSERT(index <= 0xFFFF);
147 0 : *dstC++ = cache[toggle +
148 0 : (index >> SkGradientShaderBase::kCache32Shift)];
149 : }
150 0 : toggle = next_dither_toggle(toggle);
151 : }
152 0 : }
153 :
154 0 : static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
155 : const SkPMColor* SK_RESTRICT cache, int toggle,
156 : int count) {
157 0 : for (; count > 0; --count) {
158 0 : SkFixed t = rec->nextT();
159 0 : if (TwoPtRadial::DontDrawT(t)) {
160 0 : *dstC++ = 0;
161 : } else {
162 0 : SkFixed index = repeat_tileproc(t);
163 0 : SkASSERT(index <= 0xFFFF);
164 0 : *dstC++ = cache[toggle +
165 0 : (index >> SkGradientShaderBase::kCache32Shift)];
166 : }
167 0 : toggle = next_dither_toggle(toggle);
168 : }
169 0 : }
170 :
171 0 : static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
172 : const SkPMColor* SK_RESTRICT cache, int toggle,
173 : int count) {
174 0 : for (; count > 0; --count) {
175 0 : SkFixed t = rec->nextT();
176 0 : if (TwoPtRadial::DontDrawT(t)) {
177 0 : *dstC++ = 0;
178 : } else {
179 0 : SkFixed index = mirror_tileproc(t);
180 0 : SkASSERT(index <= 0xFFFF);
181 0 : *dstC++ = cache[toggle +
182 0 : (index >> SkGradientShaderBase::kCache32Shift)];
183 : }
184 0 : toggle = next_dither_toggle(toggle);
185 : }
186 0 : }
187 :
188 : /////////////////////////////////////////////////////////////////////
189 :
190 0 : SkTwoPointConicalGradient::SkTwoPointConicalGradient(
191 : const SkPoint& start, SkScalar startRadius,
192 : const SkPoint& end, SkScalar endRadius,
193 0 : bool flippedGrad, const Descriptor& desc)
194 : : SkGradientShaderBase(desc, SkMatrix::I())
195 : , fCenter1(start)
196 : , fCenter2(end)
197 : , fRadius1(startRadius)
198 : , fRadius2(endRadius)
199 0 : , fFlippedGrad(flippedGrad)
200 : {
201 : // this is degenerate, and should be caught by our caller
202 0 : SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
203 0 : fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad);
204 0 : }
205 :
206 0 : bool SkTwoPointConicalGradient::isOpaque() const {
207 : // 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 0 : return false;
211 : }
212 :
213 0 : SkShader::Context* SkTwoPointConicalGradient::onMakeContext(
214 : const ContextRec& rec, SkArenaAlloc* alloc) const {
215 0 : return CheckedMakeContext<TwoPointConicalGradientContext>(alloc, *this, rec);
216 : }
217 :
218 0 : SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
219 0 : const SkTwoPointConicalGradient& shader, const ContextRec& rec)
220 0 : : INHERITED(shader, rec)
221 : {
222 : // in general, we might discard based on computed-radius, so clear
223 : // this flag (todo: sometimes we can detect that we never discard...)
224 0 : fFlags &= ~kOpaqueAlpha_Flag;
225 0 : }
226 :
227 0 : void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
228 : int x, int y, SkPMColor* dstCParam, int count) {
229 : const SkTwoPointConicalGradient& twoPointConicalGradient =
230 0 : static_cast<const SkTwoPointConicalGradient&>(fShader);
231 :
232 0 : int toggle = init_dither_toggle(x, y);
233 :
234 0 : SkASSERT(count > 0);
235 :
236 0 : SkPMColor* SK_RESTRICT dstC = dstCParam;
237 :
238 0 : SkMatrix::MapXYProc dstProc = fDstToIndexProc;
239 :
240 0 : const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
241 :
242 0 : TwoPointConicalProc shadeProc = twopoint_repeat;
243 0 : if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
244 0 : shadeProc = twopoint_clamp;
245 0 : } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
246 0 : shadeProc = twopoint_mirror;
247 : } else {
248 0 : SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
249 : }
250 :
251 0 : if (fDstToIndexClass != kPerspective_MatrixClass) {
252 : SkPoint srcPt;
253 0 : dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
254 0 : SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
255 0 : SkScalar dx, fx = srcPt.fX;
256 0 : SkScalar dy, fy = srcPt.fY;
257 :
258 0 : if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
259 0 : const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
260 0 : dx = step.fX;
261 0 : dy = step.fY;
262 : } else {
263 0 : SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
264 0 : dx = fDstToIndex.getScaleX();
265 0 : dy = fDstToIndex.getSkewY();
266 : }
267 :
268 0 : TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
269 0 : (*shadeProc)(&rec, dstC, cache, toggle, count);
270 : } else { // perspective case
271 0 : SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
272 0 : SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
273 0 : for (; count > 0; --count) {
274 : SkPoint srcPt;
275 0 : dstProc(fDstToIndex, dstX, dstY, &srcPt);
276 0 : TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
277 0 : (*shadeProc)(&rec, dstC, cache, toggle, 1);
278 :
279 0 : dstX += SK_Scalar1;
280 0 : toggle = next_dither_toggle(toggle);
281 0 : dstC += 1;
282 : }
283 : }
284 0 : }
285 :
286 : // Returns the original non-sorted version of the gradient
287 0 : SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
288 : GradientInfo* info) const {
289 0 : if (info) {
290 0 : commonAsAGradient(info, fFlippedGrad);
291 0 : info->fPoint[0] = fCenter1;
292 0 : info->fPoint[1] = fCenter2;
293 0 : info->fRadius[0] = fRadius1;
294 0 : info->fRadius[1] = fRadius2;
295 0 : if (fFlippedGrad) {
296 0 : SkTSwap(info->fPoint[0], info->fPoint[1]);
297 0 : SkTSwap(info->fRadius[0], info->fRadius[1]);
298 : }
299 : }
300 0 : return kConical_GradientType;
301 : }
302 :
303 0 : sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
304 0 : DescriptorScope desc;
305 0 : if (!desc.unflatten(buffer)) {
306 0 : return nullptr;
307 : }
308 0 : SkPoint c1 = buffer.readPoint();
309 0 : SkPoint c2 = buffer.readPoint();
310 0 : SkScalar r1 = buffer.readScalar();
311 0 : SkScalar r2 = buffer.readScalar();
312 :
313 0 : if (buffer.readBool()) { // flipped
314 0 : SkTSwap(c1, c2);
315 0 : SkTSwap(r1, r2);
316 :
317 0 : SkColor4f* colors = desc.mutableColors();
318 0 : SkScalar* pos = desc.mutablePos();
319 0 : const int last = desc.fCount - 1;
320 0 : const int half = desc.fCount >> 1;
321 0 : for (int i = 0; i < half; ++i) {
322 0 : SkTSwap(colors[i], colors[last - i]);
323 0 : if (pos) {
324 0 : SkScalar tmp = pos[i];
325 0 : pos[i] = SK_Scalar1 - pos[last - i];
326 0 : pos[last - i] = SK_Scalar1 - tmp;
327 : }
328 : }
329 0 : if (pos) {
330 0 : if (desc.fCount & 1) {
331 0 : pos[half] = SK_Scalar1 - pos[half];
332 : }
333 : }
334 : }
335 :
336 0 : return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
337 0 : std::move(desc.fColorSpace), desc.fPos,
338 : desc.fCount, desc.fTileMode, desc.fGradFlags,
339 0 : desc.fLocalMatrix);
340 : }
341 :
342 0 : void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
343 0 : this->INHERITED::flatten(buffer);
344 0 : buffer.writePoint(fCenter1);
345 0 : buffer.writePoint(fCenter2);
346 0 : buffer.writeScalar(fRadius1);
347 0 : buffer.writeScalar(fRadius2);
348 0 : buffer.writeBool(fFlippedGrad);
349 0 : }
350 :
351 : #if SK_SUPPORT_GPU
352 :
353 : #include "SkGr.h"
354 : #include "SkTwoPointConicalGradient_gpu.h"
355 :
356 0 : sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
357 : const AsFPArgs& args) const {
358 0 : SkASSERT(args.fContext);
359 0 : SkASSERT(fPtsToUnit.isIdentity());
360 0 : sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
361 0 : args.fDstColorSpace);
362 : sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
363 0 : GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
364 0 : std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
365 0 : return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
366 : }
367 :
368 : #endif
369 :
370 : #ifndef SK_IGNORE_TO_STRING
371 0 : void SkTwoPointConicalGradient::toString(SkString* str) const {
372 0 : str->append("SkTwoPointConicalGradient: (");
373 :
374 0 : str->append("center1: (");
375 0 : str->appendScalar(fCenter1.fX);
376 0 : str->append(", ");
377 0 : str->appendScalar(fCenter1.fY);
378 0 : str->append(") radius1: ");
379 0 : str->appendScalar(fRadius1);
380 0 : str->append(" ");
381 :
382 0 : str->append("center2: (");
383 0 : str->appendScalar(fCenter2.fX);
384 0 : str->append(", ");
385 0 : str->appendScalar(fCenter2.fY);
386 0 : str->append(") radius2: ");
387 0 : str->appendScalar(fRadius2);
388 0 : str->append(" ");
389 :
390 0 : this->INHERITED::toString(str);
391 :
392 0 : str->append(")");
393 0 : }
394 : #endif
|