1 /*
2 * Copyright 2016 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 #include "Sk4fLinearGradient.h"
9
10 namespace {
11
premul_4f(const Sk4f & c)12 Sk4f premul_4f(const Sk4f& c) {
13 const float alpha = c[SkPM4f::A];
14 // FIXME: portable swizzle?
15 return c * Sk4f(alpha, alpha, alpha, 1);
16 }
17
18 template <bool do_premul>
trunc_from_255(const Sk4f & c)19 SkPMColor trunc_from_255(const Sk4f& c) {
20 SkPMColor pmc;
21 SkNx_cast<uint8_t>(c).store(&pmc);
22 if (do_premul) {
23 pmc = SkPreMultiplyARGB(SkGetPackedA32(pmc), SkGetPackedR32(pmc),
24 SkGetPackedG32(pmc), SkGetPackedB32(pmc));
25 }
26 return pmc;
27 }
28
29 template<typename DstType, bool do_premul>
30 void fill(const Sk4f& c, DstType* dst, int n);
31
32 template<>
fill(const Sk4f & c,SkPM4f * dst,int n)33 void fill<SkPM4f, false>(const Sk4f& c, SkPM4f* dst, int n) {
34 while (n > 0) {
35 c.store(dst++);
36 n--;
37 }
38 }
39
40 template<>
fill(const Sk4f & c,SkPM4f * dst,int n)41 void fill<SkPM4f, true>(const Sk4f& c, SkPM4f* dst, int n) {
42 fill<SkPM4f, false>(premul_4f(c), dst, n);
43 }
44
45 template<>
fill(const Sk4f & c,SkPMColor * dst,int n)46 void fill<SkPMColor, false>(const Sk4f& c, SkPMColor* dst, int n) {
47 sk_memset32(dst, trunc_from_255<false>(c), n);
48 }
49
50 template<>
fill(const Sk4f & c,SkPMColor * dst,int n)51 void fill<SkPMColor, true>(const Sk4f& c, SkPMColor* dst, int n) {
52 sk_memset32(dst, trunc_from_255<true>(c), n);
53 }
54
55 template<typename DstType, bool do_premul>
56 void store(const Sk4f& color, DstType* dst);
57
58 template<>
store(const Sk4f & c,SkPM4f * dst)59 void store<SkPM4f, false>(const Sk4f& c, SkPM4f* dst) {
60 c.store(dst);
61 }
62
63 template<>
store(const Sk4f & c,SkPM4f * dst)64 void store<SkPM4f, true>(const Sk4f& c, SkPM4f* dst) {
65 store<SkPM4f, false>(premul_4f(c), dst);
66 }
67
68 template<>
store(const Sk4f & c,SkPMColor * dst)69 void store<SkPMColor, false>(const Sk4f& c, SkPMColor* dst) {
70 *dst = trunc_from_255<false>(c);
71 }
72
73 template<>
store(const Sk4f & c,SkPMColor * dst)74 void store<SkPMColor, true>(const Sk4f& c, SkPMColor* dst) {
75 *dst = trunc_from_255<true>(c);
76 }
77
78 template<typename DstType, bool do_premul>
store4x(const Sk4f & c0,const Sk4f & c1,const Sk4f & c2,const Sk4f & c3,DstType * dst)79 void store4x(const Sk4f& c0,
80 const Sk4f& c1,
81 const Sk4f& c2,
82 const Sk4f& c3,
83 DstType* dst) {
84 store<DstType, do_premul>(c0, dst++);
85 store<DstType, do_premul>(c1, dst++);
86 store<DstType, do_premul>(c2, dst++);
87 store<DstType, do_premul>(c3, dst++);
88 }
89
90 template<>
store4x(const Sk4f & c0,const Sk4f & c1,const Sk4f & c2,const Sk4f & c3,SkPMColor * dst)91 void store4x<SkPMColor, false>(const Sk4f& c0,
92 const Sk4f& c1,
93 const Sk4f& c2,
94 const Sk4f& c3,
95 SkPMColor* dst) {
96 Sk4f_ToBytes((uint8_t*)dst, c0, c1, c2, c3);
97 }
98
99 template<typename DstType, bool do_premul>
ramp(const Sk4f & c,const Sk4f & dc,DstType * dst,int n)100 void ramp(const Sk4f& c, const Sk4f& dc, DstType* dst, int n) {
101 SkASSERT(n > 0);
102
103 const Sk4f dc2 = dc + dc;
104 const Sk4f dc4 = dc2 + dc2;
105
106 Sk4f c0 = c ;
107 Sk4f c1 = c + dc;
108 Sk4f c2 = c0 + dc2;
109 Sk4f c3 = c1 + dc2;
110
111 while (n >= 4) {
112 store4x<DstType, do_premul>(c0, c1, c2, c3, dst);
113 dst += 4;
114
115 c0 = c0 + dc4;
116 c1 = c1 + dc4;
117 c2 = c2 + dc4;
118 c3 = c3 + dc4;
119 n -= 4;
120 }
121 if (n & 2) {
122 store<DstType, do_premul>(c0, dst++);
123 store<DstType, do_premul>(c1, dst++);
124 c0 = c0 + dc2;
125 }
126 if (n & 1) {
127 store<DstType, do_premul>(c0, dst);
128 }
129 }
130
131 template<SkShader::TileMode>
132 SkScalar pinFx(SkScalar);
133
134 template<>
pinFx(SkScalar fx)135 SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
136 return fx;
137 }
138
139 template<>
pinFx(SkScalar fx)140 SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
141 const SkScalar f = SkScalarFraction(fx);
142 return f < 0 ? f + 1 : f;
143 }
144
145 template<>
pinFx(SkScalar fx)146 SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
147 const SkScalar f = SkScalarMod(fx, 2.0f);
148 return f < 0 ? f + 2 : f;
149 }
150
151 template<typename DstType>
152 float dst_component_scale();
153
154 template<>
dst_component_scale()155 float dst_component_scale<SkPM4f>() {
156 return 1;
157 }
158
159 template<>
dst_component_scale()160 float dst_component_scale<SkPMColor>() {
161 return 255;
162 }
163
164 } // anonymous namespace
165
166 SkLinearGradient::
LinearGradient4fContext(const SkLinearGradient & shader,const ContextRec & rec)167 LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
168 const ContextRec& rec)
169 : INHERITED(shader, rec) {}
170
171 void SkLinearGradient::
shadeSpan(int x,int y,SkPMColor dst[],int count)172 LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
173 // TODO: plumb dithering
174 SkASSERT(count > 0);
175 if (fColorsArePremul) {
176 this->shadePremulSpan<SkPMColor, false>(x, y, dst, count);
177 } else {
178 this->shadePremulSpan<SkPMColor, true>(x, y, dst, count);
179 }
180 }
181
182 void SkLinearGradient::
shadeSpan4f(int x,int y,SkPM4f dst[],int count)183 LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
184 // TONOTDO: plumb dithering
185 SkASSERT(count > 0);
186 if (fColorsArePremul) {
187 this->shadePremulSpan<SkPM4f, false>(x, y, dst, count);
188 } else {
189 this->shadePremulSpan<SkPM4f, true>(x, y, dst, count);
190 }
191 }
192
193 template<typename DstType, bool do_premul>
194 void SkLinearGradient::
shadePremulSpan(int x,int y,DstType dst[],int count) const195 LinearGradient4fContext::shadePremulSpan(int x, int y,
196 DstType dst[],
197 int count) const {
198 const SkLinearGradient& shader =
199 static_cast<const SkLinearGradient&>(fShader);
200 switch (shader.fTileMode) {
201 case kClamp_TileMode:
202 this->shadeSpanInternal<DstType,
203 do_premul,
204 kClamp_TileMode>(x, y, dst, count);
205 break;
206 case kRepeat_TileMode:
207 this->shadeSpanInternal<DstType,
208 do_premul,
209 kRepeat_TileMode>(x, y, dst, count);
210 break;
211 case kMirror_TileMode:
212 this->shadeSpanInternal<DstType,
213 do_premul,
214 kMirror_TileMode>(x, y, dst, count);
215 break;
216 }
217 }
218
219 template<typename DstType, bool do_premul, SkShader::TileMode tileMode>
220 void SkLinearGradient::
shadeSpanInternal(int x,int y,DstType dst[],int count) const221 LinearGradient4fContext::shadeSpanInternal(int x, int y,
222 DstType dst[],
223 int count) const {
224 SkPoint pt;
225 fDstToPosProc(fDstToPos,
226 x + SK_ScalarHalf,
227 y + SK_ScalarHalf,
228 &pt);
229 const SkScalar fx = pinFx<tileMode>(pt.x());
230 const SkScalar dx = fDstToPos.getScaleX();
231 LinearIntervalProcessor<DstType, tileMode> proc(fIntervals.begin(),
232 fIntervals.end() - 1,
233 this->findInterval(fx),
234 fx,
235 dx,
236 SkScalarNearlyZero(dx * count));
237 while (count > 0) {
238 // What we really want here is SkTPin(advance, 1, count)
239 // but that's a significant perf hit for >> stops; investigate.
240 const int n = SkScalarTruncToInt(
241 SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
242
243 // The current interval advance can be +inf (e.g. when reaching
244 // the clamp mode end intervals) - when that happens, we expect to
245 // a) consume all remaining count in one swoop
246 // b) return a zero color gradient
247 SkASSERT(SkScalarIsFinite(proc.currentAdvance())
248 || (n == count && proc.currentRampIsZero()));
249
250 if (proc.currentRampIsZero()) {
251 fill<DstType, do_premul>(proc.currentColor(),
252 dst, n);
253 } else {
254 ramp<DstType, do_premul>(proc.currentColor(),
255 proc.currentColorGrad(),
256 dst, n);
257 }
258
259 proc.advance(SkIntToScalar(n));
260 count -= n;
261 dst += n;
262 }
263 }
264
265 template<typename DstType, SkShader::TileMode tileMode>
266 class SkLinearGradient::
267 LinearGradient4fContext::LinearIntervalProcessor {
268 public:
LinearIntervalProcessor(const Interval * firstInterval,const Interval * lastInterval,const Interval * i,SkScalar fx,SkScalar dx,bool is_vertical)269 LinearIntervalProcessor(const Interval* firstInterval,
270 const Interval* lastInterval,
271 const Interval* i,
272 SkScalar fx,
273 SkScalar dx,
274 bool is_vertical)
275 : fDstComponentScale(dst_component_scale<DstType>())
276 , fAdvX((i->fP1 - fx) / dx)
277 , fFirstInterval(firstInterval)
278 , fLastInterval(lastInterval)
279 , fInterval(i)
280 , fDx(dx)
281 , fIsVertical(is_vertical)
282 {
283 SkASSERT(firstInterval <= lastInterval);
284 SkASSERT(i->contains(fx));
285 this->compute_interval_props(fx - i->fP0);
286 }
287
currentAdvance() const288 SkScalar currentAdvance() const {
289 SkASSERT(fAdvX >= 0);
290 SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx);
291 return fAdvX;
292 }
293
currentRampIsZero() const294 bool currentRampIsZero() const { return fZeroRamp; }
currentColor() const295 const Sk4f& currentColor() const { return fCc; }
currentColorGrad() const296 const Sk4f& currentColorGrad() const { return fDcDx; }
297
advance(SkScalar advX)298 void advance(SkScalar advX) {
299 SkASSERT(advX > 0);
300 SkASSERT(fAdvX >= 0);
301
302 if (advX >= fAdvX) {
303 advX = this->advance_interval(advX);
304 }
305 SkASSERT(advX < fAdvX);
306
307 fCc = fCc + fDcDx * Sk4f(advX);
308 fAdvX -= advX;
309 }
310
311 private:
compute_interval_props(SkScalar t)312 void compute_interval_props(SkScalar t) {
313 fDc = Sk4f::Load(fInterval->fDc.fVec);
314 fCc = Sk4f::Load(fInterval->fC0.fVec);
315 fCc = fCc + fDc * Sk4f(t);
316 fCc = fCc * fDstComponentScale;
317 fDcDx = fDc * fDstComponentScale * Sk4f(fDx);
318 fZeroRamp = fIsVertical || fInterval->isZeroRamp();
319 }
320
next_interval(const Interval * i) const321 const Interval* next_interval(const Interval* i) const {
322 SkASSERT(i >= fFirstInterval);
323 SkASSERT(i <= fLastInterval);
324 i++;
325
326 if (tileMode == kClamp_TileMode) {
327 SkASSERT(i <= fLastInterval);
328 return i;
329 }
330
331 return (i <= fLastInterval) ? i : fFirstInterval;
332 }
333
advance_interval(SkScalar advX)334 SkScalar advance_interval(SkScalar advX) {
335 SkASSERT(advX >= fAdvX);
336
337 do {
338 advX -= fAdvX;
339 fInterval = this->next_interval(fInterval);
340 fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
341 SkASSERT(fAdvX > 0);
342 } while (advX >= fAdvX);
343
344 compute_interval_props(0);
345
346 SkASSERT(advX >= 0);
347 return advX;
348 }
349
350 const Sk4f fDstComponentScale; // cached dst scale (PMC: 255, PM4f: 1)
351
352 // Current interval properties.
353 Sk4f fDc; // local color gradient (dc/dt)
354 Sk4f fDcDx; // dst color gradient (dc/dx)
355 Sk4f fCc; // current color, interpolated in dst
356 SkScalar fAdvX; // remaining interval advance in dst
357 bool fZeroRamp; // current interval color grad is 0
358
359 const Interval* fFirstInterval;
360 const Interval* fLastInterval;
361 const Interval* fInterval; // current interval
362 const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
363 const bool fIsVertical;
364 };
365