OLD | NEW |
| (Empty) |
1 | |
2 /* | |
3 * Copyright 2012 Google Inc. | |
4 * | |
5 * Use of this source code is governed by a BSD-style license that can be | |
6 * found in the LICENSE file. | |
7 */ | |
8 | |
9 #include "SkTwoPointRadialGradient.h" | |
10 | |
11 /* Two-point radial gradients are specified by two circles, each with a center | |
12 point and radius. The gradient can be considered to be a series of | |
13 concentric circles, with the color interpolated from the start circle | |
14 (at t=0) to the end circle (at t=1). | |
15 | |
16 For each point (x, y) in the span, we want to find the | |
17 interpolated circle that intersects that point. The center | |
18 of the desired circle (Cx, Cy) falls at some distance t | |
19 along the line segment between the start point (Sx, Sy) and | |
20 end point (Ex, Ey): | |
21 | |
22 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1) | |
23 Cy = (1 - t) * Sy + t * Ey | |
24 | |
25 The radius of the desired circle (r) is also a linear interpolation t | |
26 between the start and end radii (Sr and Er): | |
27 | |
28 r = (1 - t) * Sr + t * Er | |
29 | |
30 But | |
31 | |
32 (x - Cx)^2 + (y - Cy)^2 = r^2 | |
33 | |
34 so | |
35 | |
36 (x - ((1 - t) * Sx + t * Ex))^2 | |
37 + (y - ((1 - t) * Sy + t * Ey))^2 | |
38 = ((1 - t) * Sr + t * Er)^2 | |
39 | |
40 Solving for t yields | |
41 | |
42 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2 | |
43 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t | |
44 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0 | |
45 | |
46 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy =
y - Sy | |
47 | |
48 [Dx^2 + Dy^2 - Dr^2)] * t^2 | |
49 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t | |
50 + [dx^2 + dy^2 - Sr^2] = 0 | |
51 | |
52 A quadratic in t. The two roots of the quadratic reflect the two | |
53 possible circles on which the point may fall. Solving for t yields | |
54 the gradient value to use. | |
55 | |
56 If a<0, the start circle is entirely contained in the | |
57 end circle, and one of the roots will be <0 or >1 (off the line | |
58 segment). If a>0, the start circle falls at least partially | |
59 outside the end circle (or vice versa), and the gradient | |
60 defines a "tube" where a point may be on one circle (on the | |
61 inside of the tube) or the other (outside of the tube). We choose | |
62 one arbitrarily. | |
63 | |
64 In order to keep the math to within the limits of fixed point, | |
65 we divide the entire quadratic by Dr^2, and replace | |
66 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving | |
67 | |
68 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 | |
69 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t | |
70 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0 | |
71 | |
72 (x' and y' are computed by appending the subtract and scale to the | |
73 fDstToIndex matrix in the constructor). | |
74 | |
75 Since the 'A' component of the quadratic is independent of x' and y', it | |
76 is precomputed in the constructor. Since the 'B' component is linear in | |
77 x' and y', if x and y are linear in the span, 'B' can be computed | |
78 incrementally with a simple delta (db below). If it is not (e.g., | |
79 a perspective projection), it must be computed in the loop. | |
80 | |
81 */ | |
82 | |
83 namespace { | |
84 | |
85 inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, | |
86 SkScalar sr2d2, SkScalar foura, | |
87 SkScalar oneOverTwoA, bool posRoot) { | |
88 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; | |
89 if (0 == foura) { | |
90 return SkScalarToFixed(SkScalarDiv(-c, b)); | |
91 } | |
92 | |
93 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); | |
94 if (discrim < 0) { | |
95 discrim = -discrim; | |
96 } | |
97 SkScalar rootDiscrim = SkScalarSqrt(discrim); | |
98 SkScalar result; | |
99 if (posRoot) { | |
100 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); | |
101 } else { | |
102 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); | |
103 } | |
104 return SkScalarToFixed(result); | |
105 } | |
106 | |
107 typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx, | |
108 SkScalar fy, SkScalar dy, | |
109 SkScalar b, SkScalar db, | |
110 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, | |
111 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, | |
112 int count); | |
113 | |
114 void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx, | |
115 SkScalar fy, SkScalar dy, | |
116 SkScalar b, SkScalar db, | |
117 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, | |
118 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, | |
119 int count) { | |
120 for (; count > 0; --count) { | |
121 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, | |
122 fOneOverTwoA, posRoot); | |
123 SkFixed index = SkClampMax(t, 0xFFFF); | |
124 SkASSERT(index <= 0xFFFF); | |
125 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; | |
126 fx += dx; | |
127 fy += dy; | |
128 b += db; | |
129 } | |
130 } | |
131 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx, | |
132 SkScalar fy, SkScalar dy, | |
133 SkScalar b, SkScalar db, | |
134 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, | |
135 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, | |
136 int count) { | |
137 for (; count > 0; --count) { | |
138 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, | |
139 fOneOverTwoA, posRoot); | |
140 SkFixed index = mirror_tileproc(t); | |
141 SkASSERT(index <= 0xFFFF); | |
142 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; | |
143 fx += dx; | |
144 fy += dy; | |
145 b += db; | |
146 } | |
147 } | |
148 | |
149 void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx, | |
150 SkScalar fy, SkScalar dy, | |
151 SkScalar b, SkScalar db, | |
152 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, | |
153 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, | |
154 int count) { | |
155 for (; count > 0; --count) { | |
156 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, | |
157 fOneOverTwoA, posRoot); | |
158 SkFixed index = repeat_tileproc(t); | |
159 SkASSERT(index <= 0xFFFF); | |
160 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; | |
161 fx += dx; | |
162 fy += dy; | |
163 b += db; | |
164 } | |
165 } | |
166 } | |
167 | |
168 ///////////////////////////////////////////////////////////////////// | |
169 | |
170 static SkMatrix pts_to_unit(const SkPoint& start, SkScalar diffRadius) { | |
171 SkScalar inv = diffRadius ? SkScalarInvert(diffRadius) : 0; | |
172 SkMatrix matrix; | |
173 matrix.setTranslate(-start.fX, -start.fY); | |
174 matrix.postScale(inv, inv); | |
175 return matrix; | |
176 } | |
177 | |
178 SkTwoPointRadialGradient::SkTwoPointRadialGradient(const SkPoint& start, SkScala
r startRadius, | |
179 const SkPoint& end, SkScalar
endRadius, | |
180 const Descriptor& desc) | |
181 : SkGradientShaderBase(desc, pts_to_unit(start, endRadius - startRadius)) | |
182 , fCenter1(start) | |
183 , fCenter2(end) | |
184 , fRadius1(startRadius) | |
185 , fRadius2(endRadius) | |
186 { | |
187 fDiff = fCenter1 - fCenter2; | |
188 fDiffRadius = fRadius2 - fRadius1; | |
189 // hack to avoid zero-divide for now | |
190 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0; | |
191 fDiff.fX = SkScalarMul(fDiff.fX, inv); | |
192 fDiff.fY = SkScalarMul(fDiff.fY, inv); | |
193 fStartRadius = SkScalarMul(fRadius1, inv); | |
194 fSr2D2 = SkScalarSquare(fStartRadius); | |
195 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; | |
196 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; | |
197 } | |
198 | |
199 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap( | |
200 SkBitmap* bitmap, | |
201 SkMatrix* matrix, | |
202 SkShader::TileMode* xy) const { | |
203 if (bitmap) { | |
204 this->getGradientTableBitmap(bitmap); | |
205 } | |
206 SkScalar diffL = 0; // just to avoid gcc warning | |
207 if (matrix) { | |
208 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) + | |
209 SkScalarSquare(fDiff.fY)); | |
210 } | |
211 if (matrix) { | |
212 if (diffL) { | |
213 SkScalar invDiffL = SkScalarInvert(diffL); | |
214 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY), | |
215 SkScalarMul(invDiffL, fDiff.fX)); | |
216 } else { | |
217 matrix->reset(); | |
218 } | |
219 matrix->preConcat(fPtsToUnit); | |
220 } | |
221 if (xy) { | |
222 xy[0] = fTileMode; | |
223 xy[1] = kClamp_TileMode; | |
224 } | |
225 return kTwoPointRadial_BitmapType; | |
226 } | |
227 | |
228 SkShader::GradientType SkTwoPointRadialGradient::asAGradient( | |
229 SkShader::GradientInfo* info) const { | |
230 if (info) { | |
231 commonAsAGradient(info); | |
232 info->fPoint[0] = fCenter1; | |
233 info->fPoint[1] = fCenter2; | |
234 info->fRadius[0] = fRadius1; | |
235 info->fRadius[1] = fRadius2; | |
236 } | |
237 return kRadial2_GradientType; | |
238 } | |
239 | |
240 size_t SkTwoPointRadialGradient::contextSize() const { | |
241 return sizeof(TwoPointRadialGradientContext); | |
242 } | |
243 | |
244 SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& r
ec, | |
245 void* storage) cons
t { | |
246 // For now, we might have divided by zero, so detect that. | |
247 if (0 == fDiffRadius) { | |
248 return NULL; | |
249 } | |
250 return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this,
rec)); | |
251 } | |
252 | |
253 SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientC
ontext( | |
254 const SkTwoPointRadialGradient& shader, const ContextRec& rec) | |
255 : INHERITED(shader, rec) | |
256 { | |
257 // we don't have a span16 proc | |
258 fFlags &= ~kHasSpan16_Flag; | |
259 } | |
260 | |
261 void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan( | |
262 int x, int y, SkPMColor* dstCParam, int count) { | |
263 SkASSERT(count > 0); | |
264 | |
265 const SkTwoPointRadialGradient& twoPointRadialGradient = | |
266 static_cast<const SkTwoPointRadialGradient&>(fShader); | |
267 | |
268 SkPMColor* SK_RESTRICT dstC = dstCParam; | |
269 | |
270 // Zero difference between radii: fill with transparent black. | |
271 if (twoPointRadialGradient.fDiffRadius == 0) { | |
272 sk_bzero(dstC, count * sizeof(*dstC)); | |
273 return; | |
274 } | |
275 SkMatrix::MapXYProc dstProc = fDstToIndexProc; | |
276 TileProc proc = twoPointRadialGradient.fTileProc; | |
277 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); | |
278 | |
279 SkScalar foura = twoPointRadialGradient.fA * 4; | |
280 bool posRoot = twoPointRadialGradient.fDiffRadius < 0; | |
281 if (fDstToIndexClass != kPerspective_MatrixClass) { | |
282 SkPoint srcPt; | |
283 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, | |
284 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); | |
285 SkScalar dx, fx = srcPt.fX; | |
286 SkScalar dy, fy = srcPt.fY; | |
287 | |
288 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { | |
289 SkFixed fixedX, fixedY; | |
290 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); | |
291 dx = SkFixedToScalar(fixedX); | |
292 dy = SkFixedToScalar(fixedY); | |
293 } else { | |
294 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); | |
295 dx = fDstToIndex.getScaleX(); | |
296 dy = fDstToIndex.getSkewY(); | |
297 } | |
298 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) + | |
299 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) - | |
300 twoPointRadialGradient.fStartRadius) * 2; | |
301 SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) + | |
302 SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2; | |
303 | |
304 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat; | |
305 if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) { | |
306 shadeProc = shadeSpan_twopoint_clamp; | |
307 } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMod
e) { | |
308 shadeProc = shadeSpan_twopoint_mirror; | |
309 } else { | |
310 SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileM
ode); | |
311 } | |
312 (*shadeProc)(fx, dx, fy, dy, b, db, | |
313 twoPointRadialGradient.fSr2D2, foura, | |
314 twoPointRadialGradient.fOneOverTwoA, posRoot, | |
315 dstC, cache, count); | |
316 } else { // perspective case | |
317 SkScalar dstX = SkIntToScalar(x); | |
318 SkScalar dstY = SkIntToScalar(y); | |
319 for (; count > 0; --count) { | |
320 SkPoint srcPt; | |
321 dstProc(fDstToIndex, dstX, dstY, &srcPt); | |
322 SkScalar fx = srcPt.fX; | |
323 SkScalar fy = srcPt.fY; | |
324 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) + | |
325 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) - | |
326 twoPointRadialGradient.fStartRadius) * 2; | |
327 SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D
2, foura, | |
328 twoPointRadialGradient.fOneOverTwoA, po
sRoot); | |
329 SkFixed index = proc(t); | |
330 SkASSERT(index <= 0xFFFF); | |
331 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; | |
332 dstX += SK_Scalar1; | |
333 } | |
334 } | |
335 } | |
336 | |
337 #ifndef SK_IGNORE_TO_STRING | |
338 void SkTwoPointRadialGradient::toString(SkString* str) const { | |
339 str->append("SkTwoPointRadialGradient: ("); | |
340 | |
341 str->append("center1: ("); | |
342 str->appendScalar(fCenter1.fX); | |
343 str->append(", "); | |
344 str->appendScalar(fCenter1.fY); | |
345 str->append(") radius1: "); | |
346 str->appendScalar(fRadius1); | |
347 str->append(" "); | |
348 | |
349 str->append("center2: ("); | |
350 str->appendScalar(fCenter2.fX); | |
351 str->append(", "); | |
352 str->appendScalar(fCenter2.fY); | |
353 str->append(") radius2: "); | |
354 str->appendScalar(fRadius2); | |
355 str->append(" "); | |
356 | |
357 this->INHERITED::toString(str); | |
358 | |
359 str->append(")"); | |
360 } | |
361 #endif | |
362 | |
363 SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) { | |
364 DescriptorScope desc; | |
365 if (!desc.unflatten(buffer)) { | |
366 return NULL; | |
367 } | |
368 const SkPoint c1 = buffer.readPoint(); | |
369 const SkPoint c2 = buffer.readPoint(); | |
370 const SkScalar r1 = buffer.readScalar(); | |
371 const SkScalar r2 = buffer.readScalar(); | |
372 return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors,
desc.fPos, | |
373 desc.fCount, desc.fTileMode, d
esc.fGradFlags, | |
374 desc.fLocalMatrix); | |
375 } | |
376 | |
377 void SkTwoPointRadialGradient::flatten( | |
378 SkWriteBuffer& buffer) const { | |
379 this->INHERITED::flatten(buffer); | |
380 buffer.writePoint(fCenter1); | |
381 buffer.writePoint(fCenter2); | |
382 buffer.writeScalar(fRadius1); | |
383 buffer.writeScalar(fRadius2); | |
384 } | |
385 | |
386 ///////////////////////////////////////////////////////////////////// | |
387 | |
388 #if SK_SUPPORT_GPU | |
389 | |
390 #include "SkGr.h" | |
391 #include "gl/builders/GrGLProgramBuilder.h" | |
392 | |
393 // For brevity | |
394 typedef GrGLProgramDataManager::UniformHandle UniformHandle; | |
395 | |
396 class GrGLRadial2Gradient : public GrGLGradientEffect { | |
397 | |
398 public: | |
399 | |
400 GrGLRadial2Gradient(const GrProcessor&); | |
401 virtual ~GrGLRadial2Gradient() { } | |
402 | |
403 virtual void emitCode(GrGLFPBuilder*, | |
404 const GrFragmentProcessor&, | |
405 const char* outputColor, | |
406 const char* inputColor, | |
407 const TransformedCoordsArray&, | |
408 const TextureSamplerArray&) override; | |
409 void setData(const GrGLProgramDataManager&, const GrProcessor&) override; | |
410 | |
411 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKe
yBuilder* b); | |
412 | |
413 protected: | |
414 | |
415 UniformHandle fParamUni; | |
416 | |
417 const char* fVSVaryingName; | |
418 const char* fFSVaryingName; | |
419 | |
420 bool fIsDegenerate; | |
421 | |
422 // @{ | |
423 /// Values last uploaded as uniforms | |
424 | |
425 SkScalar fCachedCenter; | |
426 SkScalar fCachedRadius; | |
427 bool fCachedPosRoot; | |
428 | |
429 // @} | |
430 | |
431 private: | |
432 | |
433 typedef GrGLGradientEffect INHERITED; | |
434 | |
435 }; | |
436 | |
437 ///////////////////////////////////////////////////////////////////// | |
438 | |
439 class GrRadial2Gradient : public GrGradientEffect { | |
440 public: | |
441 static GrFragmentProcessor* Create(GrContext* ctx, | |
442 const SkTwoPointRadialGradient& shader, | |
443 const SkMatrix& matrix, | |
444 SkShader::TileMode tm) { | |
445 return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)); | |
446 } | |
447 | |
448 virtual ~GrRadial2Gradient() { } | |
449 | |
450 const char* name() const override { return "Two-Point Radial Gradient"; } | |
451 | |
452 virtual void getGLProcessorKey(const GrGLSLCaps& caps, | |
453 GrProcessorKeyBuilder* b) const override { | |
454 GrGLRadial2Gradient::GenKey(*this, caps, b); | |
455 } | |
456 | |
457 GrGLFragmentProcessor* createGLInstance() const override { | |
458 return SkNEW_ARGS(GrGLRadial2Gradient, (*this)); | |
459 } | |
460 | |
461 // The radial gradient parameters can collapse to a linear (instead of quadr
atic) equation. | |
462 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; } | |
463 SkScalar center() const { return fCenterX1; } | |
464 SkScalar radius() const { return fRadius0; } | |
465 bool isPosRoot() const { return SkToBool(fPosRoot); } | |
466 | |
467 private: | |
468 bool onIsEqual(const GrFragmentProcessor& sBase) const override { | |
469 const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>(); | |
470 return (INHERITED::onIsEqual(sBase) && | |
471 this->fCenterX1 == s.fCenterX1 && | |
472 this->fRadius0 == s.fRadius0 && | |
473 this->fPosRoot == s.fPosRoot); | |
474 } | |
475 | |
476 GrRadial2Gradient(GrContext* ctx, | |
477 const SkTwoPointRadialGradient& shader, | |
478 const SkMatrix& matrix, | |
479 SkShader::TileMode tm) | |
480 : INHERITED(ctx, shader, matrix, tm) | |
481 , fCenterX1(shader.getCenterX1()) | |
482 , fRadius0(shader.getStartRadius()) | |
483 , fPosRoot(shader.getDiffRadius() < 0) { | |
484 this->initClassID<GrRadial2Gradient>(); | |
485 // We pass the linear part of the quadratic as a varying. | |
486 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z) | |
487 fBTransform = this->getCoordTransform(); | |
488 SkMatrix& bMatrix = *fBTransform.accessMatrix(); | |
489 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatr
ix::kMScaleX]) - | |
490 SkScalarMul(fRadius0, bMatrix[SkMatri
x::kMPersp0])); | |
491 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatri
x::kMSkewX]) - | |
492 SkScalarMul(fRadius0, bMatrix[SkMatrix
::kMPersp1])); | |
493 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatr
ix::kMTransX]) - | |
494 SkScalarMul(fRadius0, bMatrix[SkMatri
x::kMPersp2])); | |
495 this->addCoordTransform(&fBTransform); | |
496 } | |
497 | |
498 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; | |
499 | |
500 // @{ | |
501 // Cache of values - these can change arbitrarily, EXCEPT | |
502 // we shouldn't change between degenerate and non-degenerate?! | |
503 | |
504 GrCoordTransform fBTransform; | |
505 SkScalar fCenterX1; | |
506 SkScalar fRadius0; | |
507 SkBool8 fPosRoot; | |
508 | |
509 // @} | |
510 | |
511 typedef GrGradientEffect INHERITED; | |
512 }; | |
513 | |
514 ///////////////////////////////////////////////////////////////////// | |
515 | |
516 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient); | |
517 | |
518 GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random, | |
519 GrContext* context, | |
520 const GrDrawTargetCaps&, | |
521 GrTexture**) { | |
522 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; | |
523 SkScalar radius1 = random->nextUScalar1(); | |
524 SkPoint center2; | |
525 SkScalar radius2; | |
526 do { | |
527 center2.set(random->nextUScalar1(), random->nextUScalar1()); | |
528 radius2 = random->nextUScalar1 (); | |
529 // There is a bug in two point radial gradients with identical radii | |
530 } while (radius1 == radius2); | |
531 | |
532 SkColor colors[kMaxRandomGradientColors]; | |
533 SkScalar stopsArray[kMaxRandomGradientColors]; | |
534 SkScalar* stops = stopsArray; | |
535 SkShader::TileMode tm; | |
536 int colorCount = RandomGradientParams(random, colors, &stops, &tm); | |
537 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1
, radius1, | |
538 center2
, radius2, | |
539 colors,
stops, colorCount, | |
540 tm)); | |
541 SkPaint paint; | |
542 GrFragmentProcessor* fp; | |
543 GrColor paintColor; | |
544 SkAssertResult(shader->asFragmentProcessor(context, paint, | |
545 GrTest::TestMatrix(random), NULL, | |
546 &paintColor, &fp)); | |
547 return fp; | |
548 } | |
549 | |
550 ///////////////////////////////////////////////////////////////////// | |
551 | |
552 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrProcessor& processor) | |
553 : fVSVaryingName(NULL) | |
554 , fFSVaryingName(NULL) | |
555 , fCachedCenter(SK_ScalarMax) | |
556 , fCachedRadius(-SK_ScalarMax) | |
557 , fCachedPosRoot(0) { | |
558 | |
559 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>(); | |
560 fIsDegenerate = data.isDegenerate(); | |
561 } | |
562 | |
563 void GrGLRadial2Gradient::emitCode(GrGLFPBuilder* builder, | |
564 const GrFragmentProcessor& fp, | |
565 const char* outputColor, | |
566 const char* inputColor, | |
567 const TransformedCoordsArray& coords, | |
568 const TextureSamplerArray& samplers) { | |
569 const GrRadial2Gradient& ge = fp.cast<GrRadial2Gradient>(); | |
570 this->emitUniforms(builder, ge); | |
571 fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibilit
y, | |
572 kFloat_GrSLType, kDefault_GrSLPrecision
, | |
573 "Radial2FSParams", 6); | |
574 | |
575 SkString cName("c"); | |
576 SkString ac4Name("ac4"); | |
577 SkString rootName("root"); | |
578 SkString t; | |
579 SkString p0; | |
580 SkString p1; | |
581 SkString p2; | |
582 SkString p3; | |
583 SkString p4; | |
584 SkString p5; | |
585 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); | |
586 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); | |
587 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); | |
588 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); | |
589 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); | |
590 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); | |
591 | |
592 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); | |
593 // We interpolate the linear component in coords[1]. | |
594 SkASSERT(coords[0].getType() == coords[1].getType()); | |
595 const char* coords2D; | |
596 SkString bVar; | |
597 if (kVec3f_GrSLType == coords[0].getType()) { | |
598 fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\
n", | |
599 coords[0].c_str(), coords[1].c_str(), coords[0].c
_str()); | |
600 coords2D = "interpolants.xy"; | |
601 bVar = "interpolants.z"; | |
602 } else { | |
603 coords2D = coords[0].c_str(); | |
604 bVar.printf("%s.x", coords[1].c_str()); | |
605 } | |
606 | |
607 // c = (x^2)+(y^2) - params[4] | |
608 fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", | |
609 cName.c_str(), coords2D, coords2D, p4.c_str()); | |
610 | |
611 // If we aren't degenerate, emit some extra code, and accept a slightly | |
612 // more complex coord. | |
613 if (!fIsDegenerate) { | |
614 | |
615 // ac4 = 4.0 * params[0] * c | |
616 fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n", | |
617 ac4Name.c_str(), p0.c_str(), | |
618 cName.c_str()); | |
619 | |
620 // root = sqrt(b^2-4ac) | |
621 // (abs to avoid exception due to fp precision) | |
622 fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", | |
623 rootName.c_str(), bVar.c_str(), bVar.c_str(), | |
624 ac4Name.c_str()); | |
625 | |
626 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] | |
627 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), | |
628 rootName.c_str(), p1.c_str()); | |
629 } else { | |
630 // t is: -c/b | |
631 t.printf("-%s / %s", cName.c_str(), bVar.c_str()); | |
632 } | |
633 | |
634 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers); | |
635 } | |
636 | |
637 void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman, | |
638 const GrProcessor& processor) { | |
639 INHERITED::setData(pdman, processor); | |
640 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>(); | |
641 SkASSERT(data.isDegenerate() == fIsDegenerate); | |
642 SkScalar centerX1 = data.center(); | |
643 SkScalar radius0 = data.radius(); | |
644 if (fCachedCenter != centerX1 || | |
645 fCachedRadius != radius0 || | |
646 fCachedPosRoot != data.isPosRoot()) { | |
647 | |
648 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1; | |
649 | |
650 // When we're in the degenerate (linear) case, the second | |
651 // value will be INF but the program doesn't read it. (We | |
652 // use the same 6 uniforms even though we don't need them | |
653 // all in the linear case just to keep the code complexity | |
654 // down). | |
655 float values[6] = { | |
656 SkScalarToFloat(a), | |
657 1 / (2.f * SkScalarToFloat(a)), | |
658 SkScalarToFloat(centerX1), | |
659 SkScalarToFloat(radius0), | |
660 SkScalarToFloat(SkScalarMul(radius0, radius0)), | |
661 data.isPosRoot() ? 1.f : -1.f | |
662 }; | |
663 | |
664 pdman.set1fv(fParamUni, 6, values); | |
665 fCachedCenter = centerX1; | |
666 fCachedRadius = radius0; | |
667 fCachedPosRoot = data.isPosRoot(); | |
668 } | |
669 } | |
670 | |
671 void GrGLRadial2Gradient::GenKey(const GrProcessor& processor, | |
672 const GrGLSLCaps&, GrProcessorKeyBuilder* b) { | |
673 uint32_t* key = b->add32n(2); | |
674 key[0] = GenBaseGradientKey(processor); | |
675 key[1] = processor.cast<GrRadial2Gradient>().isDegenerate(); | |
676 } | |
677 | |
678 ///////////////////////////////////////////////////////////////////// | |
679 | |
680 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkP
aint& paint, | |
681 const SkMatrix&, | |
682 const SkMatrix* localMatrix,
GrColor* paintColor, | |
683 GrFragmentProcessor** fp) co
nst { | |
684 SkASSERT(context); | |
685 | |
686 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is
on x axis. | |
687 SkMatrix matrix; | |
688 if (!this->getLocalMatrix().invert(&matrix)) { | |
689 return false; | |
690 } | |
691 if (localMatrix) { | |
692 SkMatrix inv; | |
693 if (!localMatrix->invert(&inv)) { | |
694 return false; | |
695 } | |
696 matrix.postConcat(inv); | |
697 } | |
698 matrix.postConcat(fPtsToUnit); | |
699 | |
700 SkScalar diffLen = fDiff.length(); | |
701 if (0 != diffLen) { | |
702 SkScalar invDiffLen = SkScalarInvert(diffLen); | |
703 SkMatrix rot; | |
704 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY), | |
705 SkScalarMul(invDiffLen, fDiff.fX)); | |
706 matrix.postConcat(rot); | |
707 } | |
708 | |
709 *paintColor = SkColor2GrColorJustAlpha(paint.getColor()); | |
710 *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode); | |
711 | |
712 return true; | |
713 } | |
714 | |
715 #else | |
716 | |
717 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, c
onst SkMatrix&, | |
718 const SkMatrix*, | |
719 GrColor*, GrFragmentProcessor
**) const { | |
720 SkDEBUGFAIL("Should not call in GPU-less build"); | |
721 return false; | |
722 } | |
723 | |
724 #endif | |
OLD | NEW |