blob: 83e0789853181950a65b8c70b30dcc5c64b4ad96 [file] [log] [blame]
rileya@google.com1c6d64b2012-07-27 15:49:05 +00001/*
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#include "SkClampRange.h"
13#include "SkColorPriv.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000014#include "SkReadBuffer.h"
15#include "SkWriteBuffer.h"
rileya@google.com1c6d64b2012-07-27 15:49:05 +000016#include "SkMallocPixelRef.h"
17#include "SkUnitMapper.h"
18#include "SkUtils.h"
19#include "SkTemplates.h"
20#include "SkBitmapCache.h"
21#include "SkShader.h"
rileya@google.com1c6d64b2012-07-27 15:49:05 +000022
humper@google.com05af1af2013-01-07 16:47:43 +000023static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
rileya@google.com1c6d64b2012-07-27 15:49:05 +000024 int count) {
25 if (count > 0) {
26 if (v0 == v1) {
27 sk_memset32(dst, v0, count);
28 } else {
29 int pairs = count >> 1;
30 for (int i = 0; i < pairs; i++) {
31 *dst++ = v0;
32 *dst++ = v1;
33 }
34 if (count & 1) {
35 *dst = v0;
36 }
37 }
38 }
39}
40
41// Clamp
42
humper@google.com05af1af2013-01-07 16:47:43 +000043static inline SkFixed clamp_tileproc(SkFixed x) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +000044 return SkClampMax(x, 0xFFFF);
45}
46
47// Repeat
48
humper@google.com05af1af2013-01-07 16:47:43 +000049static inline SkFixed repeat_tileproc(SkFixed x) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +000050 return x & 0xFFFF;
51}
52
53// Mirror
54
55// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
56// See http://code.google.com/p/skia/issues/detail?id=472
57#if defined(_MSC_VER) && (_MSC_VER >= 1600)
58#pragma optimize("", off)
59#endif
60
61static inline SkFixed mirror_tileproc(SkFixed x) {
62 int s = x << 15 >> 31;
63 return (x ^ s) & 0xFFFF;
64}
65
66#if defined(_MSC_VER) && (_MSC_VER >= 1600)
67#pragma optimize("", on)
68#endif
69
70///////////////////////////////////////////////////////////////////////////////
71
72typedef SkFixed (*TileProc)(SkFixed);
73
74///////////////////////////////////////////////////////////////////////////////
75
76static const TileProc gTileProcs[] = {
77 clamp_tileproc,
78 repeat_tileproc,
79 mirror_tileproc
80};
81
82///////////////////////////////////////////////////////////////////////////////
83
84class SkGradientShaderBase : public SkShader {
85public:
reed@google.com437d6eb2013-05-23 19:03:05 +000086 struct Descriptor {
87 Descriptor() {
88 sk_bzero(this, sizeof(*this));
89 fTileMode = SkShader::kClamp_TileMode;
90 }
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +000091
reed@google.com437d6eb2013-05-23 19:03:05 +000092 const SkColor* fColors;
93 const SkScalar* fPos;
94 int fCount;
95 SkShader::TileMode fTileMode;
96 SkUnitMapper* fMapper;
reed@google.com3d3a8602013-05-24 14:58:44 +000097 uint32_t fFlags;
reed@google.com437d6eb2013-05-23 19:03:05 +000098 };
99
100public:
101 SkGradientShaderBase(const Descriptor& desc);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000102 virtual ~SkGradientShaderBase();
103
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000104 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
105 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
106 virtual bool isOpaque() const SK_OVERRIDE;
107
108 void getGradientTableBitmap(SkBitmap*) const;
109
110 enum {
111 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
112 /// it, use a larger cache.
113 kCache16Bits = 8,
reed@google.com3c2102c2013-02-01 12:59:40 +0000114 kCache16Count = (1 << kCache16Bits),
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000115 kCache16Shift = 16 - kCache16Bits,
116 kSqrt16Shift = 8 - kCache16Bits,
117
118 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
119 /// it, use a larger cache.
120 kCache32Bits = 8,
reed@google.com60040292013-02-04 18:21:23 +0000121 kCache32Count = (1 << kCache32Bits),
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000122 kCache32Shift = 16 - kCache32Bits,
123 kSqrt32Shift = 8 - kCache32Bits,
124
125 /// This value is used to *read* the dither cache; it may be 0
126 /// if dithering is disabled.
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000127 kDitherStride32 = kCache32Count,
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000128 kDitherStride16 = kCache16Count,
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000129 };
130
131
132protected:
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000133 SkGradientShaderBase(SkReadBuffer& );
134 virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000135 SK_TO_STRING_OVERRIDE()
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000136
137 SkUnitMapper* fMapper;
138 SkMatrix fPtsToUnit; // set by subclass
139 SkMatrix fDstToIndex;
140 SkMatrix::MapXYProc fDstToIndexProc;
141 TileMode fTileMode;
142 TileProc fTileProc;
143 int fColorCount;
144 uint8_t fDstToIndexClass;
145 uint8_t fFlags;
reed@google.com3d3a8602013-05-24 14:58:44 +0000146 uint8_t fGradFlags;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000147
148 struct Rec {
149 SkFixed fPos; // 0...1
150 uint32_t fScale; // (1 << 24) / range
151 };
152 Rec* fRecs;
153
154 const uint16_t* getCache16() const;
155 const SkPMColor* getCache32() const;
156
157 void commonAsAGradient(GradientInfo*) const;
158
159private:
160 enum {
161 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
162
163 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
164 };
165 SkColor fStorage[(kStorageSize + 3) >> 2];
166 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
167 bool fColorsAreOpaque;
168
169 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
170 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
171
172 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
173 mutable SkMallocPixelRef* fCache32PixelRef;
174 mutable unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
175
176 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
177 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
reed@google.com3d3a8602013-05-24 14:58:44 +0000178 U8CPU alpha, uint32_t gradFlags);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000179 void setCacheAlpha(U8CPU alpha) const;
180 void initCommon();
181
182 typedef SkShader INHERITED;
183};
184
reed@google.com55853db2013-02-01 19:34:59 +0000185static inline int init_dither_toggle(int x, int y) {
reed@google.com60040292013-02-04 18:21:23 +0000186 x &= 1;
187 y = (y & 1) << 1;
188 return (x | y) * SkGradientShaderBase::kDitherStride32;
reed@google.com55853db2013-02-01 19:34:59 +0000189}
190
191static inline int next_dither_toggle(int toggle) {
192 return toggle ^ SkGradientShaderBase::kDitherStride32;
193}
194
195static inline int init_dither_toggle16(int x, int y) {
196 return ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride16;
197}
198
199static inline int next_dither_toggle16(int toggle) {
200 return toggle ^ SkGradientShaderBase::kDitherStride16;
201}
202
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000203///////////////////////////////////////////////////////////////////////////////
204
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000205#if SK_SUPPORT_GPU
206
bsalomon@google.com77af6802013-10-02 13:04:56 +0000207#include "GrCoordTransform.h"
bsalomon@google.comd698f772012-10-25 13:22:00 +0000208#include "gl/GrGLEffect.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000209
bsalomon@google.com08283af2012-10-26 13:01:20 +0000210class GrEffectStage;
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000211class GrBackendEffectFactory;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000212
213/*
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000214 * The interpretation of the texture matrix depends on the sample mode. The
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000215 * texture matrix is applied both when the texture coordinates are explicit
216 * and when vertex positions are used as texture coordinates. In the latter
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000217 * case the texture matrix is applied to the pre-view-matrix position
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000218 * values.
219 *
220 * Normal SampleMode
221 * The post-matrix texture coordinates are in normalize space with (0,0) at
222 * the top-left and (1,1) at the bottom right.
223 * RadialGradient
224 * The matrix specifies the radial gradient parameters.
225 * (0,0) in the post-matrix space is center of the radial gradient.
226 * Radial2Gradient
227 * Matrix transforms to space where first circle is centered at the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000228 * origin. The second circle will be centered (x, 0) where x may be
229 * 0 and is provided by setRadial2Params. The post-matrix space is
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000230 * normalized such that 1 is the second radius - first radius.
231 * SweepGradient
232 * The angle from the origin of texture coordinates in post-matrix space
233 * determines the gradient value.
234 */
235
rileya@google.comb3e50f22012-08-20 17:43:08 +0000236 class GrTextureStripAtlas;
237
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000238// Base class for Gr gradient effects
bsalomon@google.coma469c282012-10-24 18:28:34 +0000239class GrGradientEffect : public GrEffect {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000240public:
241
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +0000242 GrGradientEffect(GrContext* ctx,
243 const SkGradientShaderBase& shader,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000244 const SkMatrix& matrix,
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +0000245 SkShader::TileMode tileMode);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000246
247 virtual ~GrGradientEffect();
248
rileya@google.comb3e50f22012-08-20 17:43:08 +0000249 bool useAtlas() const { return SkToBool(-1 != fRow); }
bsalomon@google.com81712882012-11-01 17:12:34 +0000250 SkScalar getYCoord() const { return fYCoord; };
rileya@google.comb3e50f22012-08-20 17:43:08 +0000251
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000252 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000253
bsalomon@google.com82d12232013-09-09 15:36:26 +0000254 enum ColorType {
255 kTwo_ColorType,
256 kThree_ColorType,
257 kTexture_ColorType
258 };
259
260 ColorType getColorType() const { return fColorType; }
261
262 enum PremulType {
263 kBeforeInterp_PremulType,
264 kAfterInterp_PremulType,
265 };
266
267 PremulType getPremulType() const { return fPremulType; }
268
269 const SkColor* getColors(int pos) const {
270 SkASSERT(fColorType != kTexture_ColorType);
271 SkASSERT((pos-1) <= fColorType);
272 return &fColors[pos];
273 }
bsalomon@google.com371e1052013-01-11 21:08:55 +0000274
bsalomon@google.comd4726202012-08-03 14:34:46 +0000275protected:
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000276
bsalomon@google.comd4726202012-08-03 14:34:46 +0000277 /** Populates a pair of arrays with colors and stop info to construct a random gradient.
278 The function decides whether stop values should be used or not. The return value indicates
279 the number of colors, which will be capped by kMaxRandomGradientColors. colors should be
280 sized to be at least kMaxRandomGradientColors. stops is a pointer to an array of at least
281 size kMaxRandomGradientColors. It may be updated to NULL, indicating that NULL should be
282 passed to the gradient factory rather than the array.
283 */
284 static const int kMaxRandomGradientColors = 4;
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000285 static int RandomGradientParams(SkRandom* r,
bsalomon@google.comd4726202012-08-03 14:34:46 +0000286 SkColor colors[kMaxRandomGradientColors],
287 SkScalar** stops,
288 SkShader::TileMode* tm);
289
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000290 virtual bool onIsEqual(const GrEffect& effect) const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000291
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000292 const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
293
bsalomon@google.comd4726202012-08-03 14:34:46 +0000294private:
bsalomon@google.com77af6802013-10-02 13:04:56 +0000295 static const GrCoordSet kCoordSet = kLocal_GrCoordSet;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000296
bsalomon@google.com82d12232013-09-09 15:36:26 +0000297 enum {
298 kMaxAnalyticColors = 3 // if more colors use texture
299 };
300
bsalomon@google.com77af6802013-10-02 13:04:56 +0000301 GrCoordTransform fCoordTransform;
bsalomon@google.com6d003d12012-09-11 15:45:20 +0000302 GrTextureAccess fTextureAccess;
bsalomon@google.com81712882012-11-01 17:12:34 +0000303 SkScalar fYCoord;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000304 GrTextureStripAtlas* fAtlas;
305 int fRow;
bsalomon@google.com371e1052013-01-11 21:08:55 +0000306 bool fIsOpaque;
bsalomon@google.com82d12232013-09-09 15:36:26 +0000307 ColorType fColorType;
308 SkColor fColors[kMaxAnalyticColors];
309 PremulType fPremulType; // This only changes behavior for two and three color special cases.
310 // It is already baked into to the table for texture gradients.
bsalomon@google.coma469c282012-10-24 18:28:34 +0000311 typedef GrEffect INHERITED;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000312
313};
314
315///////////////////////////////////////////////////////////////////////////////
316
bsalomon@google.com8ea78d82012-10-24 20:11:30 +0000317// Base class for GL gradient effects
bsalomon@google.comf78df332012-10-29 12:43:38 +0000318class GrGLGradientEffect : public GrGLEffect {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000319public:
bsalomon@google.com0707c292012-10-25 21:45:42 +0000320 GrGLGradientEffect(const GrBackendEffectFactory& factory);
321 virtual ~GrGLGradientEffect();
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000322
bsalomon@google.comc7818882013-03-20 19:19:53 +0000323 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000324
bsalomon@google.comf78df332012-10-29 12:43:38 +0000325protected:
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000326 enum {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000327 kPremulTypeKeyBitCnt = 1,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000328 kPremulTypeMask = 1,
bsalomon@google.com82d12232013-09-09 15:36:26 +0000329 kPremulBeforeInterpKey = kPremulTypeMask,
330
bsalomon@google.com77af6802013-10-02 13:04:56 +0000331 kTwoColorKey = 2 << kPremulTypeKeyBitCnt,
332 kThreeColorKey = 3 << kPremulTypeKeyBitCnt,
bsalomon@google.com82d12232013-09-09 15:36:26 +0000333 kColorKeyMask = kTwoColorKey | kThreeColorKey,
334 kColorKeyBitCnt = 2,
335
336 // Subclasses must shift any key bits they produce up by this amount
337 // and combine with the result of GenBaseGradientKey.
bsalomon@google.com77af6802013-10-02 13:04:56 +0000338 kBaseKeyBitCnt = (kPremulTypeKeyBitCnt + kColorKeyBitCnt)
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000339 };
340
bsalomon@google.com82d12232013-09-09 15:36:26 +0000341 static GrGradientEffect::ColorType ColorTypeFromKey(EffectKey key){
342 if (kTwoColorKey == (key & kColorKeyMask)) {
343 return GrGradientEffect::kTwo_ColorType;
344 } else if (kThreeColorKey == (key & kColorKeyMask)) {
345 return GrGradientEffect::kThree_ColorType;
346 } else {return GrGradientEffect::kTexture_ColorType;}
347 }
348
349 static GrGradientEffect::PremulType PremulTypeFromKey(EffectKey key){
350 if (kPremulBeforeInterpKey == (key & kPremulTypeMask)) {
351 return GrGradientEffect::kBeforeInterp_PremulType;
352 } else {
353 return GrGradientEffect::kAfterInterp_PremulType;
354 }
355 }
356
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000357 /**
bsalomon@google.com82d12232013-09-09 15:36:26 +0000358 * Subclasses must call this. It will return a value restricted to the lower kBaseKeyBitCnt
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000359 * bits.
360 */
bsalomon@google.com82d12232013-09-09 15:36:26 +0000361 static EffectKey GenBaseGradientKey(const GrDrawEffect&);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000362
bsalomon@google.comf78df332012-10-29 12:43:38 +0000363 // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
364 // should call this method from their emitCode().
bsalomon@google.com82d12232013-09-09 15:36:26 +0000365 void emitUniforms(GrGLShaderBuilder* builder, EffectKey key);
bsalomon@google.comf78df332012-10-29 12:43:38 +0000366
bsalomon@google.com82d12232013-09-09 15:36:26 +0000367
368 // emit code that gets a fragment's color from an expression for t; Has branches for 3 separate
369 // control flows inside -- 2 color gradients, 3 color symmetric gradients (both using
370 // native GLSL mix), and 4+ color gradients that use the traditional texture lookup.
371 void emitColor(GrGLShaderBuilder* builder,
372 const char* gradientTValue,
373 EffectKey key,
374 const char* outputColor,
375 const char* inputColor,
commit-bot@chromium.org3390b9a2013-10-03 15:17:58 +0000376 const TextureSamplerArray& samplers);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000377
378private:
bsalomon@google.com81712882012-11-01 17:12:34 +0000379 SkScalar fCachedYCoord;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000380 GrGLUniformManager::UniformHandle fFSYUni;
bsalomon@google.com82d12232013-09-09 15:36:26 +0000381 GrGLUniformManager::UniformHandle fColorStartUni;
382 GrGLUniformManager::UniformHandle fColorMidUni;
383 GrGLUniformManager::UniformHandle fColorEndUni;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000384
bsalomon@google.comf78df332012-10-29 12:43:38 +0000385 typedef GrGLEffect INHERITED;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000386};
387
388#endif
389
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000390#endif