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