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