blob: 6a24d0f15f03540264dd680c9439152d478b27bb [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
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000104 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
105 virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000106 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
commit-bot@chromium.org996402b2014-04-18 14:42:11 +0000131 enum GpuColorType {
132 kTwo_GpuColorType,
133 kThree_GpuColorType, // Symmetric three color
134 kTexture_GpuColorType
135 };
136
137 // Determines and returns the gradient is a two color gradient, symmetric three color gradient
138 // or other (texture gradient). If it is two or symmetric three color, the colors array will
139 // also be filled with the gradient colors
140 GpuColorType getGpuColorType(SkColor colors[3]) const;
141
skia.committer@gmail.com8a777a52014-04-19 03:04:56 +0000142 uint32_t getFlags() const { return fGradFlags; }
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000143
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000144protected:
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000145 SkGradientShaderBase(SkReadBuffer& );
146 virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000147 SK_TO_STRING_OVERRIDE()
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000148
149 SkUnitMapper* fMapper;
150 SkMatrix fPtsToUnit; // set by subclass
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000151 SkMatrix fDstToIndex;
152 SkMatrix::MapXYProc fDstToIndexProc;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000153 TileMode fTileMode;
154 TileProc fTileProc;
155 int fColorCount;
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000156 uint8_t fDstToIndexClass;
157 uint8_t fFlags;
reed@google.com3d3a8602013-05-24 14:58:44 +0000158 uint8_t fGradFlags;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000159
160 struct Rec {
161 SkFixed fPos; // 0...1
162 uint32_t fScale; // (1 << 24) / range
163 };
164 Rec* fRecs;
165
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000166 const uint16_t* getCache16() const;
167 const SkPMColor* getCache32() const;
168
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000169 void commonAsAGradient(GradientInfo*) const;
170
171private:
172 enum {
173 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
174
175 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
176 };
177 SkColor fStorage[(kStorageSize + 3) >> 2];
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000178 SkColor* fOrigColors; // original colors, before modulation by paint in setContext
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000179 bool fColorsAreOpaque;
180
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000181 mutable uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
182 mutable SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000183
commit-bot@chromium.org53783b02014-04-17 21:09:49 +0000184 mutable uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
185 mutable SkMallocPixelRef* fCache32PixelRef;
186 mutable unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
187
188 static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
189 static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
190 U8CPU alpha, uint32_t gradFlags);
191 void setCacheAlpha(U8CPU alpha) const;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000192 void initCommon();
193
194 typedef SkShader INHERITED;
195};
196
reed@google.com55853db2013-02-01 19:34:59 +0000197static inline int init_dither_toggle(int x, int y) {
reed@google.com60040292013-02-04 18:21:23 +0000198 x &= 1;
199 y = (y & 1) << 1;
200 return (x | y) * SkGradientShaderBase::kDitherStride32;
reed@google.com55853db2013-02-01 19:34:59 +0000201}
202
203static inline int next_dither_toggle(int toggle) {
204 return toggle ^ SkGradientShaderBase::kDitherStride32;
205}
206
207static inline int init_dither_toggle16(int x, int y) {
208 return ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride16;
209}
210
211static inline int next_dither_toggle16(int toggle) {
212 return toggle ^ SkGradientShaderBase::kDitherStride16;
213}
214
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000215///////////////////////////////////////////////////////////////////////////////
216
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000217#if SK_SUPPORT_GPU
218
bsalomon@google.com77af6802013-10-02 13:04:56 +0000219#include "GrCoordTransform.h"
bsalomon@google.comd698f772012-10-25 13:22:00 +0000220#include "gl/GrGLEffect.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000221
bsalomon@google.com08283af2012-10-26 13:01:20 +0000222class GrEffectStage;
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000223class GrBackendEffectFactory;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000224
225/*
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000226 * The interpretation of the texture matrix depends on the sample mode. The
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000227 * texture matrix is applied both when the texture coordinates are explicit
228 * and when vertex positions are used as texture coordinates. In the latter
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000229 * case the texture matrix is applied to the pre-view-matrix position
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000230 * values.
231 *
232 * Normal SampleMode
233 * The post-matrix texture coordinates are in normalize space with (0,0) at
234 * the top-left and (1,1) at the bottom right.
235 * RadialGradient
236 * The matrix specifies the radial gradient parameters.
237 * (0,0) in the post-matrix space is center of the radial gradient.
238 * Radial2Gradient
239 * Matrix transforms to space where first circle is centered at the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000240 * origin. The second circle will be centered (x, 0) where x may be
241 * 0 and is provided by setRadial2Params. The post-matrix space is
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000242 * normalized such that 1 is the second radius - first radius.
243 * SweepGradient
244 * The angle from the origin of texture coordinates in post-matrix space
245 * determines the gradient value.
246 */
247
rileya@google.comb3e50f22012-08-20 17:43:08 +0000248 class GrTextureStripAtlas;
249
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000250// Base class for Gr gradient effects
bsalomon@google.coma469c282012-10-24 18:28:34 +0000251class GrGradientEffect : public GrEffect {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000252public:
253
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +0000254 GrGradientEffect(GrContext* ctx,
255 const SkGradientShaderBase& shader,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000256 const SkMatrix& matrix,
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +0000257 SkShader::TileMode tileMode);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000258
259 virtual ~GrGradientEffect();
260
rileya@google.comb3e50f22012-08-20 17:43:08 +0000261 bool useAtlas() const { return SkToBool(-1 != fRow); }
bsalomon@google.com81712882012-11-01 17:12:34 +0000262 SkScalar getYCoord() const { return fYCoord; };
rileya@google.comb3e50f22012-08-20 17:43:08 +0000263
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000264 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000265
commit-bot@chromium.org996402b2014-04-18 14:42:11 +0000266 SkGradientShaderBase::GpuColorType getColorType() const { return fColorType; }
bsalomon@google.com82d12232013-09-09 15:36:26 +0000267
268 enum PremulType {
269 kBeforeInterp_PremulType,
270 kAfterInterp_PremulType,
271 };
272
273 PremulType getPremulType() const { return fPremulType; }
274
275 const SkColor* getColors(int pos) const {
commit-bot@chromium.org996402b2014-04-18 14:42:11 +0000276 SkASSERT(fColorType != SkGradientShaderBase::kTexture_GpuColorType);
bsalomon@google.com82d12232013-09-09 15:36:26 +0000277 SkASSERT((pos-1) <= fColorType);
278 return &fColors[pos];
279 }
bsalomon@google.com371e1052013-01-11 21:08:55 +0000280
bsalomon@google.comd4726202012-08-03 14:34:46 +0000281protected:
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000282
bsalomon@google.comd4726202012-08-03 14:34:46 +0000283 /** Populates a pair of arrays with colors and stop info to construct a random gradient.
284 The function decides whether stop values should be used or not. The return value indicates
285 the number of colors, which will be capped by kMaxRandomGradientColors. colors should be
286 sized to be at least kMaxRandomGradientColors. stops is a pointer to an array of at least
287 size kMaxRandomGradientColors. It may be updated to NULL, indicating that NULL should be
288 passed to the gradient factory rather than the array.
289 */
290 static const int kMaxRandomGradientColors = 4;
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000291 static int RandomGradientParams(SkRandom* r,
bsalomon@google.comd4726202012-08-03 14:34:46 +0000292 SkColor colors[kMaxRandomGradientColors],
293 SkScalar** stops,
294 SkShader::TileMode* tm);
295
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000296 virtual bool onIsEqual(const GrEffect& effect) const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000297
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000298 const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
299
bsalomon@google.comd4726202012-08-03 14:34:46 +0000300private:
bsalomon@google.com77af6802013-10-02 13:04:56 +0000301 static const GrCoordSet kCoordSet = kLocal_GrCoordSet;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000302
bsalomon@google.com77af6802013-10-02 13:04:56 +0000303 GrCoordTransform fCoordTransform;
bsalomon@google.com6d003d12012-09-11 15:45:20 +0000304 GrTextureAccess fTextureAccess;
bsalomon@google.com81712882012-11-01 17:12:34 +0000305 SkScalar fYCoord;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000306 GrTextureStripAtlas* fAtlas;
307 int fRow;
bsalomon@google.com371e1052013-01-11 21:08:55 +0000308 bool fIsOpaque;
commit-bot@chromium.org996402b2014-04-18 14:42:11 +0000309 SkGradientShaderBase::GpuColorType fColorType;
310 SkColor fColors[3]; // More than 3 colors we use texture
bsalomon@google.com82d12232013-09-09 15:36:26 +0000311 PremulType fPremulType; // This only changes behavior for two and three color special cases.
312 // It is already baked into to the table for texture gradients.
bsalomon@google.coma469c282012-10-24 18:28:34 +0000313 typedef GrEffect INHERITED;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000314
315};
316
317///////////////////////////////////////////////////////////////////////////////
318
bsalomon@google.com8ea78d82012-10-24 20:11:30 +0000319// Base class for GL gradient effects
bsalomon@google.comf78df332012-10-29 12:43:38 +0000320class GrGLGradientEffect : public GrGLEffect {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000321public:
bsalomon@google.com0707c292012-10-25 21:45:42 +0000322 GrGLGradientEffect(const GrBackendEffectFactory& factory);
323 virtual ~GrGLGradientEffect();
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000324
bsalomon@google.comc7818882013-03-20 19:19:53 +0000325 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000326
bsalomon@google.comf78df332012-10-29 12:43:38 +0000327protected:
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000328 enum {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000329 kPremulTypeKeyBitCnt = 1,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000330 kPremulTypeMask = 1,
bsalomon@google.com82d12232013-09-09 15:36:26 +0000331 kPremulBeforeInterpKey = kPremulTypeMask,
332
bsalomon@google.com77af6802013-10-02 13:04:56 +0000333 kTwoColorKey = 2 << kPremulTypeKeyBitCnt,
334 kThreeColorKey = 3 << kPremulTypeKeyBitCnt,
bsalomon@google.com82d12232013-09-09 15:36:26 +0000335 kColorKeyMask = kTwoColorKey | kThreeColorKey,
336 kColorKeyBitCnt = 2,
337
338 // Subclasses must shift any key bits they produce up by this amount
339 // and combine with the result of GenBaseGradientKey.
bsalomon@google.com77af6802013-10-02 13:04:56 +0000340 kBaseKeyBitCnt = (kPremulTypeKeyBitCnt + kColorKeyBitCnt)
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000341 };
342
commit-bot@chromium.org996402b2014-04-18 14:42:11 +0000343 static SkGradientShaderBase::GpuColorType ColorTypeFromKey(EffectKey key){
bsalomon@google.com82d12232013-09-09 15:36:26 +0000344 if (kTwoColorKey == (key & kColorKeyMask)) {
commit-bot@chromium.org996402b2014-04-18 14:42:11 +0000345 return SkGradientShaderBase::kTwo_GpuColorType;
bsalomon@google.com82d12232013-09-09 15:36:26 +0000346 } else if (kThreeColorKey == (key & kColorKeyMask)) {
commit-bot@chromium.org996402b2014-04-18 14:42:11 +0000347 return SkGradientShaderBase::kThree_GpuColorType;
348 } else {return SkGradientShaderBase::kTexture_GpuColorType;}
bsalomon@google.com82d12232013-09-09 15:36:26 +0000349 }
350
351 static GrGradientEffect::PremulType PremulTypeFromKey(EffectKey key){
352 if (kPremulBeforeInterpKey == (key & kPremulTypeMask)) {
353 return GrGradientEffect::kBeforeInterp_PremulType;
354 } else {
355 return GrGradientEffect::kAfterInterp_PremulType;
356 }
357 }
358
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000359 /**
bsalomon@google.com82d12232013-09-09 15:36:26 +0000360 * Subclasses must call this. It will return a value restricted to the lower kBaseKeyBitCnt
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000361 * bits.
362 */
bsalomon@google.com82d12232013-09-09 15:36:26 +0000363 static EffectKey GenBaseGradientKey(const GrDrawEffect&);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000364
bsalomon@google.comf78df332012-10-29 12:43:38 +0000365 // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
366 // should call this method from their emitCode().
bsalomon@google.com82d12232013-09-09 15:36:26 +0000367 void emitUniforms(GrGLShaderBuilder* builder, EffectKey key);
bsalomon@google.comf78df332012-10-29 12:43:38 +0000368
bsalomon@google.com82d12232013-09-09 15:36:26 +0000369
370 // emit code that gets a fragment's color from an expression for t; Has branches for 3 separate
371 // control flows inside -- 2 color gradients, 3 color symmetric gradients (both using
372 // native GLSL mix), and 4+ color gradients that use the traditional texture lookup.
373 void emitColor(GrGLShaderBuilder* builder,
374 const char* gradientTValue,
375 EffectKey key,
376 const char* outputColor,
377 const char* inputColor,
commit-bot@chromium.org3390b9a2013-10-03 15:17:58 +0000378 const TextureSamplerArray& samplers);
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000379
380private:
bsalomon@google.com81712882012-11-01 17:12:34 +0000381 SkScalar fCachedYCoord;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000382 GrGLUniformManager::UniformHandle fFSYUni;
bsalomon@google.com82d12232013-09-09 15:36:26 +0000383 GrGLUniformManager::UniformHandle fColorStartUni;
384 GrGLUniformManager::UniformHandle fColorMidUni;
385 GrGLUniformManager::UniformHandle fColorEndUni;
rileya@google.comb3e50f22012-08-20 17:43:08 +0000386
bsalomon@google.comf78df332012-10-29 12:43:38 +0000387 typedef GrGLEffect INHERITED;
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000388};
389
390#endif
391
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000392#endif