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 #ifndef SkGradientShaderPriv_DEFINED
      9 #define SkGradientShaderPriv_DEFINED
     10 
     11 #include "SkGradientShader.h"
     12 
     13 #include "SkArenaAlloc.h"
     14 #include "SkAutoMalloc.h"
     15 #include "SkMatrix.h"
     16 #include "SkShaderBase.h"
     17 #include "SkTArray.h"
     18 #include "SkTemplates.h"
     19 
     20 class SkColorSpace;
     21 class SkColorSpaceXformer;
     22 class SkRasterPipeline;
     23 class SkReadBuffer;
     24 class SkWriteBuffer;
     25 
     26 class SkGradientShaderBase : public SkShaderBase {
     27 public:
     28     struct Descriptor {
     29         Descriptor() {
     30             sk_bzero(this, sizeof(*this));
     31             fTileMode = SkShader::kClamp_TileMode;
     32         }
     33 
     34         const SkMatrix*     fLocalMatrix;
     35         const SkColor4f*    fColors;
     36         sk_sp<SkColorSpace> fColorSpace;
     37         const SkScalar*     fPos;
     38         int                 fCount;
     39         SkShader::TileMode  fTileMode;
     40         uint32_t            fGradFlags;
     41 
     42         void flatten(SkWriteBuffer&) const;
     43     };
     44 
     45     class DescriptorScope : public Descriptor {
     46     public:
     47         DescriptorScope() {}
     48 
     49         bool unflatten(SkReadBuffer&);
     50 
     51         // fColors and fPos always point into local memory, so they can be safely mutated
     52         //
     53         SkColor4f* mutableColors() { return const_cast<SkColor4f*>(fColors); }
     54         SkScalar* mutablePos() { return const_cast<SkScalar*>(fPos); }
     55 
     56     private:
     57         enum {
     58             kStorageCount = 16
     59         };
     60         SkColor4f fColorStorage[kStorageCount];
     61         SkScalar fPosStorage[kStorageCount];
     62         SkMatrix fLocalMatrixStorage;
     63         SkAutoMalloc fDynamicStorage;
     64     };
     65 
     66     SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit);
     67     ~SkGradientShaderBase() override;
     68 
     69     bool isOpaque() const override;
     70 
     71     enum class GradientBitmapType : uint8_t {
     72         kLegacy,
     73         kSRGB,
     74         kHalfFloat,
     75     };
     76 
     77     void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const;
     78 
     79     uint32_t getGradFlags() const { return fGradFlags; }
     80 
     81     SkColor4f getXformedColor(size_t index, SkColorSpace*) const;
     82 
     83 protected:
     84     class GradientShaderBase4fContext;
     85 
     86     SkGradientShaderBase(SkReadBuffer& );
     87     void flatten(SkWriteBuffer&) const override;
     88     SK_TO_STRING_OVERRIDE()
     89 
     90     void commonAsAGradient(GradientInfo*) const;
     91 
     92     bool onAsLuminanceColor(SkColor*) const override;
     93 
     94     void initLinearBitmap(SkBitmap* bitmap, GradientBitmapType) const;
     95 
     96     bool onAppendStages(const StageRec&) const override;
     97 
     98     virtual void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
     99                                       SkRasterPipeline* postPipeline) const = 0;
    100 
    101     template <typename T, typename... Args>
    102     static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {
    103         auto* ctx = alloc->make<T>(std::forward<Args>(args)...);
    104         if (!ctx->isValid()) {
    105             return nullptr;
    106         }
    107         return ctx;
    108     }
    109 
    110     struct AutoXformColors {
    111         AutoXformColors(const SkGradientShaderBase&, SkColorSpaceXformer*);
    112 
    113         SkAutoSTMalloc<8, SkColor> fColors;
    114     };
    115 
    116     const SkMatrix fPtsToUnit;
    117     TileMode       fTileMode;
    118     uint8_t        fGradFlags;
    119 
    120 public:
    121     SkScalar getPos(int i) const {
    122         SkASSERT(i < fColorCount);
    123         return fOrigPos ? fOrigPos[i] : SkIntToScalar(i) / (fColorCount - 1);
    124     }
    125 
    126     SkColor getLegacyColor(int i) const {
    127         SkASSERT(i < fColorCount);
    128         return fOrigColors4f[i].toSkColor();
    129     }
    130 
    131     SkColor4f*          fOrigColors4f; // original colors, as linear floats
    132     SkScalar*           fOrigPos;      // original positions
    133     int                 fColorCount;
    134     sk_sp<SkColorSpace> fColorSpace;   // color space of gradient stops
    135 
    136     bool colorsAreOpaque() const { return fColorsAreOpaque; }
    137 
    138     TileMode getTileMode() const { return fTileMode; }
    139 
    140 private:
    141     // Reserve inline space for up to 4 stops.
    142     static constexpr size_t kInlineStopCount   = 4;
    143     static constexpr size_t kInlineStorageSize = (sizeof(SkColor4f) + sizeof(SkScalar))
    144                                                * kInlineStopCount;
    145     SkAutoSTMalloc<kInlineStorageSize, uint8_t> fStorage;
    146 
    147     bool                                        fColorsAreOpaque;
    148 
    149     typedef SkShaderBase INHERITED;
    150 };
    151 
    152 ///////////////////////////////////////////////////////////////////////////////
    153 
    154 #if SK_SUPPORT_GPU
    155 
    156 #include "GrColorSpaceInfo.h"
    157 #include "GrCoordTransform.h"
    158 #include "GrFragmentProcessor.h"
    159 #include "glsl/GrGLSLFragmentProcessor.h"
    160 #include "glsl/GrGLSLProgramDataManager.h"
    161 
    162 class GrInvariantOutput;
    163 
    164 /*
    165  * The interpretation of the texture matrix depends on the sample mode. The
    166  * texture matrix is applied both when the texture coordinates are explicit
    167  * and  when vertex positions are used as texture  coordinates. In the latter
    168  * case the texture matrix is applied to the pre-view-matrix position
    169  * values.
    170  *
    171  * Normal SampleMode
    172  *  The post-matrix texture coordinates are in normalize space with (0,0) at
    173  *  the top-left and (1,1) at the bottom right.
    174  * RadialGradient
    175  *  The matrix specifies the radial gradient parameters.
    176  *  (0,0) in the post-matrix space is center of the radial gradient.
    177  * Radial2Gradient
    178  *   Matrix transforms to space where first circle is centered at the
    179  *   origin. The second circle will be centered (x, 0) where x may be
    180  *   0 and is provided by setRadial2Params. The post-matrix space is
    181  *   normalized such that 1 is the second radius - first radius.
    182  * SweepGradient
    183  *  The angle from the origin of texture coordinates in post-matrix space
    184  *  determines the gradient value.
    185  */
    186 
    187  class GrTextureStripAtlas;
    188 
    189 // Base class for Gr gradient effects
    190 class GrGradientEffect : public GrFragmentProcessor {
    191 public:
    192     struct CreateArgs {
    193         CreateArgs(GrContext* context,
    194                    const SkGradientShaderBase* shader,
    195                    const SkMatrix* matrix,
    196                    SkShader::TileMode tileMode,
    197                    SkColorSpace* dstColorSpace)
    198                 : fContext(context)
    199                 , fShader(shader)
    200                 , fMatrix(matrix)
    201                 , fDstColorSpace(dstColorSpace) {
    202             switch (tileMode) {
    203                 case SkShader::kClamp_TileMode:
    204                     fWrapMode = GrSamplerState::WrapMode::kClamp;
    205                     break;
    206                 case SkShader::kRepeat_TileMode:
    207                     fWrapMode = GrSamplerState::WrapMode::kRepeat;
    208                     break;
    209                 case SkShader::kMirror_TileMode:
    210                     fWrapMode = GrSamplerState::WrapMode::kMirrorRepeat;
    211                     break;
    212             }
    213         }
    214 
    215         CreateArgs(GrContext* context,
    216                    const SkGradientShaderBase* shader,
    217                    const SkMatrix* matrix,
    218                    GrSamplerState::WrapMode wrapMode,
    219                    SkColorSpace* dstColorSpace)
    220                 : fContext(context)
    221                 , fShader(shader)
    222                 , fMatrix(matrix)
    223                 , fWrapMode(wrapMode)
    224                 , fDstColorSpace(dstColorSpace) {}
    225 
    226         GrContext*                  fContext;
    227         const SkGradientShaderBase* fShader;
    228         const SkMatrix*             fMatrix;
    229         GrSamplerState::WrapMode    fWrapMode;
    230         SkColorSpace*               fDstColorSpace;
    231     };
    232 
    233     class GLSLProcessor;
    234 
    235     ~GrGradientEffect() override;
    236 
    237     bool useAtlas() const { return SkToBool(-1 != fRow); }
    238 
    239     // Controls the implementation strategy for this effect.
    240     // NB: all entries need to be reflected in the key.
    241     enum class InterpolationStrategy : uint8_t {
    242         kSingle,          // interpolation in a single domain [0,1]
    243         kThreshold,       // interpolation in two domains [0,T) [T,1], with normal clamping
    244         kThresholdClamp0, // same as kThreshold, but clamped only on the left edge
    245         kThresholdClamp1, // same as kThreshold, but clamped only on the right edge
    246         kTexture,         // texture-based fallback
    247     };
    248 
    249     enum PremulType {
    250         kBeforeInterp_PremulType,
    251         kAfterInterp_PremulType,
    252     };
    253 
    254 protected:
    255     GrGradientEffect(ClassID classID, const CreateArgs&, bool isOpaque);
    256     explicit GrGradientEffect(const GrGradientEffect&);  // facilitates clone() implementations
    257 
    258     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
    259 
    260     // Helper function used by derived class factories to handle color space transformation and
    261     // modulation by input alpha.
    262     static std::unique_ptr<GrFragmentProcessor> AdjustFP(
    263             std::unique_ptr<GrGradientEffect> gradientFP, const CreateArgs& args) {
    264         if (!gradientFP->isValid()) {
    265             return nullptr;
    266         }
    267         std::unique_ptr<GrFragmentProcessor> fp;
    268         // With analytic gradients, we pre-convert the stops to the destination color space, so no
    269         // xform is needed. With texture-based gradients, we leave the data in the source color
    270         // space (to avoid clamping if we can't use F16)... Add an extra FP to do the xform.
    271         if (gradientFP->fStrategy == InterpolationStrategy::kTexture) {
    272             // Our texture is always either F16 or sRGB, so the data is "linear" in the shader.
    273             // Create our xform assuming float inputs, which will suppress any extra sRGB work.
    274             // We do support having a transfer function on the color space of the stops, so
    275             // this FP may include that transformation.
    276             fp = GrColorSpaceXformEffect::Make(std::move(gradientFP),
    277                                                args.fShader->fColorSpace.get(),
    278                                                kRGBA_float_GrPixelConfig,
    279                                                args.fDstColorSpace);
    280         } else {
    281             fp = std::move(gradientFP);
    282         }
    283         return GrFragmentProcessor::MulChildByInputAlpha(std::move(fp));
    284     }
    285 
    286 #if GR_TEST_UTILS
    287     /** Helper struct that stores (and populates) parameters to construct a random gradient.
    288         If fUseColors4f is true, then the SkColor4f factory should be called, with fColors4f and
    289         fColorSpace. Otherwise, the SkColor factory should be called, with fColors. fColorCount
    290         will be the number of color stops in either case, and fColors and fStops can be passed to
    291         the gradient factory. (The constructor may decide not to use stops, in which case fStops
    292         will be nullptr). */
    293     struct RandomGradientParams {
    294         static constexpr int kMaxRandomGradientColors = 5;
    295 
    296         RandomGradientParams(SkRandom* r);
    297 
    298         bool fUseColors4f;
    299         SkColor fColors[kMaxRandomGradientColors];
    300         SkColor4f fColors4f[kMaxRandomGradientColors];
    301         sk_sp<SkColorSpace> fColorSpace;
    302         SkScalar fStopStorage[kMaxRandomGradientColors];
    303         SkShader::TileMode fTileMode;
    304         int fColorCount;
    305         SkScalar* fStops;
    306     };
    307     #endif
    308 
    309     bool onIsEqual(const GrFragmentProcessor&) const override;
    310 
    311     const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
    312 
    313     /** Checks whether the constructor failed to fully initialize the processor. */
    314     bool isValid() const {
    315         return fStrategy != InterpolationStrategy::kTexture || fTextureSampler.isInitialized();
    316     }
    317 
    318 private:
    319     void addInterval(const SkGradientShaderBase&, size_t idx0, size_t idx1, SkColorSpace*);
    320 
    321     static OptimizationFlags OptFlags(bool isOpaque);
    322 
    323     // Interpolation intervals, encoded as 4f tuples of (scale, bias)
    324     // such that color(t) = t * scale + bias.
    325     SkSTArray<4, GrColor4f, true> fIntervals;
    326 
    327     GrSamplerState::WrapMode fWrapMode;
    328 
    329     GrCoordTransform fCoordTransform;
    330     TextureSampler fTextureSampler;
    331     SkScalar fYCoord;
    332     GrTextureStripAtlas* fAtlas;
    333     int fRow;
    334     bool fIsOpaque;
    335 
    336     InterpolationStrategy fStrategy;
    337     SkScalar              fThreshold;  // used for InterpolationStrategy::kThreshold
    338     PremulType            fPremulType; // This is already baked into the table for texture
    339                                        // gradients, and only changes behavior for gradients
    340                                        // that don't use a texture.
    341 
    342     typedef GrFragmentProcessor INHERITED;
    343 
    344 };
    345 
    346 ///////////////////////////////////////////////////////////////////////////////
    347 
    348 // Base class for GL gradient effects
    349 class GrGradientEffect::GLSLProcessor : public GrGLSLFragmentProcessor {
    350 public:
    351     GLSLProcessor() {
    352         fCachedYCoord = SK_ScalarMax;
    353     }
    354 
    355     static uint32_t GenBaseGradientKey(const GrProcessor&);
    356 
    357 protected:
    358     void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
    359 
    360     // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
    361     // should call this method from their emitCode().
    362     void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&);
    363 
    364     // Emit code that gets a fragment's color from an expression for t; has branches for
    365     // several control flows inside -- 2-color gradients, 3-color symmetric gradients, 4+
    366     // color gradients that use the traditional texture lookup, as well as several varieties
    367     // of hard stop gradients
    368     void emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
    369                    GrGLSLUniformHandler* uniformHandler,
    370                    const GrShaderCaps* shaderCaps,
    371                    const GrGradientEffect&,
    372                    const char* gradientTValue,
    373                    const char* outputColor,
    374                    const char* inputColor,
    375                    const TextureSamplers&);
    376 
    377 private:
    378     void emitAnalyticalColor(GrGLSLFPFragmentBuilder* fragBuilder,
    379                              GrGLSLUniformHandler* uniformHandler,
    380                              const GrShaderCaps* shaderCaps,
    381                              const GrGradientEffect&,
    382                              const char* gradientTValue,
    383                              const char* outputColor,
    384                              const char* inputColor);
    385 
    386     SkScalar fCachedYCoord;
    387     GrGLSLProgramDataManager::UniformHandle fIntervalsUni;
    388     GrGLSLProgramDataManager::UniformHandle fThresholdUni;
    389     GrGLSLProgramDataManager::UniformHandle fFSYUni;
    390 
    391     typedef GrGLSLFragmentProcessor INHERITED;
    392 };
    393 
    394 #endif
    395 
    396 #endif
    397