blob: fd48a6267938edc726956acb9ab6de93e02b4aa4 [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
reed773ceda2016-03-03 18:18:25 -0800213size_t SkTwoPointConicalGradient::onContextSize(const ContextRec&) const {
reeda0cee5f2016-03-04 07:38:11 -0800214 return sizeof(TwoPointConicalGradientContext);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000215}
216
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000217SkShader::Context* SkTwoPointConicalGradient::onCreateContext(const ContextRec& rec,
218 void* storage) const {
halcanary385fe4d2015-08-26 13:07:48 -0700219 return new (storage) TwoPointConicalGradientContext(*this, rec);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000220}
221
222SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000223 const SkTwoPointConicalGradient& shader, const ContextRec& rec)
224 : INHERITED(shader, rec)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000225{
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000226 // in general, we might discard based on computed-radius, so clear
227 // this flag (todo: sometimes we can detect that we never discard...)
228 fFlags &= ~kOpaqueAlpha_Flag;
229}
230
231void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
232 int x, int y, SkPMColor* dstCParam, int count) {
233 const SkTwoPointConicalGradient& twoPointConicalGradient =
234 static_cast<const SkTwoPointConicalGradient&>(fShader);
235
reed@google.com60040292013-02-04 18:21:23 +0000236 int toggle = init_dither_toggle(x, y);
reed@google.com60040292013-02-04 18:21:23 +0000237
rileya@google.com589708b2012-07-26 20:04:23 +0000238 SkASSERT(count > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000239
rileya@google.com589708b2012-07-26 20:04:23 +0000240 SkPMColor* SK_RESTRICT dstC = dstCParam;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000241
rileya@google.com589708b2012-07-26 20:04:23 +0000242 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
bsalomon@google.com100abf42012-09-05 17:40:04 +0000243
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000244 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
rileya@google.com589708b2012-07-26 20:04:23 +0000245
reed@google.com60040292013-02-04 18:21:23 +0000246 TwoPointConicalProc shadeProc = twopoint_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000247 if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000248 shadeProc = twopoint_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000249 } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000250 shadeProc = twopoint_mirror;
251 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000252 SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000253 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000254
rileya@google.com589708b2012-07-26 20:04:23 +0000255 if (fDstToIndexClass != kPerspective_MatrixClass) {
256 SkPoint srcPt;
257 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
258 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
259 SkScalar dx, fx = srcPt.fX;
260 SkScalar dy, fy = srcPt.fY;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000261
rileya@google.com589708b2012-07-26 20:04:23 +0000262 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
benjaminwagner8e175562016-02-16 10:09:40 -0800263 const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
264 dx = step.fX;
265 dy = step.fY;
rileya@google.com589708b2012-07-26 20:04:23 +0000266 } else {
267 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
268 dx = fDstToIndex.getScaleX();
269 dy = fDstToIndex.getSkewY();
270 }
271
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000272 TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
273 (*shadeProc)(&rec, dstC, cache, toggle, count);
rileya@google.com589708b2012-07-26 20:04:23 +0000274 } else { // perspective case
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000275 SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
276 SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
rileya@google.com589708b2012-07-26 20:04:23 +0000277 for (; count > 0; --count) {
278 SkPoint srcPt;
279 dstProc(fDstToIndex, dstX, dstY, &srcPt);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000280 TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
281 (*shadeProc)(&rec, dstC, cache, toggle, 1);
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000282
283 dstX += SK_Scalar1;
reed@google.com60040292013-02-04 18:21:23 +0000284 toggle = next_dither_toggle(toggle);
mike@reedtribe.org139a2352013-11-14 20:15:51 +0000285 dstC += 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000286 }
287 }
288}
289
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000290// Returns the original non-sorted version of the gradient
rileya@google.com589708b2012-07-26 20:04:23 +0000291SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
292 GradientInfo* info) const {
293 if (info) {
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000294 commonAsAGradient(info, fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000295 info->fPoint[0] = fCenter1;
296 info->fPoint[1] = fCenter2;
297 info->fRadius[0] = fRadius1;
298 info->fRadius[1] = fRadius2;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000299 if (fFlippedGrad) {
300 SkTSwap(info->fPoint[0], info->fPoint[1]);
301 SkTSwap(info->fRadius[0], info->fRadius[1]);
302 }
rileya@google.com589708b2012-07-26 20:04:23 +0000303 }
304 return kConical_GradientType;
305}
306
reed60c9b582016-04-03 09:11:13 -0700307sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -0700308 DescriptorScope desc;
309 if (!desc.unflatten(buffer)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700310 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700311 }
312 SkPoint c1 = buffer.readPoint();
313 SkPoint c2 = buffer.readPoint();
314 SkScalar r1 = buffer.readScalar();
315 SkScalar r2 = buffer.readScalar();
316
317 if (buffer.readBool()) { // flipped
318 SkTSwap(c1, c2);
319 SkTSwap(r1, r2);
320
brianosmane25d71c2016-09-28 11:27:28 -0700321 SkColor4f* colors = desc.mutableColors();
reed9fa60da2014-08-21 07:59:51 -0700322 SkScalar* pos = desc.mutablePos();
323 const int last = desc.fCount - 1;
324 const int half = desc.fCount >> 1;
325 for (int i = 0; i < half; ++i) {
326 SkTSwap(colors[i], colors[last - i]);
327 if (pos) {
328 SkScalar tmp = pos[i];
329 pos[i] = SK_Scalar1 - pos[last - i];
330 pos[last - i] = SK_Scalar1 - tmp;
331 }
332 }
333 if (pos) {
334 if (desc.fCount & 1) {
335 pos[half] = SK_Scalar1 - pos[half];
336 }
337 }
338 }
339
brianosmane25d71c2016-09-28 11:27:28 -0700340 return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
341 std::move(desc.fColorSpace), desc.fPos,
reed8a21c9f2016-03-08 18:50:00 -0800342 desc.fCount, desc.fTileMode, desc.fGradFlags,
reed60c9b582016-04-03 09:11:13 -0700343 desc.fLocalMatrix);
reed9fa60da2014-08-21 07:59:51 -0700344}
345
346void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000347 this->INHERITED::flatten(buffer);
348 buffer.writePoint(fCenter1);
349 buffer.writePoint(fCenter2);
350 buffer.writeScalar(fRadius1);
351 buffer.writeScalar(fRadius2);
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000352 buffer.writeBool(fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000353}
354
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000355#if SK_SUPPORT_GPU
356
dandov9de5b512014-06-10 14:38:28 -0700357#include "SkGr.h"
brianosman9557c272016-09-15 06:59:15 -0700358#include "SkTwoPointConicalGradient_gpu.h"
dandov9de5b512014-06-10 14:38:28 -0700359
bungeman06ca8ec2016-06-09 08:01:03 -0700360sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
brianosman1638c0d2016-07-25 05:12:53 -0700361 const AsFPArgs& args) const {
brianosman839345d2016-07-22 11:04:53 -0700362 SkASSERT(args.fContext);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000363 SkASSERT(fPtsToUnit.isIdentity());
brianosmanb9c51372016-09-15 11:09:45 -0700364 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
365 args.fDstColorSpace);
brianosman9557c272016-09-15 06:59:15 -0700366 sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
brianosmanb9c51372016-09-15 11:09:45 -0700367 GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
368 std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
bungeman06ca8ec2016-06-09 08:01:03 -0700369 return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000370}
371
twiz@google.coma5e65ec2012-08-02 15:15:16 +0000372#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000373
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000374#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000375void SkTwoPointConicalGradient::toString(SkString* str) const {
376 str->append("SkTwoPointConicalGradient: (");
377
378 str->append("center1: (");
379 str->appendScalar(fCenter1.fX);
380 str->append(", ");
381 str->appendScalar(fCenter1.fY);
382 str->append(") radius1: ");
383 str->appendScalar(fRadius1);
384 str->append(" ");
385
386 str->append("center2: (");
387 str->appendScalar(fCenter2.fX);
388 str->append(", ");
389 str->appendScalar(fCenter2.fY);
390 str->append(") radius2: ");
391 str->appendScalar(fRadius2);
392 str->append(" ");
393
394 this->INHERITED::toString(str);
395
396 str->append(")");
397}
398#endif