1589708bf7c706348b763e8277004cb160b202bdbrileya@google.com/*
2589708bf7c706348b763e8277004cb160b202bdbrileya@google.com * Copyright 2012 Google Inc.
3589708bf7c706348b763e8277004cb160b202bdbrileya@google.com *
4589708bf7c706348b763e8277004cb160b202bdbrileya@google.com * Use of this source code is governed by a BSD-style license that can be
5589708bf7c706348b763e8277004cb160b202bdbrileya@google.com * found in the LICENSE file.
6589708bf7c706348b763e8277004cb160b202bdbrileya@google.com */
7589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
8589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#include "SkTwoPointConicalGradient.h"
9aa64fbfd349789f27b3cc35c1968d9dc0612cf8ecommit-bot@chromium.org#include "SkTwoPointConicalGradient_gpu.h"
10aa64fbfd349789f27b3cc35c1968d9dc0612cf8ecommit-bot@chromium.org
1187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgstruct TwoPtRadialContext {
1287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    const TwoPtRadial&  fRec;
1387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    float               fRelX, fRelY;
1487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    const float         fIncX, fIncY;
1587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    float               fB;
1687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    const float         fDB;
1787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
1887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
1987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org                       SkScalar dfx, SkScalar dfy);
2087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    SkFixed nextT();
2187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org};
2287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
23589708bf7c706348b763e8277004cb160b202bdbrileya@google.comstatic int valid_divide(float numer, float denom, float* ratio) {
24589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkASSERT(ratio);
25589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (0 == denom) {
26589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        return 0;
27589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
28589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    *ratio = numer / denom;
29589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return 1;
30589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
31589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
32589708bf7c706348b763e8277004cb160b202bdbrileya@google.com// Return the number of distinct real roots, and write them into roots[] in
33589708bf7c706348b763e8277004cb160b202bdbrileya@google.com// ascending order
3444d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.orgstatic int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) {
35589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkASSERT(roots);
36fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
37589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (A == 0) {
38589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        return valid_divide(-C, B, roots);
39589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
40fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
41589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    float R = B*B - 4*A*C;
42589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (R < 0) {
43589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        return 0;
44589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
45589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    R = sk_float_sqrt(R);
46589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
47589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#if 1
48589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    float Q = B;
49589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (Q < 0) {
50589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        Q -= R;
51589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    } else {
52589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        Q += R;
53589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
54589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#else
55589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    // on 10.6 this was much slower than the above branch :(
56589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    float Q = B + copysignf(R, B);
57589708bf7c706348b763e8277004cb160b202bdbrileya@google.com#endif
58589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    Q *= -0.5f;
59589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (0 == Q) {
60589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        roots[0] = 0;
61589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        return 1;
62589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
63589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
64589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    float r0 = Q / A;
65589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    float r1 = C / Q;
66589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    roots[0] = r0 < r1 ? r0 : r1;
67589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    roots[1] = r0 > r1 ? r0 : r1;
6844d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    if (descendingOrder) {
6944d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        SkTSwap(roots[0], roots[1]);
7044d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    }
71589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return 2;
72589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
73589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
74589708bf7c706348b763e8277004cb160b202bdbrileya@google.comstatic float lerp(float x, float dx, float t) {
75589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return x + t * dx;
76589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
77589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
78589708bf7c706348b763e8277004cb160b202bdbrileya@google.comstatic float sqr(float x) { return x * x; }
79589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
80589708bf7c706348b763e8277004cb160b202bdbrileya@google.comvoid TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
8144d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org                       const SkPoint& center1, SkScalar rad1,
8244d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org                       bool flipped) {
83589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fCenterX = SkScalarToFloat(center0.fX);
84589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fCenterY = SkScalarToFloat(center0.fY);
85589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
86589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
87589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRadius = SkScalarToFloat(rad0);
88589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fDRadius = SkScalarToFloat(rad1) - fRadius;
89589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
90589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
91589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRadius2 = sqr(fRadius);
92589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRDR = fRadius * fDRadius;
9344d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org
9444d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    fFlipped = flipped;
95589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
96589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
9787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgTwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
9887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org                                       SkScalar dfx, SkScalar dfy)
9987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    : fRec(rec)
10087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    , fRelX(SkScalarToFloat(fx) - rec.fCenterX)
10187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    , fRelY(SkScalarToFloat(fy) - rec.fCenterY)
10287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    , fIncX(SkScalarToFloat(dfx))
10387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    , fIncY(SkScalarToFloat(dfy))
10487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR))
10587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {}
10687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
10787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgSkFixed TwoPtRadialContext::nextT() {
108589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    float roots[2];
109fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
11087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
11187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped);
112589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
113589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRelX += fIncX;
114589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRelY += fIncY;
115589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fB += fDB;
116589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
117589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (0 == countRoots) {
11887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        return TwoPtRadial::kDontDrawT;
119589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
120589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
121589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    // Prefer the bigger t value if both give a radius(t) > 0
122589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    // find_quad_roots returns the values sorted, so we start with the last
123589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    float t = roots[countRoots - 1];
12487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    float r = lerp(fRec.fRadius, fRec.fDRadius, t);
125589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (r <= 0) {
126589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        t = roots[0];   // might be the same as roots[countRoots-1]
12787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        r = lerp(fRec.fRadius, fRec.fDRadius, t);
128589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if (r <= 0) {
12987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org            return TwoPtRadial::kDontDrawT;
130589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
131589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
132589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return SkFloatToFixed(t);
133589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
134589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
13587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgtypedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC,
13660040292be58ac553298209fb2e0684a4cb17dccreed@google.com                                    const SkPMColor* cache, int toggle, int count);
137589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
13887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgstatic void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
13960040292be58ac553298209fb2e0684a4cb17dccreed@google.com                           const SkPMColor* SK_RESTRICT cache, int toggle,
14060040292be58ac553298209fb2e0684a4cb17dccreed@google.com                           int count) {
141589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    for (; count > 0; --count) {
142589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkFixed t = rec->nextT();
143589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if (TwoPtRadial::DontDrawT(t)) {
144589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            *dstC++ = 0;
145589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } else {
146589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkFixed index = SkClampMax(t, 0xFFFF);
147589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkASSERT(index <= 0xFFFF);
14860040292be58ac553298209fb2e0684a4cb17dccreed@google.com            *dstC++ = cache[toggle +
14960040292be58ac553298209fb2e0684a4cb17dccreed@google.com                            (index >> SkGradientShaderBase::kCache32Shift)];
150589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
15160040292be58ac553298209fb2e0684a4cb17dccreed@google.com        toggle = next_dither_toggle(toggle);
152589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
153589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
154589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
15587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgstatic void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
15660040292be58ac553298209fb2e0684a4cb17dccreed@google.com                            const SkPMColor* SK_RESTRICT cache, int toggle,
15760040292be58ac553298209fb2e0684a4cb17dccreed@google.com                            int count) {
158589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    for (; count > 0; --count) {
159589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkFixed t = rec->nextT();
160589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if (TwoPtRadial::DontDrawT(t)) {
161589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            *dstC++ = 0;
162589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } else {
163589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkFixed index = repeat_tileproc(t);
164589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkASSERT(index <= 0xFFFF);
16560040292be58ac553298209fb2e0684a4cb17dccreed@google.com            *dstC++ = cache[toggle +
16660040292be58ac553298209fb2e0684a4cb17dccreed@google.com                            (index >> SkGradientShaderBase::kCache32Shift)];
167589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
16860040292be58ac553298209fb2e0684a4cb17dccreed@google.com        toggle = next_dither_toggle(toggle);
169589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
170589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
171589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
17287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgstatic void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
17360040292be58ac553298209fb2e0684a4cb17dccreed@google.com                            const SkPMColor* SK_RESTRICT cache, int toggle,
17460040292be58ac553298209fb2e0684a4cb17dccreed@google.com                            int count) {
175589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    for (; count > 0; --count) {
176589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkFixed t = rec->nextT();
177589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if (TwoPtRadial::DontDrawT(t)) {
178589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            *dstC++ = 0;
179589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } else {
180589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkFixed index = mirror_tileproc(t);
181589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkASSERT(index <= 0xFFFF);
18260040292be58ac553298209fb2e0684a4cb17dccreed@google.com            *dstC++ = cache[toggle +
18360040292be58ac553298209fb2e0684a4cb17dccreed@google.com                            (index >> SkGradientShaderBase::kCache32Shift)];
184589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
18560040292be58ac553298209fb2e0684a4cb17dccreed@google.com        toggle = next_dither_toggle(toggle);
186589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
187589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
188589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
189589708bf7c706348b763e8277004cb160b202bdbrileya@google.comvoid SkTwoPointConicalGradient::init() {
19044d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad);
191589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fPtsToUnit.reset();
192589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
193589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
19498e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com/////////////////////////////////////////////////////////////////////
19598e8b6de04e4c20451fbe3353645e3e384a76550rileya@google.com
196589708bf7c706348b763e8277004cb160b202bdbrileya@google.comSkTwoPointConicalGradient::SkTwoPointConicalGradient(
1973d3a860d0ba878adb905512a45c500a67532b0a3reed@google.com        const SkPoint& start, SkScalar startRadius,
1983d3a860d0ba878adb905512a45c500a67532b0a3reed@google.com        const SkPoint& end, SkScalar endRadius,
1999c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org        bool flippedGrad, const Descriptor& desc,
2009c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org        const SkMatrix* localMatrix)
2019c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org    : SkGradientShaderBase(desc, localMatrix),
202589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fCenter1(start),
203589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fCenter2(end),
204589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRadius1(startRadius),
20544d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    fRadius2(endRadius),
20644d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    fFlippedGrad(flippedGrad) {
207589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    // this is degenerate, and should be caught by our caller
208589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
209589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    this->init();
210589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
211589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
2123fbab82bd30158ccabc708a20419025923e08655commit-bot@chromium.orgbool SkTwoPointConicalGradient::isOpaque() const {
213cb6d97ca718c0335dde678bb64169f6de70b62d6robertphillips@google.com    // Because areas outside the cone are left untouched, we cannot treat the
214cb6d97ca718c0335dde678bb64169f6de70b62d6robertphillips@google.com    // shader as opaque even if the gradient itself is opaque.
215cb6d97ca718c0335dde678bb64169f6de70b62d6robertphillips@google.com    // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
216cb6d97ca718c0335dde678bb64169f6de70b62d6robertphillips@google.com    return false;
2173fbab82bd30158ccabc708a20419025923e08655commit-bot@chromium.org}
2183fbab82bd30158ccabc708a20419025923e08655commit-bot@chromium.org
21987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgsize_t SkTwoPointConicalGradient::contextSize() const {
22087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    return sizeof(TwoPointConicalGradientContext);
22187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org}
22287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
223ce56d965069c1649afe14319cb239e6ad670682acommit-bot@chromium.orgSkShader::Context* SkTwoPointConicalGradient::onCreateContext(const ContextRec& rec,
224ce56d965069c1649afe14319cb239e6ad670682acommit-bot@chromium.org                                                              void* storage) const {
225e901b6de3ef8dea842008a08fc81e92fb1478d61commit-bot@chromium.org    return SkNEW_PLACEMENT_ARGS(storage, TwoPointConicalGradientContext, (*this, rec));
22687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org}
22787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
22887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgSkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
229e901b6de3ef8dea842008a08fc81e92fb1478d61commit-bot@chromium.org        const SkTwoPointConicalGradient& shader, const ContextRec& rec)
230e901b6de3ef8dea842008a08fc81e92fb1478d61commit-bot@chromium.org    : INHERITED(shader, rec)
23187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org{
23287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    // we don't have a span16 proc
23387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    fFlags &= ~kHasSpan16_Flag;
23487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
23587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    // in general, we might discard based on computed-radius, so clear
23687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    // this flag (todo: sometimes we can detect that we never discard...)
23787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    fFlags &= ~kOpaqueAlpha_Flag;
23887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org}
23987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
24087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgvoid SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
24187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        int x, int y, SkPMColor* dstCParam, int count) {
24287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    const SkTwoPointConicalGradient& twoPointConicalGradient =
24387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org            static_cast<const SkTwoPointConicalGradient&>(fShader);
24487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org
24560040292be58ac553298209fb2e0684a4cb17dccreed@google.com    int toggle = init_dither_toggle(x, y);
24660040292be58ac553298209fb2e0684a4cb17dccreed@google.com
247589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkASSERT(count > 0);
248fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
249589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkPMColor* SK_RESTRICT dstC = dstCParam;
250fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
251589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
252100abf49e10544bc4f436bf1f38e6929779621f4bsalomon@google.com
25387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
254589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
25560040292be58ac553298209fb2e0684a4cb17dccreed@google.com    TwoPointConicalProc shadeProc = twopoint_repeat;
25687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
257589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        shadeProc = twopoint_clamp;
25887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org    } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
259589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        shadeProc = twopoint_mirror;
260589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    } else {
26187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
262589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
263fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
264589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (fDstToIndexClass != kPerspective_MatrixClass) {
265589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkPoint srcPt;
266589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
267589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
268589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkScalar dx, fx = srcPt.fX;
269589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        SkScalar dy, fy = srcPt.fY;
270fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
271589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
272589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkFixed fixedX, fixedY;
273589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
274589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            dx = SkFixedToScalar(fixedX);
275589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            dy = SkFixedToScalar(fixedY);
276589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } else {
277589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
278589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            dx = fDstToIndex.getScaleX();
279589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            dy = fDstToIndex.getSkewY();
280589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
281589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
28287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
28387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org        (*shadeProc)(&rec, dstC, cache, toggle, count);
284589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    } else {    // perspective case
285139a2359abdeedca1ff836aa24c1a6943d9274f8mike@reedtribe.org        SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
286139a2359abdeedca1ff836aa24c1a6943d9274f8mike@reedtribe.org        SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
287589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        for (; count > 0; --count) {
288589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkPoint srcPt;
289589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            dstProc(fDstToIndex, dstX, dstY, &srcPt);
29087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org            TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
29187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org            (*shadeProc)(&rec, dstC, cache, toggle, 1);
292139a2359abdeedca1ff836aa24c1a6943d9274f8mike@reedtribe.org
293139a2359abdeedca1ff836aa24c1a6943d9274f8mike@reedtribe.org            dstX += SK_Scalar1;
29460040292be58ac553298209fb2e0684a4cb17dccreed@google.com            toggle = next_dither_toggle(toggle);
295139a2359abdeedca1ff836aa24c1a6943d9274f8mike@reedtribe.org            dstC += 1;
296589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
297589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
298589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
299589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
300589708bf7c706348b763e8277004cb160b202bdbrileya@google.comSkShader::BitmapType SkTwoPointConicalGradient::asABitmap(
301589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const {
302589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkPoint diff = fCenter2 - fCenter1;
303589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    SkScalar diffLen = 0;
304589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
305589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (bitmap) {
3061c6d64b78b24083ee9fd7411dac8a4a7e2d03a3crileya@google.com        this->getGradientTableBitmap(bitmap);
307589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
308589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (matrix) {
309589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        diffLen = diff.length();
310589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
311589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (matrix) {
312589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        if (diffLen) {
313589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            SkScalar invDiffLen = SkScalarInvert(diffLen);
314589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            // rotate to align circle centers with the x-axis
315589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
316589708bf7c706348b763e8277004cb160b202bdbrileya@google.com                              SkScalarMul(invDiffLen, diff.fX));
317589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        } else {
318589708bf7c706348b763e8277004cb160b202bdbrileya@google.com            matrix->reset();
319589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        }
320589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        matrix->preTranslate(-fCenter1.fX, -fCenter1.fY);
321589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
322589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (xy) {
323589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        xy[0] = fTileMode;
324589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        xy[1] = kClamp_TileMode;
325589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
326589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return kTwoPointConical_BitmapType;
327589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
328589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
32944d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org// Returns the original non-sorted version of the gradient
330589708bf7c706348b763e8277004cb160b202bdbrileya@google.comSkShader::GradientType SkTwoPointConicalGradient::asAGradient(
331589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    GradientInfo* info) const {
332589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    if (info) {
33344d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        commonAsAGradient(info, fFlippedGrad);
334589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        info->fPoint[0] = fCenter1;
335589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        info->fPoint[1] = fCenter2;
336589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        info->fRadius[0] = fRadius1;
337589708bf7c706348b763e8277004cb160b202bdbrileya@google.com        info->fRadius[1] = fRadius2;
33844d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        if (fFlippedGrad) {
33944d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org            SkTSwap(info->fPoint[0], info->fPoint[1]);
34044d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org            SkTSwap(info->fRadius[0], info->fRadius[1]);
34144d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        }
342589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    }
343589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    return kConical_GradientType;
344589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
345589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
346589708bf7c706348b763e8277004cb160b202bdbrileya@google.comSkTwoPointConicalGradient::SkTwoPointConicalGradient(
3478b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org    SkReadBuffer& buffer)
348589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    : INHERITED(buffer),
349589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fCenter1(buffer.readPoint()),
350589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fCenter2(buffer.readPoint()),
351589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRadius1(buffer.readScalar()),
352589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    fRadius2(buffer.readScalar()) {
3537ed173b1ebac84671fb0dc1b9bd323a5e6e63771commit-bot@chromium.org    if (buffer.isVersionLT(SkReadBuffer::kGradientFlippedFlag_Version)) {
35444d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        // V23_COMPATIBILITY_CODE
35544d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        // Sort gradient by radius size for old pictures
35644d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        if (fRadius2 < fRadius1) {
35744d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org            SkTSwap(fCenter1, fCenter2);
35844d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org            SkTSwap(fRadius1, fRadius2);
35944d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org            this->flipGradientColors();
36044d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org            fFlippedGrad = true;
36144d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        } else {
36244d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org            fFlippedGrad = false;
36344d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org        }
3647ed173b1ebac84671fb0dc1b9bd323a5e6e63771commit-bot@chromium.org    } else {
3657ed173b1ebac84671fb0dc1b9bd323a5e6e63771commit-bot@chromium.org        fFlippedGrad = buffer.readBool();
36644d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    }
367589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    this->init();
368589708bf7c706348b763e8277004cb160b202bdbrileya@google.com};
369589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
370589708bf7c706348b763e8277004cb160b202bdbrileya@google.comvoid SkTwoPointConicalGradient::flatten(
3718b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org    SkWriteBuffer& buffer) const {
372589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    this->INHERITED::flatten(buffer);
373589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    buffer.writePoint(fCenter1);
374589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    buffer.writePoint(fCenter2);
375589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    buffer.writeScalar(fRadius1);
376589708bf7c706348b763e8277004cb160b202bdbrileya@google.com    buffer.writeScalar(fRadius2);
37744d83c1e81b0555efa94f78e2a53b862208cdd06commit-bot@chromium.org    buffer.writeBool(fFlippedGrad);
378589708bf7c706348b763e8277004cb160b202bdbrileya@google.com}
379589708bf7c706348b763e8277004cb160b202bdbrileya@google.com
380cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com#if SK_SUPPORT_GPU
381cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com
3829de5b514d38c5b36066bcdc14fba2f7e5196d372dandov#include "SkGr.h"
3839de5b514d38c5b36066bcdc14fba2f7e5196d372dandov
3849de5b514d38c5b36066bcdc14fba2f7e5196d372dandovbool SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint& paint,
3859de5b514d38c5b36066bcdc14fba2f7e5196d372dandov                                             const SkMatrix* localMatrix, GrColor* grColor,
3869de5b514d38c5b36066bcdc14fba2f7e5196d372dandov                                             GrEffectRef** grEffect)  const {
38700835cc55046e66b5a33633ec045bc9aa0015ebdbsalomon@google.com    SkASSERT(NULL != context);
388f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com    SkASSERT(fPtsToUnit.isIdentity());
389dfdb7e5240276493077b7c6e1f3cc8b8a0e195babsalomon@google.com
3909de5b514d38c5b36066bcdc14fba2f7e5196d372dandov    *grEffect = Gr2PtConicalGradientEffect::Create(context, *this, fTileMode, localMatrix);
3919de5b514d38c5b36066bcdc14fba2f7e5196d372dandov    *grColor = SkColor2GrColorJustAlpha(paint.getColor());
3929de5b514d38c5b36066bcdc14fba2f7e5196d372dandov    return true;
393d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com}
394d7cc651b8da11d52ae90e910b948f5e2d15daaf9rileya@google.com
395cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com#else
396cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com
3979de5b514d38c5b36066bcdc14fba2f7e5196d372dandovbool SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint& paint,
3989de5b514d38c5b36066bcdc14fba2f7e5196d372dandov                                            const SkMatrix* localMatrix, GrColor* grColor,
3999de5b514d38c5b36066bcdc14fba2f7e5196d372dandov                                            GrEffectRef** grEffect)  const {
400cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com    SkDEBUGFAIL("Should not call in GPU-less build");
4019de5b514d38c5b36066bcdc14fba2f7e5196d372dandov    return false;
402cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com}
403cf8fb1f6f03fc77f9927564f9ef9abeeeec508d2bsalomon@google.com
404a5e65ec434fed44dc616e4f64950b835b541181btwiz@google.com#endif
40576f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
4060f10f7bf1fb43ca6346dc220a076773b1f19a367commit-bot@chromium.org#ifndef SK_IGNORE_TO_STRING
40776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.comvoid SkTwoPointConicalGradient::toString(SkString* str) const {
40876f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append("SkTwoPointConicalGradient: (");
40976f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
41076f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append("center1: (");
41176f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendScalar(fCenter1.fX);
41276f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(", ");
41376f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendScalar(fCenter1.fY);
41476f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(") radius1: ");
41576f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendScalar(fRadius1);
41676f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(" ");
41776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
41876f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append("center2: (");
41976f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendScalar(fCenter2.fX);
42076f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(", ");
42176f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendScalar(fCenter2.fY);
42276f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(") radius2: ");
42376f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->appendScalar(fRadius2);
42476f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(" ");
42576f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
42676f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    this->INHERITED::toString(str);
42776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com
42876f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com    str->append(")");
42976f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com}
43076f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com#endif
431