• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
DescriptorDescriptor29         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:
DescriptorScope()47         DescriptorScope() {}
48 
49         bool unflatten(SkReadBuffer&);
50 
51         // fColors and fPos always point into local memory, so they can be safely mutated
52         //
mutableColors()53         SkColor4f* mutableColors() { return const_cast<SkColor4f*>(fColors); }
mutablePos()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 
getGradFlags()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>
CheckedMakeContext(SkArenaAlloc * alloc,Args &&...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:
getPos(int i)121     SkScalar getPos(int i) const {
122         SkASSERT(i < fColorCount);
123         return fOrigPos ? fOrigPos[i] : SkIntToScalar(i) / (fColorCount - 1);
124     }
125 
getLegacyColor(int i)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 
colorsAreOpaque()136     bool colorsAreOpaque() const { return fColorsAreOpaque; }
137 
getTileMode()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 {
CreateArgsCreateArgs193         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 
CreateArgsCreateArgs215         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 
useAtlas()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.
AdjustFP(std::unique_ptr<GrGradientEffect> gradientFP,const CreateArgs & args)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 
getCoordTransform()311     const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
312 
313     /** Checks whether the constructor failed to fully initialize the processor. */
isValid()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:
GLSLProcessor()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