Home | History | Annotate | Download | only in gradients
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkRadialGradient.h"
      9 #include "SkNx.h"
     10 
     11 namespace {
     12 
     13 // GCC doesn't like using static functions as template arguments.  So force these to be non-static.
     14 inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
     15     return mirror_tileproc(x);
     16 }
     17 
     18 inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
     19     return repeat_tileproc(x);
     20 }
     21 
     22 SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
     23     SkScalar    inv = SkScalarInvert(radius);
     24 
     25     SkMatrix matrix;
     26     matrix.setTranslate(-center.fX, -center.fY);
     27     matrix.postScale(inv, inv);
     28     return matrix;
     29 }
     30 
     31 
     32 }  // namespace
     33 
     34 /////////////////////////////////////////////////////////////////////
     35 
     36 SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
     37     : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
     38     , fCenter(center)
     39     , fRadius(radius) {
     40 }
     41 
     42 SkShader::Context* SkRadialGradient::onMakeContext(
     43     const ContextRec& rec, SkArenaAlloc* alloc) const
     44 {
     45     return CheckedMakeContext<RadialGradientContext>(alloc, *this, rec);
     46 }
     47 
     48 SkRadialGradient::RadialGradientContext::RadialGradientContext(
     49         const SkRadialGradient& shader, const ContextRec& rec)
     50     : INHERITED(shader, rec) {}
     51 
     52 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
     53     if (info) {
     54         commonAsAGradient(info);
     55         info->fPoint[0] = fCenter;
     56         info->fRadius[0] = fRadius;
     57     }
     58     return kRadial_GradientType;
     59 }
     60 
     61 sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
     62     DescriptorScope desc;
     63     if (!desc.unflatten(buffer)) {
     64         return nullptr;
     65     }
     66     const SkPoint center = buffer.readPoint();
     67     const SkScalar radius = buffer.readScalar();
     68     return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace),
     69                                         desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags,
     70                                         desc.fLocalMatrix);
     71 }
     72 
     73 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
     74     this->INHERITED::flatten(buffer);
     75     buffer.writePoint(fCenter);
     76     buffer.writeScalar(fRadius);
     77 }
     78 
     79 namespace {
     80 
     81 inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
     82     // fast, overly-conservative test: checks unit square instead of unit circle
     83     bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
     84     bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
     85     return xClamped || yClamped;
     86 }
     87 
     88 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
     89         SkScalar sfy, SkScalar sdy,
     90         SkPMColor* dstC, const SkPMColor* cache,
     91         int count, int toggle);
     92 
     93 static inline Sk4f fast_sqrt(const Sk4f& R) {
     94     return R * R.rsqrt();
     95 }
     96 
     97 static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
     98     return a * a + b * b;
     99 }
    100 
    101 void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
    102                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    103                              int count, int toggle) {
    104     if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
    105         unsigned fi = SkGradientShaderBase::kCache32Count - 1;
    106         sk_memset32_dither(dstC,
    107                            cache[toggle + fi],
    108                            cache[next_dither_toggle(toggle) + fi],
    109                            count);
    110     } else {
    111         const Sk4f min(SK_ScalarNearlyZero);
    112         const Sk4f max(255);
    113         const float scale = 255;
    114         sfx *= scale;
    115         sfy *= scale;
    116         sdx *= scale;
    117         sdy *= scale;
    118         const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
    119         const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
    120         const Sk4f dx4(sdx * 4);
    121         const Sk4f dy4(sdy * 4);
    122 
    123         Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
    124         Sk4f tmpdxdy = sum_squares(dx4, dy4);
    125         Sk4f R = Sk4f::Max(sum_squares(fx4, fy4), min);
    126         Sk4f dR = tmpxy + tmpxy + tmpdxdy;
    127         const Sk4f ddR = tmpdxdy + tmpdxdy;
    128 
    129         for (int i = 0; i < (count >> 2); ++i) {
    130             Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
    131             R = Sk4f::Max(R + dR, min);
    132             dR = dR + ddR;
    133 
    134             uint8_t fi[4];
    135             SkNx_cast<uint8_t>(dist).store(fi);
    136 
    137             for (int i = 0; i < 4; i++) {
    138                 *dstC++ = cache[toggle + fi[i]];
    139                 toggle = next_dither_toggle(toggle);
    140             }
    141         }
    142         count &= 3;
    143         if (count) {
    144             Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
    145 
    146             uint8_t fi[4];
    147             SkNx_cast<uint8_t>(dist).store(fi);
    148             for (int i = 0; i < count; i++) {
    149                 *dstC++ = cache[toggle + fi[i]];
    150                 toggle = next_dither_toggle(toggle);
    151             }
    152         }
    153     }
    154 }
    155 
    156 // Unrolling this loop doesn't seem to help (when float); we're stalling to
    157 // get the results of the sqrt (?), and don't have enough extra registers to
    158 // have many in flight.
    159 template <SkFixed (*TileProc)(SkFixed)>
    160 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
    161                       SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    162                       int count, int toggle) {
    163     do {
    164         const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
    165         const unsigned fi = TileProc(dist);
    166         SkASSERT(fi <= 0xFFFF);
    167         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
    168         toggle = next_dither_toggle(toggle);
    169         fx += dx;
    170         fy += dy;
    171     } while (--count != 0);
    172 }
    173 
    174 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
    175                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    176                              int count, int toggle) {
    177     shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
    178 }
    179 
    180 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
    181                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    182                              int count, int toggle) {
    183     shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
    184 }
    185 
    186 }  // namespace
    187 
    188 void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
    189                                                         SkPMColor* SK_RESTRICT dstC, int count) {
    190     SkASSERT(count > 0);
    191 
    192     const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
    193 
    194     SkPoint             srcPt;
    195     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    196     TileProc            proc = radialGradient.fTileProc;
    197     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
    198     int toggle = init_dither_toggle(x, y);
    199 
    200     if (fDstToIndexClass != kPerspective_MatrixClass) {
    201         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    202                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    203         SkScalar sdx = fDstToIndex.getScaleX();
    204         SkScalar sdy = fDstToIndex.getSkewY();
    205 
    206         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    207             const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
    208             sdx = step.fX;
    209             sdy = step.fY;
    210         } else {
    211             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    212         }
    213 
    214         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
    215         if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
    216             shadeProc = shadeSpan_radial_clamp2;
    217         } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
    218             shadeProc = shadeSpan_radial_mirror;
    219         } else {
    220             SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
    221         }
    222         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
    223     } else {    // perspective case
    224         SkScalar dstX = SkIntToScalar(x);
    225         SkScalar dstY = SkIntToScalar(y);
    226         do {
    227             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    228             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
    229             SkASSERT(fi <= 0xFFFF);
    230             *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
    231             dstX += SK_Scalar1;
    232         } while (--count != 0);
    233     }
    234 }
    235 
    236 /////////////////////////////////////////////////////////////////////
    237 
    238 #if SK_SUPPORT_GPU
    239 
    240 #include "SkGr.h"
    241 #include "GrShaderCaps.h"
    242 #include "glsl/GrGLSLFragmentShaderBuilder.h"
    243 
    244 class GrRadialGradient : public GrGradientEffect {
    245 public:
    246     class GLSLRadialProcessor;
    247 
    248     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
    249         return sk_sp<GrFragmentProcessor>(new GrRadialGradient(args));
    250     }
    251 
    252     ~GrRadialGradient() override {}
    253 
    254     const char* name() const override { return "Radial Gradient"; }
    255 
    256 private:
    257     GrRadialGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
    258         this->initClassID<GrRadialGradient>();
    259     }
    260 
    261     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
    262 
    263     virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
    264                                        GrProcessorKeyBuilder* b) const override;
    265 
    266     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
    267 
    268     typedef GrGradientEffect INHERITED;
    269 };
    270 
    271 /////////////////////////////////////////////////////////////////////
    272 
    273 class GrRadialGradient::GLSLRadialProcessor : public GrGradientEffect::GLSLProcessor {
    274 public:
    275     GLSLRadialProcessor(const GrProcessor&) {}
    276     ~GLSLRadialProcessor() override {}
    277 
    278     virtual void emitCode(EmitArgs&) override;
    279 
    280     static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
    281         b->add32(GenBaseGradientKey(processor));
    282     }
    283 
    284 private:
    285     typedef GrGradientEffect::GLSLProcessor INHERITED;
    286 
    287 };
    288 
    289 /////////////////////////////////////////////////////////////////////
    290 
    291 GrGLSLFragmentProcessor* GrRadialGradient::onCreateGLSLInstance() const {
    292     return new GrRadialGradient::GLSLRadialProcessor(*this);
    293 }
    294 
    295 void GrRadialGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    296                                              GrProcessorKeyBuilder* b) const {
    297     GrRadialGradient::GLSLRadialProcessor::GenKey(*this, caps, b);
    298 }
    299 
    300 /////////////////////////////////////////////////////////////////////
    301 
    302 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
    303 
    304 #if GR_TEST_UTILS
    305 sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) {
    306     sk_sp<SkShader> shader;
    307     do {
    308         RandomGradientParams params(d->fRandom);
    309         SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
    310         SkScalar radius = d->fRandom->nextUScalar1();
    311         shader = params.fUseColors4f
    312                          ? SkGradientShader::MakeRadial(center, radius, params.fColors4f,
    313                                                         params.fColorSpace, params.fStops,
    314                                                         params.fColorCount, params.fTileMode)
    315                          : SkGradientShader::MakeRadial(center, radius, params.fColors,
    316                                                         params.fStops, params.fColorCount,
    317                                                         params.fTileMode);
    318     } while (!shader);
    319     GrTest::TestAsFPArgs asFPArgs(d);
    320     sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
    321     GrAlwaysAssert(fp);
    322     return fp;
    323 }
    324 #endif
    325 
    326 /////////////////////////////////////////////////////////////////////
    327 
    328 void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) {
    329     const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
    330     this->emitUniforms(args.fUniformHandler, ge);
    331     SkString t("length(");
    332     t.append(args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]));
    333     t.append(")");
    334     this->emitColor(args.fFragBuilder,
    335                     args.fUniformHandler,
    336                     args.fShaderCaps,
    337                     ge, t.c_str(),
    338                     args.fOutputColor,
    339                     args.fInputColor,
    340                     args.fTexSamplers);
    341 }
    342 
    343 /////////////////////////////////////////////////////////////////////
    344 
    345 sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs& args) const {
    346     SkASSERT(args.fContext);
    347 
    348     SkMatrix matrix;
    349     if (!this->getLocalMatrix().invert(&matrix)) {
    350         return nullptr;
    351     }
    352     if (args.fLocalMatrix) {
    353         SkMatrix inv;
    354         if (!args.fLocalMatrix->invert(&inv)) {
    355             return nullptr;
    356         }
    357         matrix.postConcat(inv);
    358     }
    359     matrix.postConcat(fPtsToUnit);
    360     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
    361                                                                        args.fDstColorSpace);
    362     sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make(
    363         GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
    364                                      std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
    365     return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
    366 }
    367 
    368 #endif
    369 
    370 #ifndef SK_IGNORE_TO_STRING
    371 void SkRadialGradient::toString(SkString* str) const {
    372     str->append("SkRadialGradient: (");
    373 
    374     str->append("center: (");
    375     str->appendScalar(fCenter.fX);
    376     str->append(", ");
    377     str->appendScalar(fCenter.fY);
    378     str->append(") radius: ");
    379     str->appendScalar(fRadius);
    380     str->append(" ");
    381 
    382     this->INHERITED::toString(str);
    383 
    384     str->append(")");
    385 }
    386 #endif
    387