blob: e01609462b6ea22038f24562a8d982383a03c362 [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"
commit-bot@chromium.org001f4ed2014-04-16 10:16:39 +000022#include "SkOnce.h"
rileya@google.com1c6d64b2012-07-27 15:49:05 +000023
humper@google.com05af1af2013-01-07 16:47:43 +000024static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
rileya@google.com1c6d64b2012-07-27 15:49:05 +000025 int count) {
26 if (count > 0) {
27 if (v0 == v1) {
28 sk_memset32(dst, v0, count);
29 } else {
30 int pairs = count >> 1;
31 for (int i = 0; i < pairs; i++) {
32 *dst++ = v0;
33 *dst++ = v1;
34 }
35 if (count & 1) {
36 *dst = v0;
37 }
38 }
39 }
40}
41
42// Clamp
43
humper@google.com05af1af2013-01-07 16:47:43 +000044static inline SkFixed clamp_tileproc(SkFixed x) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +000045 return SkClampMax(x, 0xFFFF);
46}
47
48// Repeat
49
humper@google.com05af1af2013-01-07 16:47:43 +000050static inline SkFixed repeat_tileproc(SkFixed x) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +000051 return x & 0xFFFF;
52}
53
54// Mirror
55
56// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
57// See http://code.google.com/p/skia/issues/detail?id=472
58#if defined(_MSC_VER) && (_MSC_VER >= 1600)
59#pragma optimize("", off)
60#endif
61
62static inline SkFixed mirror_tileproc(SkFixed x) {
63 int s = x << 15 >> 31;
64 return (x ^ s) & 0xFFFF;
65}
66
67#if defined(_MSC_VER) && (_MSC_VER >= 1600)
68#pragma optimize("", on)
69#endif
70
71///////////////////////////////////////////////////////////////////////////////
72
73typedef SkFixed (*TileProc)(SkFixed);
74
75///////////////////////////////////////////////////////////////////////////////
76
77static const TileProc gTileProcs[] = {
78 clamp_tileproc,
79 repeat_tileproc,
80 mirror_tileproc
81};
82
83///////////////////////////////////////////////////////////////////////////////
84
85class SkGradientShaderBase : public SkShader {
86public:
reed@google.com437d6eb2013-05-23 19:03:05 +000087 struct Descriptor {
88 Descriptor() {
89 sk_bzero(this, sizeof(*this));
90 fTileMode = SkShader::kClamp_TileMode;
91 }
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +000092
reed@google.com437d6eb2013-05-23 19:03:05 +000093 const SkColor* fColors;
94 const SkScalar* fPos;
95 int fCount;
96 SkShader::TileMode fTileMode;
97 SkUnitMapper* fMapper;
reed@google.com3d3a8602013-05-24 14:58:44 +000098 uint32_t fFlags;
reed@google.com437d6eb2013-05-23 19:03:05 +000099 };
100
101public:
102 SkGradientShaderBase(const Descriptor& desc);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000103 virtual ~SkGradientShaderBase();
104
commit-bot@chromium.org001f4ed2014-04-16 10:16:39 +0000105 // The cache is initialized on-demand when getCache16/32 is called.
106 class GradientShaderCache : public SkRefCnt {
107 public:
108 GradientShaderCache(U8CPU alpha, const SkGradientShaderBase& shader);
109 ~GradientShaderCache();
110
111 const uint16_t* getCache16();
112 const SkPMColor* getCache32();
113
114 SkMallocPixelRef* getCache32PixelRef() const { return fCache32PixelRef; }
115
116 unsigned getAlpha() const { return fCacheAlpha; }
117
118 private:
119 // Working pointers. If either is NULL, we need to recompute the corresponding cache values.
120 uint16_t* fCache16;
121 SkPMColor* fCache32;
122
123 uint16_t* fCache16Storage; // Storage for fCache16, allocated on demand.
124 SkMallocPixelRef* fCache32PixelRef;
125 const unsigned fCacheAlpha; // The alpha value we used when we computed the cache.
126 // Larger than 8bits so we can store uninitialized
127 // value.
128
129 const SkGradientShaderBase& fShader;
130
131 // Make sure we only initialize the caches once.
132 bool fCache16Inited, fCache32Inited;
133 SkMutex fCache16Mutex, fCache32Mutex;
134
135 static void initCache16(GradientShaderCache* cache);
136 static void initCache32(GradientShaderCache* cache);
137
138 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
139 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
140 U8CPU alpha, uint32_t gradFlags);
141 };
142
143 class GradientShaderBaseContext : public SkShader::Context {
144 public:
145 GradientShaderBaseContext(const SkGradientShaderBase& shader, const SkBitmap& device,
146 const SkPaint& paint, const SkMatrix& matrix);
147 ~GradientShaderBaseContext() {}
148
149 virtual uint32_t getFlags() const SK_OVERRIDE { return fFlags; }
150
151 protected:
152 SkMatrix fDstToIndex;
153 SkMatrix::MapXYProc fDstToIndexProc;
154 uint8_t fDstToIndexClass;
155 uint8_t fFlags;
156
157 SkAutoTUnref<GradientShaderCache> fCache;
158
159 private:
160 typedef SkShader::Context INHERITED;
161 };
162
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000163 virtual bool isOpaque() const SK_OVERRIDE;
164
165 void getGradientTableBitmap(SkBitmap*) const;
166
167 enum {
168 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
169 /// it, use a larger cache.
170 kCache16Bits = 8,
reed@google.com3c2102c2013-02-01 12:59:40 +0000171 kCache16Count = (1 << kCache16Bits),
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000172 kCache16Shift = 16 - kCache16Bits,
173 kSqrt16Shift = 8 - kCache16Bits,
174
175 /// Seems like enough for visual accuracy. TODO: if pos[] deserves
176 /// it, use a larger cache.
177 kCache32Bits = 8,
reed@google.com60040292013-02-04 18:21:23 +0000178 kCache32Count = (1 << kCache32Bits),
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000179 kCache32Shift = 16 - kCache32Bits,
180 kSqrt32Shift = 8 - kCache32Bits,
181
182 /// This value is used to *read* the dither cache; it may be 0
183 /// if dithering is disabled.
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000184 kDitherStride32 = kCache32Count,
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000185 kDitherStride16 = kCache16Count,
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000186 };
187
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000188protected:
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000189 SkGradientShaderBase(SkReadBuffer& );
190 virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000191 SK_TO_STRING_OVERRIDE()
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000192
193 SkUnitMapper* fMapper;
194 SkMatrix fPtsToUnit; // set by subclass
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000195 TileMode fTileMode;
196 TileProc fTileProc;
197 int fColorCount;
reed@google.com3d3a8602013-05-24 14:58:44 +0000198 uint8_t fGradFlags;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000199
200 struct Rec {
201 SkFixed fPos; // 0...1
202 uint32_t fScale; // (1 << 24) / range
203 };
204 Rec* fRecs;
205
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000206 void commonAsAGradient(GradientInfo*) const;
207
208private:
209 enum {
210 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
211
212 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
213 };
214 SkColor fStorage[(kStorageSize + 3) >> 2];
commit-bot@chromium.org001f4ed2014-04-16 10:16:39 +0000215 SkColor* fOrigColors; // original colors, before modulation by paint in context.
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000216 bool fColorsAreOpaque;
217
commit-bot@chromium.org001f4ed2014-04-16 10:16:39 +0000218 GradientShaderCache* getCache(U8CPU alpha) const;
219 mutable SkMutex fCacheMutex;
220 mutable SkAutoTUnref<GradientShaderCache> fCache;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000221
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000222 void initCommon();
223
224 typedef SkShader INHERITED;
225};
226
reed@google.com55853db2013-02-01 19:34:59 +0000227static inline int init_dither_toggle(int x, int y) {
reed@google.com60040292013-02-04 18:21:23 +0000228 x &= 1;
229 y = (y & 1) << 1;
230 return (x | y) * SkGradientShaderBase::kDitherStride32;
reed@google.com55853db2013-02-01 19:34:59 +0000231}
232
233static inline int next_dither_toggle(int toggle) {
234 return toggle ^ SkGradientShaderBase::kDitherStride32;
235}
236
237static inline int init_dither_toggle16(int x, int y) {
238 return ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride16;
239}
240
241static inline int next_dither_toggle16(int toggle) {
242 return toggle ^ SkGradientShaderBase::kDitherStride16;
243}
244
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000245///////////////////////////////////////////////////////////////////////////////
246
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000247#if SK_SUPPORT_GPU
248
bsalomon@google.com77af6802013-10-02 13:04:56 +0000249#include "GrCoordTransform.h"
bsalomon@google.comd698f772012-10-25 13:22:00 +0000250#include "gl/GrGLEffect.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000251
bsalomon@google.com08283af2012-10-26 13:01:20 +0000252class GrEffectStage;
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000253class GrBackendEffectFactory;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000254
255/*
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000256 * The interpretation of the texture matrix depends on the sample mode. The
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000257 * texture matrix is applied both when the texture coordinates are explicit
258 * and when vertex positions are used as texture coordinates. In the latter
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000259 * case the texture matrix is applied to the pre-view-matrix position
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000260 * values.
261 *
262 * Normal SampleMode
263 * The post-matrix texture coordinates are in normalize space with (0,0) at
264 * the top-left and (1,1) at the bottom right.
265 * RadialGradient
266 * The matrix specifies the radial gradient parameters.
267 * (0,0) in the post-matrix space is center of the radial gradient.
268 * Radial2Gradient
269 * Matrix transforms to space where first circle is centered at the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000270 * origin. The second circle will be centered (x, 0) where x may be
271 * 0 and is provided by setRadial2Params. The post-matrix space is
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000272 * normalized such that 1 is the second radius - first radius.
273 * SweepGradient
274 * The angle from the origin of texture coordinates in post-matrix space
275 * determines the gradient value.
276 */
277
rileya@google.comb3e50f22012-08-20 17:43:08 +0000278 class GrTextureStripAtlas;
279
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000280// Base class for Gr gradient effects
bsalomon@google.coma469c282012-10-24 18:28:34 +0000281class GrGradientEffect : public GrEffect {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000282public:
283
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +0000284 GrGradientEffect(GrContext* ctx,
285 const SkGradientShaderBase& shader,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000286 const SkMatrix& matrix,
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +0000287 SkShader::TileMode tileMode);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000288
289 virtual ~GrGradientEffect();
290
rileya@google.comb3e50f22012-08-20 17:43:08 +0000291 bool useAtlas() const { return SkToBool(-1 != fRow); }
bsalomon@google.com81712882012-11-01 17:12:34 +0000292 SkScalar getYCoord() const { return fYCoord; };
rileya@google.comb3e50f22012-08-20 17:43:08 +0000293
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000294 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000295
bsalomon@google.com82d12232013-09-09 15:36:26 +0000296 enum ColorType {
297 kTwo_ColorType,
298 kThree_ColorType,
299 kTexture_ColorType
300 };
301
302 ColorType getColorType() const { return fColorType; }
303
304 enum PremulType {
305 kBeforeInterp_PremulType,
306 kAfterInterp_PremulType,
307 };
308
309 PremulType getPremulType() const { return fPremulType; }
310
311 const SkColor* getColors(int pos) const {
312 SkASSERT(fColorType != kTexture_ColorType);
313 SkASSERT((pos-1) <= fColorType);
314 return &fColors[pos];
315 }
bsalomon@google.com371e1052013-01-11 21:08:55 +0000316
bsalomon@google.comd4726202012-08-03 14:34:46 +0000317protected:
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000318
bsalomon@google.comd4726202012-08-03 14:34:46 +0000319 /** Populates a pair of arrays with colors and stop info to construct a random gradient.
320 The function decides whether stop values should be used or not. The return value indicates
321 the number of colors, which will be capped by kMaxRandomGradientColors. colors should be
322 sized to be at least kMaxRandomGradientColors. stops is a pointer to an array of at least
323 size kMaxRandomGradientColors. It may be updated to NULL, indicating that NULL should be
324 passed to the gradient factory rather than the array.
325 */
326 static const int kMaxRandomGradientColors = 4;
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000327 static int RandomGradientParams(SkRandom* r,
bsalomon@google.comd4726202012-08-03 14:34:46 +0000328 SkColor colors[kMaxRandomGradientColors],
329 SkScalar** stops,
330 SkShader::TileMode* tm);
331
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000332 virtual bool onIsEqual(const GrEffect& effect) const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000333
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000334 const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
335
bsalomon@google.comd4726202012-08-03 14:34:46 +0000336private:
bsalomon@google.com77af6802013-10-02 13:04:56 +0000337 static const GrCoordSet kCoordSet = kLocal_GrCoordSet;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000338
bsalomon@google.com82d12232013-09-09 15:36:26 +0000339 enum {
340 kMaxAnalyticColors = 3 // if more colors use texture
341 };
342
bsalomon@google.com77af6802013-10-02 13:04:56 +0000343 GrCoordTransform fCoordTransform;
bsalomon@google.com6d003d12012-09-11 15:45:20 +0000344 GrTextureAccess fTextureAccess;
bsalomon@google.com81712882012-11-01 17:12:34 +0000345 SkScalar fYCoord;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000346 GrTextureStripAtlas* fAtlas;
347 int fRow;
bsalomon@google.com371e1052013-01-11 21:08:55 +0000348 bool fIsOpaque;
bsalomon@google.com82d12232013-09-09 15:36:26 +0000349 ColorType fColorType;
350 SkColor fColors[kMaxAnalyticColors];
351 PremulType fPremulType; // This only changes behavior for two and three color special cases.
352 // It is already baked into to the table for texture gradients.
bsalomon@google.coma469c282012-10-24 18:28:34 +0000353 typedef GrEffect INHERITED;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000354
355};
356
357///////////////////////////////////////////////////////////////////////////////
358
bsalomon@google.com8ea78d82012-10-24 20:11:30 +0000359// Base class for GL gradient effects
bsalomon@google.comf78df332012-10-29 12:43:38 +0000360class GrGLGradientEffect : public GrGLEffect {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000361public:
bsalomon@google.com0707c292012-10-25 21:45:42 +0000362 GrGLGradientEffect(const GrBackendEffectFactory& factory);
363 virtual ~GrGLGradientEffect();
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000364
bsalomon@google.comc7818882013-03-20 19:19:53 +0000365 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000366
bsalomon@google.comf78df332012-10-29 12:43:38 +0000367protected:
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000368 enum {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000369 kPremulTypeKeyBitCnt = 1,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000370 kPremulTypeMask = 1,
bsalomon@google.com82d12232013-09-09 15:36:26 +0000371 kPremulBeforeInterpKey = kPremulTypeMask,
372
bsalomon@google.com77af6802013-10-02 13:04:56 +0000373 kTwoColorKey = 2 << kPremulTypeKeyBitCnt,
374 kThreeColorKey = 3 << kPremulTypeKeyBitCnt,
bsalomon@google.com82d12232013-09-09 15:36:26 +0000375 kColorKeyMask = kTwoColorKey | kThreeColorKey,
376 kColorKeyBitCnt = 2,
377
378 // Subclasses must shift any key bits they produce up by this amount
379 // and combine with the result of GenBaseGradientKey.
bsalomon@google.com77af6802013-10-02 13:04:56 +0000380 kBaseKeyBitCnt = (kPremulTypeKeyBitCnt + kColorKeyBitCnt)
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000381 };
382
bsalomon@google.com82d12232013-09-09 15:36:26 +0000383 static GrGradientEffect::ColorType ColorTypeFromKey(EffectKey key){
384 if (kTwoColorKey == (key & kColorKeyMask)) {
385 return GrGradientEffect::kTwo_ColorType;
386 } else if (kThreeColorKey == (key & kColorKeyMask)) {
387 return GrGradientEffect::kThree_ColorType;
388 } else {return GrGradientEffect::kTexture_ColorType;}
389 }
390
391 static GrGradientEffect::PremulType PremulTypeFromKey(EffectKey key){
392 if (kPremulBeforeInterpKey == (key & kPremulTypeMask)) {
393 return GrGradientEffect::kBeforeInterp_PremulType;
394 } else {
395 return GrGradientEffect::kAfterInterp_PremulType;
396 }
397 }
398
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000399 /**
bsalomon@google.com82d12232013-09-09 15:36:26 +0000400 * Subclasses must call this. It will return a value restricted to the lower kBaseKeyBitCnt
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000401 * bits.
402 */
bsalomon@google.com82d12232013-09-09 15:36:26 +0000403 static EffectKey GenBaseGradientKey(const GrDrawEffect&);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000404
bsalomon@google.comf78df332012-10-29 12:43:38 +0000405 // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
406 // should call this method from their emitCode().
bsalomon@google.com82d12232013-09-09 15:36:26 +0000407 void emitUniforms(GrGLShaderBuilder* builder, EffectKey key);
bsalomon@google.comf78df332012-10-29 12:43:38 +0000408
bsalomon@google.com82d12232013-09-09 15:36:26 +0000409
410 // emit code that gets a fragment's color from an expression for t; Has branches for 3 separate
411 // control flows inside -- 2 color gradients, 3 color symmetric gradients (both using
412 // native GLSL mix), and 4+ color gradients that use the traditional texture lookup.
413 void emitColor(GrGLShaderBuilder* builder,
414 const char* gradientTValue,
415 EffectKey key,
416 const char* outputColor,
417 const char* inputColor,
commit-bot@chromium.org3390b9a2013-10-03 15:17:58 +0000418 const TextureSamplerArray& samplers);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000419
420private:
bsalomon@google.com81712882012-11-01 17:12:34 +0000421 SkScalar fCachedYCoord;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000422 GrGLUniformManager::UniformHandle fFSYUni;
bsalomon@google.com82d12232013-09-09 15:36:26 +0000423 GrGLUniformManager::UniformHandle fColorStartUni;
424 GrGLUniformManager::UniformHandle fColorMidUni;
425 GrGLUniformManager::UniformHandle fColorEndUni;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000426
bsalomon@google.comf78df332012-10-29 12:43:38 +0000427 typedef GrGLEffect INHERITED;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000428};
429
430#endif
431
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000432#endif