gfx/skia/trunk/src/effects/gradients/SkRadialGradient.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/effects/gradients/SkRadialGradient.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,582 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2012 Google Inc.
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +#include "SkRadialGradient.h"
    1.13 +#include "SkRadialGradient_Table.h"
    1.14 +
    1.15 +#define kSQRT_TABLE_BITS    11
    1.16 +#define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
    1.17 +
    1.18 +#if 0
    1.19 +
    1.20 +#include <stdio.h>
    1.21 +
    1.22 +void SkRadialGradient_BuildTable() {
    1.23 +    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
    1.24 +
    1.25 +    FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
    1.26 +    SkASSERT(file);
    1.27 +    ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
    1.28 +
    1.29 +    for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
    1.30 +        if ((i & 15) == 0) {
    1.31 +            ::fprintf(file, "\t");
    1.32 +        }
    1.33 +
    1.34 +        uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
    1.35 +
    1.36 +        ::fprintf(file, "0x%02X", value);
    1.37 +        if (i < kSQRT_TABLE_SIZE-1) {
    1.38 +            ::fprintf(file, ", ");
    1.39 +        }
    1.40 +        if ((i & 15) == 15) {
    1.41 +            ::fprintf(file, "\n");
    1.42 +        }
    1.43 +    }
    1.44 +    ::fprintf(file, "};\n");
    1.45 +    ::fclose(file);
    1.46 +}
    1.47 +
    1.48 +#endif
    1.49 +
    1.50 +namespace {
    1.51 +
    1.52 +// GCC doesn't like using static functions as template arguments.  So force these to be non-static.
    1.53 +inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
    1.54 +    return mirror_tileproc(x);
    1.55 +}
    1.56 +
    1.57 +inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
    1.58 +    return repeat_tileproc(x);
    1.59 +}
    1.60 +
    1.61 +void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
    1.62 +                               SkMatrix* matrix) {
    1.63 +    SkScalar    inv = SkScalarInvert(radius);
    1.64 +
    1.65 +    matrix->setTranslate(-center.fX, -center.fY);
    1.66 +    matrix->postScale(inv, inv);
    1.67 +}
    1.68 +
    1.69 +typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
    1.70 +        SkScalar sfy, SkScalar sdy,
    1.71 +        uint16_t* dstC, const uint16_t* cache,
    1.72 +        int toggle, int count);
    1.73 +
    1.74 +void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
    1.75 +        SkScalar sfy, SkScalar sdy,
    1.76 +        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
    1.77 +        int toggle, int count) {
    1.78 +    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
    1.79 +
    1.80 +    /* knock these down so we can pin against +- 0x7FFF, which is an
    1.81 +       immediate load, rather than 0xFFFF which is slower. This is a
    1.82 +       compromise, since it reduces our precision, but that appears
    1.83 +       to be visually OK. If we decide this is OK for all of our cases,
    1.84 +       we could (it seems) put this scale-down into fDstToIndex,
    1.85 +       to avoid having to do these extra shifts each time.
    1.86 +    */
    1.87 +    SkFixed fx = SkScalarToFixed(sfx) >> 1;
    1.88 +    SkFixed dx = SkScalarToFixed(sdx) >> 1;
    1.89 +    SkFixed fy = SkScalarToFixed(sfy) >> 1;
    1.90 +    SkFixed dy = SkScalarToFixed(sdy) >> 1;
    1.91 +    // might perform this check for the other modes,
    1.92 +    // but the win will be a smaller % of the total
    1.93 +    if (dy == 0) {
    1.94 +        fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
    1.95 +        fy *= fy;
    1.96 +        do {
    1.97 +            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
    1.98 +            unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
    1.99 +            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
   1.100 +            fx += dx;
   1.101 +            *dstC++ = cache[toggle +
   1.102 +                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
   1.103 +            toggle = next_dither_toggle16(toggle);
   1.104 +        } while (--count != 0);
   1.105 +    } else {
   1.106 +        do {
   1.107 +            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
   1.108 +            unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
   1.109 +            fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
   1.110 +            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
   1.111 +            fx += dx;
   1.112 +            fy += dy;
   1.113 +            *dstC++ = cache[toggle +
   1.114 +                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
   1.115 +            toggle = next_dither_toggle16(toggle);
   1.116 +        } while (--count != 0);
   1.117 +    }
   1.118 +}
   1.119 +
   1.120 +template <SkFixed (*TileProc)(SkFixed)>
   1.121 +void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
   1.122 +                        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
   1.123 +                        int toggle, int count) {
   1.124 +    do {
   1.125 +        const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
   1.126 +        const unsigned fi = TileProc(dist);
   1.127 +        SkASSERT(fi <= 0xFFFF);
   1.128 +        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
   1.129 +        toggle = next_dither_toggle16(toggle);
   1.130 +        fx += dx;
   1.131 +        fy += dy;
   1.132 +    } while (--count != 0);
   1.133 +}
   1.134 +
   1.135 +void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
   1.136 +                               uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
   1.137 +                               int toggle, int count) {
   1.138 +    shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
   1.139 +}
   1.140 +
   1.141 +void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
   1.142 +                               uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
   1.143 +                               int toggle, int count) {
   1.144 +    shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
   1.145 +}
   1.146 +
   1.147 +}  // namespace
   1.148 +
   1.149 +/////////////////////////////////////////////////////////////////////
   1.150 +
   1.151 +SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
   1.152 +                                   const Descriptor& desc)
   1.153 +    : SkGradientShaderBase(desc),
   1.154 +      fCenter(center),
   1.155 +      fRadius(radius)
   1.156 +{
   1.157 +    // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
   1.158 +    SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
   1.159 +
   1.160 +    rad_to_unit_matrix(center, radius, &fPtsToUnit);
   1.161 +}
   1.162 +
   1.163 +void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
   1.164 +                         int count) {
   1.165 +    SkASSERT(count > 0);
   1.166 +
   1.167 +    uint16_t* SK_RESTRICT dstC = dstCParam;
   1.168 +
   1.169 +    SkPoint             srcPt;
   1.170 +    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
   1.171 +    TileProc            proc = fTileProc;
   1.172 +    const uint16_t* SK_RESTRICT cache = this->getCache16();
   1.173 +    int                 toggle = init_dither_toggle16(x, y);
   1.174 +
   1.175 +    if (fDstToIndexClass != kPerspective_MatrixClass) {
   1.176 +        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
   1.177 +                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
   1.178 +
   1.179 +        SkScalar sdx = fDstToIndex.getScaleX();
   1.180 +        SkScalar sdy = fDstToIndex.getSkewY();
   1.181 +
   1.182 +        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
   1.183 +            SkFixed storage[2];
   1.184 +            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
   1.185 +                                           &storage[0], &storage[1]);
   1.186 +            sdx = SkFixedToScalar(storage[0]);
   1.187 +            sdy = SkFixedToScalar(storage[1]);
   1.188 +        } else {
   1.189 +            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
   1.190 +        }
   1.191 +
   1.192 +        RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
   1.193 +        if (SkShader::kClamp_TileMode == fTileMode) {
   1.194 +            shadeProc = shadeSpan16_radial_clamp;
   1.195 +        } else if (SkShader::kMirror_TileMode == fTileMode) {
   1.196 +            shadeProc = shadeSpan16_radial_mirror;
   1.197 +        } else {
   1.198 +            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
   1.199 +        }
   1.200 +        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
   1.201 +                     cache, toggle, count);
   1.202 +    } else {    // perspective case
   1.203 +        SkScalar dstX = SkIntToScalar(x);
   1.204 +        SkScalar dstY = SkIntToScalar(y);
   1.205 +        do {
   1.206 +            dstProc(fDstToIndex, dstX, dstY, &srcPt);
   1.207 +            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
   1.208 +            SkASSERT(fi <= 0xFFFF);
   1.209 +
   1.210 +            int index = fi >> (16 - kCache16Bits);
   1.211 +            *dstC++ = cache[toggle + index];
   1.212 +            toggle = next_dither_toggle16(toggle);
   1.213 +
   1.214 +            dstX += SK_Scalar1;
   1.215 +        } while (--count != 0);
   1.216 +    }
   1.217 +}
   1.218 +
   1.219 +SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
   1.220 +    SkMatrix* matrix, SkShader::TileMode* xy) const {
   1.221 +    if (bitmap) {
   1.222 +        this->getGradientTableBitmap(bitmap);
   1.223 +    }
   1.224 +    if (matrix) {
   1.225 +        matrix->setScale(SkIntToScalar(kCache32Count),
   1.226 +                         SkIntToScalar(kCache32Count));
   1.227 +        matrix->preConcat(fPtsToUnit);
   1.228 +    }
   1.229 +    if (xy) {
   1.230 +        xy[0] = fTileMode;
   1.231 +        xy[1] = kClamp_TileMode;
   1.232 +    }
   1.233 +    return kRadial_BitmapType;
   1.234 +}
   1.235 +
   1.236 +SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
   1.237 +    if (info) {
   1.238 +        commonAsAGradient(info);
   1.239 +        info->fPoint[0] = fCenter;
   1.240 +        info->fRadius[0] = fRadius;
   1.241 +    }
   1.242 +    return kRadial_GradientType;
   1.243 +}
   1.244 +
   1.245 +SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
   1.246 +    : INHERITED(buffer),
   1.247 +      fCenter(buffer.readPoint()),
   1.248 +      fRadius(buffer.readScalar()) {
   1.249 +}
   1.250 +
   1.251 +void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
   1.252 +    this->INHERITED::flatten(buffer);
   1.253 +    buffer.writePoint(fCenter);
   1.254 +    buffer.writeScalar(fRadius);
   1.255 +}
   1.256 +
   1.257 +namespace {
   1.258 +
   1.259 +inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
   1.260 +    // fast, overly-conservative test: checks unit square instead
   1.261 +    // of unit circle
   1.262 +    bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
   1.263 +                    (fx <= -SK_FixedHalf && dx <= 0);
   1.264 +    bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
   1.265 +                    (fy <= -SK_FixedHalf && dy <= 0);
   1.266 +
   1.267 +    return xClamped || yClamped;
   1.268 +}
   1.269 +
   1.270 +// Return true if (fx * fy) is always inside the unit circle
   1.271 +// SkPin32 is expensive, but so are all the SkFixedMul in this test,
   1.272 +// so it shouldn't be run if count is small.
   1.273 +inline bool no_need_for_radial_pin(int fx, int dx,
   1.274 +                                          int fy, int dy, int count) {
   1.275 +    SkASSERT(count > 0);
   1.276 +    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
   1.277 +        return false;
   1.278 +    }
   1.279 +    if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
   1.280 +        return false;
   1.281 +    }
   1.282 +    fx += (count - 1) * dx;
   1.283 +    fy += (count - 1) * dy;
   1.284 +    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
   1.285 +        return false;
   1.286 +    }
   1.287 +    return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
   1.288 +}
   1.289 +
   1.290 +#define UNPINNED_RADIAL_STEP \
   1.291 +    fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
   1.292 +    *dstC++ = cache[toggle + \
   1.293 +                    (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
   1.294 +    toggle = next_dither_toggle(toggle); \
   1.295 +    fx += dx; \
   1.296 +    fy += dy;
   1.297 +
   1.298 +typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
   1.299 +        SkScalar sfy, SkScalar sdy,
   1.300 +        SkPMColor* dstC, const SkPMColor* cache,
   1.301 +        int count, int toggle);
   1.302 +
   1.303 +// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
   1.304 +void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
   1.305 +        SkScalar sfy, SkScalar sdy,
   1.306 +        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.307 +        int count, int toggle) {
   1.308 +    // Floating point seems to be slower than fixed point,
   1.309 +    // even when we have float hardware.
   1.310 +    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
   1.311 +    SkFixed fx = SkScalarToFixed(sfx) >> 1;
   1.312 +    SkFixed dx = SkScalarToFixed(sdx) >> 1;
   1.313 +    SkFixed fy = SkScalarToFixed(sfy) >> 1;
   1.314 +    SkFixed dy = SkScalarToFixed(sdy) >> 1;
   1.315 +    if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
   1.316 +        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
   1.317 +        sk_memset32_dither(dstC,
   1.318 +            cache[toggle + fi],
   1.319 +            cache[next_dither_toggle(toggle) + fi],
   1.320 +            count);
   1.321 +    } else if ((count > 4) &&
   1.322 +               no_need_for_radial_pin(fx, dx, fy, dy, count)) {
   1.323 +        unsigned fi;
   1.324 +        // 4x unroll appears to be no faster than 2x unroll on Linux
   1.325 +        while (count > 1) {
   1.326 +            UNPINNED_RADIAL_STEP;
   1.327 +            UNPINNED_RADIAL_STEP;
   1.328 +            count -= 2;
   1.329 +        }
   1.330 +        if (count) {
   1.331 +            UNPINNED_RADIAL_STEP;
   1.332 +        }
   1.333 +    } else  {
   1.334 +        // Specializing for dy == 0 gains us 25% on Skia benchmarks
   1.335 +        if (dy == 0) {
   1.336 +            unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
   1.337 +            yy *= yy;
   1.338 +            do {
   1.339 +                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
   1.340 +                unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
   1.341 +                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
   1.342 +                *dstC++ = cache[toggle + (sqrt_table[fi] >>
   1.343 +                    SkGradientShaderBase::kSqrt32Shift)];
   1.344 +                toggle = next_dither_toggle(toggle);
   1.345 +                fx += dx;
   1.346 +            } while (--count != 0);
   1.347 +        } else {
   1.348 +            do {
   1.349 +                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
   1.350 +                unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
   1.351 +                fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
   1.352 +                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
   1.353 +                *dstC++ = cache[toggle + (sqrt_table[fi] >>
   1.354 +                    SkGradientShaderBase::kSqrt32Shift)];
   1.355 +                toggle = next_dither_toggle(toggle);
   1.356 +                fx += dx;
   1.357 +                fy += dy;
   1.358 +            } while (--count != 0);
   1.359 +        }
   1.360 +    }
   1.361 +}
   1.362 +
   1.363 +// Unrolling this loop doesn't seem to help (when float); we're stalling to
   1.364 +// get the results of the sqrt (?), and don't have enough extra registers to
   1.365 +// have many in flight.
   1.366 +template <SkFixed (*TileProc)(SkFixed)>
   1.367 +void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
   1.368 +                      SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.369 +                      int count, int toggle) {
   1.370 +    do {
   1.371 +        const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
   1.372 +        const unsigned fi = TileProc(dist);
   1.373 +        SkASSERT(fi <= 0xFFFF);
   1.374 +        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
   1.375 +        toggle = next_dither_toggle(toggle);
   1.376 +        fx += dx;
   1.377 +        fy += dy;
   1.378 +    } while (--count != 0);
   1.379 +}
   1.380 +
   1.381 +void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
   1.382 +                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.383 +                             int count, int toggle) {
   1.384 +    shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
   1.385 +}
   1.386 +
   1.387 +void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
   1.388 +                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.389 +                             int count, int toggle) {
   1.390 +    shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
   1.391 +}
   1.392 +
   1.393 +}  // namespace
   1.394 +
   1.395 +void SkRadialGradient::shadeSpan(int x, int y,
   1.396 +                                SkPMColor* SK_RESTRICT dstC, int count) {
   1.397 +    SkASSERT(count > 0);
   1.398 +
   1.399 +    SkPoint             srcPt;
   1.400 +    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
   1.401 +    TileProc            proc = fTileProc;
   1.402 +    const SkPMColor* SK_RESTRICT cache = this->getCache32();
   1.403 +    int toggle = init_dither_toggle(x, y);
   1.404 +
   1.405 +    if (fDstToIndexClass != kPerspective_MatrixClass) {
   1.406 +        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
   1.407 +                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
   1.408 +        SkScalar sdx = fDstToIndex.getScaleX();
   1.409 +        SkScalar sdy = fDstToIndex.getSkewY();
   1.410 +
   1.411 +        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
   1.412 +            SkFixed storage[2];
   1.413 +            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
   1.414 +                                           &storage[0], &storage[1]);
   1.415 +            sdx = SkFixedToScalar(storage[0]);
   1.416 +            sdy = SkFixedToScalar(storage[1]);
   1.417 +        } else {
   1.418 +            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
   1.419 +        }
   1.420 +
   1.421 +        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
   1.422 +        if (SkShader::kClamp_TileMode == fTileMode) {
   1.423 +            shadeProc = shadeSpan_radial_clamp;
   1.424 +        } else if (SkShader::kMirror_TileMode == fTileMode) {
   1.425 +            shadeProc = shadeSpan_radial_mirror;
   1.426 +        } else {
   1.427 +            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
   1.428 +        }
   1.429 +        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
   1.430 +    } else {    // perspective case
   1.431 +        SkScalar dstX = SkIntToScalar(x);
   1.432 +        SkScalar dstY = SkIntToScalar(y);
   1.433 +        do {
   1.434 +            dstProc(fDstToIndex, dstX, dstY, &srcPt);
   1.435 +            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
   1.436 +            SkASSERT(fi <= 0xFFFF);
   1.437 +            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
   1.438 +            dstX += SK_Scalar1;
   1.439 +        } while (--count != 0);
   1.440 +    }
   1.441 +}
   1.442 +
   1.443 +/////////////////////////////////////////////////////////////////////
   1.444 +
   1.445 +#if SK_SUPPORT_GPU
   1.446 +
   1.447 +#include "GrTBackendEffectFactory.h"
   1.448 +
   1.449 +class GrGLRadialGradient : public GrGLGradientEffect {
   1.450 +public:
   1.451 +
   1.452 +    GrGLRadialGradient(const GrBackendEffectFactory& factory,
   1.453 +                       const GrDrawEffect&) : INHERITED (factory) { }
   1.454 +    virtual ~GrGLRadialGradient() { }
   1.455 +
   1.456 +    virtual void emitCode(GrGLShaderBuilder*,
   1.457 +                          const GrDrawEffect&,
   1.458 +                          EffectKey,
   1.459 +                          const char* outputColor,
   1.460 +                          const char* inputColor,
   1.461 +                          const TransformedCoordsArray&,
   1.462 +                          const TextureSamplerArray&) SK_OVERRIDE;
   1.463 +
   1.464 +    static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
   1.465 +        return GenBaseGradientKey(drawEffect);
   1.466 +    }
   1.467 +
   1.468 +private:
   1.469 +
   1.470 +    typedef GrGLGradientEffect INHERITED;
   1.471 +
   1.472 +};
   1.473 +
   1.474 +/////////////////////////////////////////////////////////////////////
   1.475 +
   1.476 +class GrRadialGradient : public GrGradientEffect {
   1.477 +public:
   1.478 +    static GrEffectRef* Create(GrContext* ctx,
   1.479 +                               const SkRadialGradient& shader,
   1.480 +                               const SkMatrix& matrix,
   1.481 +                               SkShader::TileMode tm) {
   1.482 +        AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
   1.483 +        return CreateEffectRef(effect);
   1.484 +    }
   1.485 +
   1.486 +    virtual ~GrRadialGradient() { }
   1.487 +
   1.488 +    static const char* Name() { return "Radial Gradient"; }
   1.489 +    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
   1.490 +        return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
   1.491 +    }
   1.492 +
   1.493 +    typedef GrGLRadialGradient GLEffect;
   1.494 +
   1.495 +private:
   1.496 +    GrRadialGradient(GrContext* ctx,
   1.497 +                     const SkRadialGradient& shader,
   1.498 +                     const SkMatrix& matrix,
   1.499 +                     SkShader::TileMode tm)
   1.500 +        : INHERITED(ctx, shader, matrix, tm) {
   1.501 +    }
   1.502 +
   1.503 +    GR_DECLARE_EFFECT_TEST;
   1.504 +
   1.505 +    typedef GrGradientEffect INHERITED;
   1.506 +};
   1.507 +
   1.508 +/////////////////////////////////////////////////////////////////////
   1.509 +
   1.510 +GR_DEFINE_EFFECT_TEST(GrRadialGradient);
   1.511 +
   1.512 +GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
   1.513 +                                          GrContext* context,
   1.514 +                                          const GrDrawTargetCaps&,
   1.515 +                                          GrTexture**) {
   1.516 +    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
   1.517 +    SkScalar radius = random->nextUScalar1();
   1.518 +
   1.519 +    SkColor colors[kMaxRandomGradientColors];
   1.520 +    SkScalar stopsArray[kMaxRandomGradientColors];
   1.521 +    SkScalar* stops = stopsArray;
   1.522 +    SkShader::TileMode tm;
   1.523 +    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
   1.524 +    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
   1.525 +                                                                 colors, stops, colorCount,
   1.526 +                                                                 tm));
   1.527 +    SkPaint paint;
   1.528 +    return shader->asNewEffect(context, paint);
   1.529 +}
   1.530 +
   1.531 +/////////////////////////////////////////////////////////////////////
   1.532 +
   1.533 +void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
   1.534 +                                  const GrDrawEffect&,
   1.535 +                                  EffectKey key,
   1.536 +                                  const char* outputColor,
   1.537 +                                  const char* inputColor,
   1.538 +                                  const TransformedCoordsArray& coords,
   1.539 +                                  const TextureSamplerArray& samplers) {
   1.540 +    this->emitUniforms(builder, key);
   1.541 +    SkString t("length(");
   1.542 +    t.append(builder->ensureFSCoords2D(coords, 0));
   1.543 +    t.append(")");
   1.544 +    this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
   1.545 +}
   1.546 +
   1.547 +/////////////////////////////////////////////////////////////////////
   1.548 +
   1.549 +GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
   1.550 +    SkASSERT(NULL != context);
   1.551 +
   1.552 +    SkMatrix matrix;
   1.553 +    if (!this->getLocalMatrix().invert(&matrix)) {
   1.554 +        return NULL;
   1.555 +    }
   1.556 +    matrix.postConcat(fPtsToUnit);
   1.557 +    return GrRadialGradient::Create(context, *this, matrix, fTileMode);
   1.558 +}
   1.559 +
   1.560 +#else
   1.561 +
   1.562 +GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
   1.563 +    SkDEBUGFAIL("Should not call in GPU-less build");
   1.564 +    return NULL;
   1.565 +}
   1.566 +
   1.567 +#endif
   1.568 +
   1.569 +#ifndef SK_IGNORE_TO_STRING
   1.570 +void SkRadialGradient::toString(SkString* str) const {
   1.571 +    str->append("SkRadialGradient: (");
   1.572 +
   1.573 +    str->append("center: (");
   1.574 +    str->appendScalar(fCenter.fX);
   1.575 +    str->append(", ");
   1.576 +    str->appendScalar(fCenter.fY);
   1.577 +    str->append(") radius: ");
   1.578 +    str->appendScalar(fRadius);
   1.579 +    str->append(" ");
   1.580 +
   1.581 +    this->INHERITED::toString(str);
   1.582 +
   1.583 +    str->append(")");
   1.584 +}
   1.585 +#endif

mercurial