SkRadialGradient.cpp revision 4eaf9cef5a76098f78efac30beb966ac833d32c2
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 "SkRadialGradient.h"
10#include "SkRadialGradient_Table.h"
11#include "SkNx.h"
12
13#define kSQRT_TABLE_BITS    11
14#define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
15
16SK_COMPILE_ASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, SqrtTableSizesMatch);
17
18#if 0
19
20#include <stdio.h>
21
22void SkRadialGradient_BuildTable() {
23    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
24
25    FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
26    SkASSERT(file);
27    ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
28
29    for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
30        if ((i & 15) == 0) {
31            ::fprintf(file, "\t");
32        }
33
34        uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
35
36        ::fprintf(file, "0x%02X", value);
37        if (i < kSQRT_TABLE_SIZE-1) {
38            ::fprintf(file, ", ");
39        }
40        if ((i & 15) == 15) {
41            ::fprintf(file, "\n");
42        }
43    }
44    ::fprintf(file, "};\n");
45    ::fclose(file);
46}
47
48#endif
49
50namespace {
51
52// GCC doesn't like using static functions as template arguments.  So force these to be non-static.
53inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
54    return mirror_tileproc(x);
55}
56
57inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
58    return repeat_tileproc(x);
59}
60
61SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
62    SkScalar    inv = SkScalarInvert(radius);
63
64    SkMatrix matrix;
65    matrix.setTranslate(-center.fX, -center.fY);
66    matrix.postScale(inv, inv);
67    return matrix;
68}
69
70typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
71        SkScalar sfy, SkScalar sdy,
72        uint16_t* dstC, const uint16_t* cache,
73        int toggle, int count);
74
75void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
76        SkScalar sfy, SkScalar sdy,
77        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
78        int toggle, int count) {
79    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
80
81    /* knock these down so we can pin against +- 0x7FFF, which is an
82       immediate load, rather than 0xFFFF which is slower. This is a
83       compromise, since it reduces our precision, but that appears
84       to be visually OK. If we decide this is OK for all of our cases,
85       we could (it seems) put this scale-down into fDstToIndex,
86       to avoid having to do these extra shifts each time.
87    */
88    SkFixed fx = SkScalarToFixed(sfx) >> 1;
89    SkFixed dx = SkScalarToFixed(sdx) >> 1;
90    SkFixed fy = SkScalarToFixed(sfy) >> 1;
91    SkFixed dy = SkScalarToFixed(sdy) >> 1;
92    // might perform this check for the other modes,
93    // but the win will be a smaller % of the total
94    if (dy == 0) {
95        fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
96        fy *= fy;
97        do {
98            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
99            unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
100            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
101            fx += dx;
102            *dstC++ = cache[toggle +
103                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
104            toggle = next_dither_toggle16(toggle);
105        } while (--count != 0);
106    } else {
107        do {
108            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
109            unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
110            fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
111            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
112            fx += dx;
113            fy += dy;
114            *dstC++ = cache[toggle +
115                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
116            toggle = next_dither_toggle16(toggle);
117        } while (--count != 0);
118    }
119}
120
121template <SkFixed (*TileProc)(SkFixed)>
122void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
123                        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
124                        int toggle, int count) {
125    do {
126        const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
127        const unsigned fi = TileProc(dist);
128        SkASSERT(fi <= 0xFFFF);
129        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
130        toggle = next_dither_toggle16(toggle);
131        fx += dx;
132        fy += dy;
133    } while (--count != 0);
134}
135
136void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
137                               uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
138                               int toggle, int count) {
139    shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
140}
141
142void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
143                               uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
144                               int toggle, int count) {
145    shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
146}
147
148}  // namespace
149
150/////////////////////////////////////////////////////////////////////
151
152SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
153    : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
154    , fCenter(center)
155    , fRadius(radius) {
156}
157
158size_t SkRadialGradient::contextSize() const {
159    return sizeof(RadialGradientContext);
160}
161
162SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
163    return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
164}
165
166SkRadialGradient::RadialGradientContext::RadialGradientContext(
167        const SkRadialGradient& shader, const ContextRec& rec)
168    : INHERITED(shader, rec) {}
169
170void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
171                                                          int count) {
172    SkASSERT(count > 0);
173
174    const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
175
176    uint16_t* SK_RESTRICT dstC = dstCParam;
177
178    SkPoint             srcPt;
179    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
180    TileProc            proc = radialGradient.fTileProc;
181    const uint16_t* SK_RESTRICT cache = fCache->getCache16();
182    int                 toggle = init_dither_toggle16(x, y);
183
184    if (fDstToIndexClass != kPerspective_MatrixClass) {
185        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
186                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
187
188        SkScalar sdx = fDstToIndex.getScaleX();
189        SkScalar sdy = fDstToIndex.getSkewY();
190
191        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
192            SkFixed storage[2];
193            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
194                                           &storage[0], &storage[1]);
195            sdx = SkFixedToScalar(storage[0]);
196            sdy = SkFixedToScalar(storage[1]);
197        } else {
198            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
199        }
200
201        RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
202        if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
203            shadeProc = shadeSpan16_radial_clamp;
204        } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
205            shadeProc = shadeSpan16_radial_mirror;
206        } else {
207            SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
208        }
209        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
210                     cache, toggle, count);
211    } else {    // perspective case
212        SkScalar dstX = SkIntToScalar(x);
213        SkScalar dstY = SkIntToScalar(y);
214        do {
215            dstProc(fDstToIndex, dstX, dstY, &srcPt);
216            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
217            SkASSERT(fi <= 0xFFFF);
218
219            int index = fi >> (16 - kCache16Bits);
220            *dstC++ = cache[toggle + index];
221            toggle = next_dither_toggle16(toggle);
222
223            dstX += SK_Scalar1;
224        } while (--count != 0);
225    }
226}
227
228SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
229    SkMatrix* matrix, SkShader::TileMode* xy) const {
230    if (bitmap) {
231        this->getGradientTableBitmap(bitmap);
232    }
233    if (matrix) {
234        matrix->setScale(SkIntToScalar(kCache32Count),
235                         SkIntToScalar(kCache32Count));
236        matrix->preConcat(fPtsToUnit);
237    }
238    if (xy) {
239        xy[0] = fTileMode;
240        xy[1] = kClamp_TileMode;
241    }
242    return kRadial_BitmapType;
243}
244
245SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
246    if (info) {
247        commonAsAGradient(info);
248        info->fPoint[0] = fCenter;
249        info->fRadius[0] = fRadius;
250    }
251    return kRadial_GradientType;
252}
253
254SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
255    DescriptorScope desc;
256    if (!desc.unflatten(buffer)) {
257        return NULL;
258    }
259    const SkPoint center = buffer.readPoint();
260    const SkScalar radius = buffer.readScalar();
261    return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
262                                          desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
263}
264
265void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
266    this->INHERITED::flatten(buffer);
267    buffer.writePoint(fCenter);
268    buffer.writeScalar(fRadius);
269}
270
271namespace {
272
273inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
274    // fast, overly-conservative test: checks unit square instead of unit circle
275    bool xClamped = (fx >= SK_FixedHalf && dx >= 0) || (fx <= -SK_FixedHalf && dx <= 0);
276    bool yClamped = (fy >= SK_FixedHalf && dy >= 0) || (fy <= -SK_FixedHalf && dy <= 0);
277    return xClamped || yClamped;
278}
279
280inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
281    // fast, overly-conservative test: checks unit square instead of unit circle
282    bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
283    bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
284    return xClamped || yClamped;
285}
286
287// Return true if (fx * fy) is always inside the unit circle
288// SkPin32 is expensive, but so are all the SkFixedMul in this test,
289// so it shouldn't be run if count is small.
290inline bool no_need_for_radial_pin(int fx, int dx,
291                                          int fy, int dy, int count) {
292    SkASSERT(count > 0);
293    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
294        return false;
295    }
296    if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
297        return false;
298    }
299    fx += (count - 1) * dx;
300    fy += (count - 1) * dy;
301    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
302        return false;
303    }
304    return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
305}
306
307#define UNPINNED_RADIAL_STEP \
308    fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
309    *dstC++ = cache[toggle + \
310                    (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
311    toggle = next_dither_toggle(toggle); \
312    fx += dx; \
313    fy += dy;
314
315typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
316        SkScalar sfy, SkScalar sdy,
317        SkPMColor* dstC, const SkPMColor* cache,
318        int count, int toggle);
319
320// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
321void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
322        SkScalar sfy, SkScalar sdy,
323        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
324        int count, int toggle) {
325    // Floating point seems to be slower than fixed point,
326    // even when we have float hardware.
327    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
328    SkFixed fx = SkScalarToFixed(sfx) >> 1;
329    SkFixed dx = SkScalarToFixed(sdx) >> 1;
330    SkFixed fy = SkScalarToFixed(sfy) >> 1;
331    SkFixed dy = SkScalarToFixed(sdy) >> 1;
332    if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
333        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
334        sk_memset32_dither(dstC,
335            cache[toggle + fi],
336            cache[next_dither_toggle(toggle) + fi],
337            count);
338    } else if ((count > 4) &&
339               no_need_for_radial_pin(fx, dx, fy, dy, count)) {
340        unsigned fi;
341        // 4x unroll appears to be no faster than 2x unroll on Linux
342        while (count > 1) {
343            UNPINNED_RADIAL_STEP;
344            UNPINNED_RADIAL_STEP;
345            count -= 2;
346        }
347        if (count) {
348            UNPINNED_RADIAL_STEP;
349        }
350    } else  {
351        // Specializing for dy == 0 gains us 25% on Skia benchmarks
352        if (dy == 0) {
353            unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
354            yy *= yy;
355            do {
356                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
357                unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
358                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
359                *dstC++ = cache[toggle + (sqrt_table[fi] >>
360                    SkGradientShaderBase::kSqrt32Shift)];
361                toggle = next_dither_toggle(toggle);
362                fx += dx;
363            } while (--count != 0);
364        } else {
365            do {
366                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
367                unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
368                fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
369                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
370                *dstC++ = cache[toggle + (sqrt_table[fi] >>
371                    SkGradientShaderBase::kSqrt32Shift)];
372                toggle = next_dither_toggle(toggle);
373                fx += dx;
374                fy += dy;
375            } while (--count != 0);
376        }
377    }
378}
379
380static inline Sk4f fast_sqrt(const Sk4f& R) {
381    // R * R.rsqrt0() is much faster, but it's non-monotonic, which isn't so pretty for gradients.
382    return R * R.rsqrt1();
383}
384
385static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
386    return a * a + b * b;
387}
388
389void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
390                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
391                             int count, int toggle) {
392    if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
393        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
394        sk_memset32_dither(dstC,
395                           cache[toggle + fi],
396                           cache[next_dither_toggle(toggle) + fi],
397                           count);
398    } else {
399        const Sk4f max(255);
400        const float scale = 255;
401        sfx *= scale;
402        sfy *= scale;
403        sdx *= scale;
404        sdy *= scale;
405        const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
406        const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
407        const Sk4f dx4(sdx * 4);
408        const Sk4f dy4(sdy * 4);
409
410        Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
411        Sk4f tmpdxdy = sum_squares(dx4, dy4);
412        Sk4f R = sum_squares(fx4, fy4);
413        Sk4f dR = tmpxy + tmpxy + tmpdxdy;
414        const Sk4f ddR = tmpdxdy + tmpdxdy;
415
416        for (int i = 0; i < (count >> 2); ++i) {
417            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
418            R += dR;
419            dR += ddR;
420
421            int fi[4];
422            dist.castTrunc().store(fi);
423
424            for (int i = 0; i < 4; i++) {
425                *dstC++ = cache[toggle + fi[i]];
426                toggle = next_dither_toggle(toggle);
427            }
428        }
429        count &= 3;
430        if (count) {
431            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
432
433            int fi[4];
434            dist.castTrunc().store(fi);
435            for (int i = 0; i < count; i++) {
436                *dstC++ = cache[toggle + fi[i]];
437                toggle = next_dither_toggle(toggle);
438            }
439        }
440    }
441}
442
443// Unrolling this loop doesn't seem to help (when float); we're stalling to
444// get the results of the sqrt (?), and don't have enough extra registers to
445// have many in flight.
446template <SkFixed (*TileProc)(SkFixed)>
447void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
448                      SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
449                      int count, int toggle) {
450    do {
451        const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
452        const unsigned fi = TileProc(dist);
453        SkASSERT(fi <= 0xFFFF);
454        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
455        toggle = next_dither_toggle(toggle);
456        fx += dx;
457        fy += dy;
458    } while (--count != 0);
459}
460
461void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
462                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
463                             int count, int toggle) {
464    shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
465}
466
467void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
468                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
469                             int count, int toggle) {
470    shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
471}
472
473}  // namespace
474
475void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
476                                                        SkPMColor* SK_RESTRICT dstC, int count) {
477#ifdef SK_SUPPORT_LEGACY_RADIAL_GRADIENT_SQRT
478    const bool use_new_proc = false;
479#else
480    const bool use_new_proc = true;
481#endif
482    SkASSERT(count > 0);
483
484    const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
485
486    SkPoint             srcPt;
487    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
488    TileProc            proc = radialGradient.fTileProc;
489    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
490    int toggle = init_dither_toggle(x, y);
491
492    if (fDstToIndexClass != kPerspective_MatrixClass) {
493        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
494                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
495        SkScalar sdx = fDstToIndex.getScaleX();
496        SkScalar sdy = fDstToIndex.getSkewY();
497
498        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
499            SkFixed storage[2];
500            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
501                                           &storage[0], &storage[1]);
502            sdx = SkFixedToScalar(storage[0]);
503            sdy = SkFixedToScalar(storage[1]);
504        } else {
505            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
506        }
507
508        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
509        if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
510            shadeProc = use_new_proc ? shadeSpan_radial_clamp2 : shadeSpan_radial_clamp;
511        } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
512            shadeProc = shadeSpan_radial_mirror;
513        } else {
514            SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
515        }
516        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
517    } else {    // perspective case
518        SkScalar dstX = SkIntToScalar(x);
519        SkScalar dstY = SkIntToScalar(y);
520        do {
521            dstProc(fDstToIndex, dstX, dstY, &srcPt);
522            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
523            SkASSERT(fi <= 0xFFFF);
524            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
525            dstX += SK_Scalar1;
526        } while (--count != 0);
527    }
528}
529
530/////////////////////////////////////////////////////////////////////
531
532#if SK_SUPPORT_GPU
533
534#include "SkGr.h"
535#include "gl/builders/GrGLProgramBuilder.h"
536
537class GrGLRadialGradient : public GrGLGradientEffect {
538public:
539
540    GrGLRadialGradient(const GrProcessor&) {}
541    virtual ~GrGLRadialGradient() { }
542
543    virtual void emitCode(GrGLFPBuilder*,
544                          const GrFragmentProcessor&,
545                          const char* outputColor,
546                          const char* inputColor,
547                          const TransformedCoordsArray&,
548                          const TextureSamplerArray&) override;
549
550    static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
551        b->add32(GenBaseGradientKey(processor));
552    }
553
554private:
555
556    typedef GrGLGradientEffect INHERITED;
557
558};
559
560/////////////////////////////////////////////////////////////////////
561
562class GrRadialGradient : public GrGradientEffect {
563public:
564    static GrFragmentProcessor* Create(GrContext* ctx,
565                                       const SkRadialGradient& shader,
566                                       const SkMatrix& matrix,
567                                       SkShader::TileMode tm) {
568        return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
569    }
570
571    virtual ~GrRadialGradient() { }
572
573    const char* name() const override { return "Radial Gradient"; }
574
575    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
576                                   GrProcessorKeyBuilder* b) const override {
577        GrGLRadialGradient::GenKey(*this, caps, b);
578    }
579
580    GrGLFragmentProcessor* createGLInstance() const override {
581        return SkNEW_ARGS(GrGLRadialGradient, (*this));
582    }
583
584private:
585    GrRadialGradient(GrContext* ctx,
586                     const SkRadialGradient& shader,
587                     const SkMatrix& matrix,
588                     SkShader::TileMode tm)
589        : INHERITED(ctx, shader, matrix, tm) {
590        this->initClassID<GrRadialGradient>();
591    }
592
593    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
594
595    typedef GrGradientEffect INHERITED;
596};
597
598/////////////////////////////////////////////////////////////////////
599
600GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
601
602GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random,
603                                                  GrContext* context,
604                                                  const GrDrawTargetCaps&,
605                                                  GrTexture**) {
606    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
607    SkScalar radius = random->nextUScalar1();
608
609    SkColor colors[kMaxRandomGradientColors];
610    SkScalar stopsArray[kMaxRandomGradientColors];
611    SkScalar* stops = stopsArray;
612    SkShader::TileMode tm;
613    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
614    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
615                                                                 colors, stops, colorCount,
616                                                                 tm));
617    SkPaint paint;
618    GrColor paintColor;
619    GrFragmentProcessor* fp;
620    SkAssertResult(shader->asFragmentProcessor(context, paint,
621                                               GrTest::TestMatrix(random), NULL,
622                                               &paintColor, &fp));
623    return fp;
624}
625
626/////////////////////////////////////////////////////////////////////
627
628void GrGLRadialGradient::emitCode(GrGLFPBuilder* builder,
629                                  const GrFragmentProcessor& fp,
630                                  const char* outputColor,
631                                  const char* inputColor,
632                                  const TransformedCoordsArray& coords,
633                                  const TextureSamplerArray& samplers) {
634    const GrRadialGradient& ge = fp.cast<GrRadialGradient>();
635    this->emitUniforms(builder, ge);
636    SkString t("length(");
637    t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0));
638    t.append(")");
639    this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
640}
641
642/////////////////////////////////////////////////////////////////////
643
644bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
645                                           const SkMatrix& viewM,
646                                           const SkMatrix* localMatrix, GrColor* paintColor,
647                                           GrFragmentProcessor** fp) const {
648    SkASSERT(context);
649
650    SkMatrix matrix;
651    if (!this->getLocalMatrix().invert(&matrix)) {
652        return false;
653    }
654    if (localMatrix) {
655        SkMatrix inv;
656        if (!localMatrix->invert(&inv)) {
657            return false;
658        }
659        matrix.postConcat(inv);
660    }
661    matrix.postConcat(fPtsToUnit);
662
663    *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
664    *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode);
665
666    return true;
667}
668
669#else
670
671bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
672                                           const SkMatrix*, GrColor*,
673                                           GrFragmentProcessor**) const {
674    SkDEBUGFAIL("Should not call in GPU-less build");
675    return false;
676}
677
678#endif
679
680#ifndef SK_IGNORE_TO_STRING
681void SkRadialGradient::toString(SkString* str) const {
682    str->append("SkRadialGradient: (");
683
684    str->append("center: (");
685    str->appendScalar(fCenter.fX);
686    str->append(", ");
687    str->appendScalar(fCenter.fY);
688    str->append(") radius: ");
689    str->appendScalar(fRadius);
690    str->append(" ");
691
692    this->INHERITED::toString(str);
693
694    str->append(")");
695}
696#endif
697