blob: 9bc84c49957d6a07ef8e70d10fc3176a10965ccb [file] [log] [blame]
fmalitabc590c02016-02-22 09:12:33 -08001/*
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
fmalita7e6fcf82016-03-10 11:18:43 -08008#include "Sk4fGradientPriv.h"
fmalitabc590c02016-02-22 09:12:33 -08009#include "Sk4fLinearGradient.h"
10
11namespace {
12
fmalitabc590c02016-02-22 09:12:33 -080013template<typename DstType, bool do_premul>
14void fill(const Sk4f& c, DstType* dst, int n);
15
16template<>
17void fill<SkPM4f, false>(const Sk4f& c, SkPM4f* dst, int n) {
18 while (n > 0) {
19 c.store(dst++);
20 n--;
21 }
22}
23
24template<>
25void fill<SkPM4f, true>(const Sk4f& c, SkPM4f* dst, int n) {
26 fill<SkPM4f, false>(premul_4f(c), dst, n);
27}
28
29template<>
30void fill<SkPMColor, false>(const Sk4f& c, SkPMColor* dst, int n) {
31 sk_memset32(dst, trunc_from_255<false>(c), n);
32}
33
34template<>
35void fill<SkPMColor, true>(const Sk4f& c, SkPMColor* dst, int n) {
36 sk_memset32(dst, trunc_from_255<true>(c), n);
37}
38
39template<typename DstType, bool do_premul>
fmalitabc590c02016-02-22 09:12:33 -080040void ramp(const Sk4f& c, const Sk4f& dc, DstType* dst, int n) {
41 SkASSERT(n > 0);
42
43 const Sk4f dc2 = dc + dc;
44 const Sk4f dc4 = dc2 + dc2;
45
46 Sk4f c0 = c ;
47 Sk4f c1 = c + dc;
48 Sk4f c2 = c0 + dc2;
49 Sk4f c3 = c1 + dc2;
50
51 while (n >= 4) {
52 store4x<DstType, do_premul>(c0, c1, c2, c3, dst);
53 dst += 4;
54
55 c0 = c0 + dc4;
56 c1 = c1 + dc4;
57 c2 = c2 + dc4;
58 c3 = c3 + dc4;
59 n -= 4;
60 }
61 if (n & 2) {
62 store<DstType, do_premul>(c0, dst++);
63 store<DstType, do_premul>(c1, dst++);
64 c0 = c0 + dc2;
65 }
66 if (n & 1) {
67 store<DstType, do_premul>(c0, dst);
68 }
69}
70
71template<SkShader::TileMode>
72SkScalar pinFx(SkScalar);
73
74template<>
75SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
76 return fx;
77}
78
79template<>
80SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
81 const SkScalar f = SkScalarFraction(fx);
82 return f < 0 ? f + 1 : f;
83}
84
85template<>
86SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
87 const SkScalar f = SkScalarMod(fx, 2.0f);
88 return f < 0 ? f + 2 : f;
89}
90
fmalita7520fc42016-03-04 11:01:24 -080091// true when x is in [k1,k2)
92bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
93 SkASSERT(k1 != k2);
94 return (k1 < k2)
95 ? (x >= k1 && x < k2)
96 : (x >= k2 && x < k1);
97}
98
fmalitabc590c02016-02-22 09:12:33 -080099} // anonymous namespace
100
101SkLinearGradient::
102LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
103 const ContextRec& rec)
fmalita7520fc42016-03-04 11:01:24 -0800104 : INHERITED(shader, rec) {
fmalita7520fc42016-03-04 11:01:24 -0800105
fmalita7e6fcf82016-03-10 11:18:43 -0800106 // Our fast path expects interval points to be monotonically increasing in x.
107 const bool reverseIntervals = this->isFast() && fDstToPos.getScaleX() < 0;
108 this->buildIntervals(shader, rec, reverseIntervals);
fmalita7520fc42016-03-04 11:01:24 -0800109
110 SkASSERT(fIntervals.count() > 0);
111 fCachedInterval = fIntervals.begin();
112}
113
fmalita7520fc42016-03-04 11:01:24 -0800114const SkGradientShaderBase::GradientShaderBase4fContext::Interval*
115SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
116 SkASSERT(in_range(fx, fIntervals.front().fP0, fIntervals.back().fP1));
117
118 if (1) {
119 // Linear search, using the last scanline interval as a starting point.
120 SkASSERT(fCachedInterval >= fIntervals.begin());
121 SkASSERT(fCachedInterval < fIntervals.end());
122 const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
123 while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) {
124 fCachedInterval += search_dir;
125 if (fCachedInterval >= fIntervals.end()) {
126 fCachedInterval = fIntervals.begin();
127 } else if (fCachedInterval < fIntervals.begin()) {
128 fCachedInterval = fIntervals.end() - 1;
129 }
130 }
131 return fCachedInterval;
132 } else {
133 // Binary search. Seems less effective than linear + caching.
134 const Interval* i0 = fIntervals.begin();
135 const Interval* i1 = fIntervals.end() - 1;
136
137 while (i0 != i1) {
138 SkASSERT(i0 < i1);
139 SkASSERT(in_range(fx, i0->fP0, i1->fP1));
140
141 const Interval* i = i0 + ((i1 - i0) >> 1);
142
143 if (in_range(fx, i0->fP0, i->fP1)) {
144 i1 = i;
145 } else {
146 SkASSERT(in_range(fx, i->fP1, i1->fP1));
147 i0 = i + 1;
148 }
149 }
150
151 SkASSERT(in_range(fx, i0->fP0, i0->fP1));
152 return i0;
153 }
154}
fmalitabc590c02016-02-22 09:12:33 -0800155
156void SkLinearGradient::
157LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800158 if (!this->isFast()) {
159 this->INHERITED::shadeSpan(x, y, dst, count);
160 return;
161 }
162
fmalitabc590c02016-02-22 09:12:33 -0800163 // TODO: plumb dithering
164 SkASSERT(count > 0);
165 if (fColorsArePremul) {
166 this->shadePremulSpan<SkPMColor, false>(x, y, dst, count);
167 } else {
168 this->shadePremulSpan<SkPMColor, true>(x, y, dst, count);
169 }
170}
171
172void SkLinearGradient::
173LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800174 if (!this->isFast()) {
175 this->INHERITED::shadeSpan4f(x, y, dst, count);
176 return;
177 }
178
fmalitabc590c02016-02-22 09:12:33 -0800179 // TONOTDO: plumb dithering
180 SkASSERT(count > 0);
181 if (fColorsArePremul) {
182 this->shadePremulSpan<SkPM4f, false>(x, y, dst, count);
183 } else {
184 this->shadePremulSpan<SkPM4f, true>(x, y, dst, count);
185 }
186}
187
188template<typename DstType, bool do_premul>
189void SkLinearGradient::
190LinearGradient4fContext::shadePremulSpan(int x, int y,
191 DstType dst[],
192 int count) const {
193 const SkLinearGradient& shader =
194 static_cast<const SkLinearGradient&>(fShader);
195 switch (shader.fTileMode) {
196 case kClamp_TileMode:
197 this->shadeSpanInternal<DstType,
198 do_premul,
199 kClamp_TileMode>(x, y, dst, count);
200 break;
201 case kRepeat_TileMode:
202 this->shadeSpanInternal<DstType,
203 do_premul,
204 kRepeat_TileMode>(x, y, dst, count);
205 break;
206 case kMirror_TileMode:
207 this->shadeSpanInternal<DstType,
208 do_premul,
209 kMirror_TileMode>(x, y, dst, count);
210 break;
211 }
212}
213
214template<typename DstType, bool do_premul, SkShader::TileMode tileMode>
215void SkLinearGradient::
216LinearGradient4fContext::shadeSpanInternal(int x, int y,
217 DstType dst[],
218 int count) const {
219 SkPoint pt;
220 fDstToPosProc(fDstToPos,
221 x + SK_ScalarHalf,
222 y + SK_ScalarHalf,
223 &pt);
224 const SkScalar fx = pinFx<tileMode>(pt.x());
225 const SkScalar dx = fDstToPos.getScaleX();
226 LinearIntervalProcessor<DstType, tileMode> proc(fIntervals.begin(),
227 fIntervals.end() - 1,
228 this->findInterval(fx),
229 fx,
230 dx,
231 SkScalarNearlyZero(dx * count));
232 while (count > 0) {
233 // What we really want here is SkTPin(advance, 1, count)
234 // but that's a significant perf hit for >> stops; investigate.
235 const int n = SkScalarTruncToInt(
236 SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
237
238 // The current interval advance can be +inf (e.g. when reaching
239 // the clamp mode end intervals) - when that happens, we expect to
240 // a) consume all remaining count in one swoop
241 // b) return a zero color gradient
242 SkASSERT(SkScalarIsFinite(proc.currentAdvance())
243 || (n == count && proc.currentRampIsZero()));
244
245 if (proc.currentRampIsZero()) {
246 fill<DstType, do_premul>(proc.currentColor(),
247 dst, n);
248 } else {
249 ramp<DstType, do_premul>(proc.currentColor(),
250 proc.currentColorGrad(),
251 dst, n);
252 }
253
254 proc.advance(SkIntToScalar(n));
255 count -= n;
256 dst += n;
257 }
258}
259
260template<typename DstType, SkShader::TileMode tileMode>
261class SkLinearGradient::
262LinearGradient4fContext::LinearIntervalProcessor {
263public:
264 LinearIntervalProcessor(const Interval* firstInterval,
265 const Interval* lastInterval,
266 const Interval* i,
267 SkScalar fx,
268 SkScalar dx,
269 bool is_vertical)
270 : fDstComponentScale(dst_component_scale<DstType>())
271 , fAdvX((i->fP1 - fx) / dx)
272 , fFirstInterval(firstInterval)
273 , fLastInterval(lastInterval)
274 , fInterval(i)
275 , fDx(dx)
276 , fIsVertical(is_vertical)
277 {
278 SkASSERT(firstInterval <= lastInterval);
fmalita7e6fcf82016-03-10 11:18:43 -0800279 SkASSERT(in_range(fx, i->fP0, i->fP1));
fmalitabc590c02016-02-22 09:12:33 -0800280 this->compute_interval_props(fx - i->fP0);
281 }
282
283 SkScalar currentAdvance() const {
284 SkASSERT(fAdvX >= 0);
285 SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx);
286 return fAdvX;
287 }
288
289 bool currentRampIsZero() const { return fZeroRamp; }
290 const Sk4f& currentColor() const { return fCc; }
291 const Sk4f& currentColorGrad() const { return fDcDx; }
292
293 void advance(SkScalar advX) {
294 SkASSERT(advX > 0);
295 SkASSERT(fAdvX >= 0);
296
297 if (advX >= fAdvX) {
298 advX = this->advance_interval(advX);
299 }
300 SkASSERT(advX < fAdvX);
301
302 fCc = fCc + fDcDx * Sk4f(advX);
303 fAdvX -= advX;
304 }
305
306private:
307 void compute_interval_props(SkScalar t) {
fmalitad8a4f772016-03-08 10:20:49 -0800308 fDc = dst_swizzle<DstType>(fInterval->fDc);
309 fCc = dst_swizzle<DstType>(fInterval->fC0);
fmalitabc590c02016-02-22 09:12:33 -0800310 fCc = fCc + fDc * Sk4f(t);
311 fCc = fCc * fDstComponentScale;
312 fDcDx = fDc * fDstComponentScale * Sk4f(fDx);
313 fZeroRamp = fIsVertical || fInterval->isZeroRamp();
314 }
315
316 const Interval* next_interval(const Interval* i) const {
317 SkASSERT(i >= fFirstInterval);
318 SkASSERT(i <= fLastInterval);
319 i++;
320
321 if (tileMode == kClamp_TileMode) {
322 SkASSERT(i <= fLastInterval);
323 return i;
324 }
325
326 return (i <= fLastInterval) ? i : fFirstInterval;
327 }
328
329 SkScalar advance_interval(SkScalar advX) {
330 SkASSERT(advX >= fAdvX);
331
332 do {
333 advX -= fAdvX;
334 fInterval = this->next_interval(fInterval);
335 fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
336 SkASSERT(fAdvX > 0);
337 } while (advX >= fAdvX);
338
339 compute_interval_props(0);
340
341 SkASSERT(advX >= 0);
342 return advX;
343 }
344
345 const Sk4f fDstComponentScale; // cached dst scale (PMC: 255, PM4f: 1)
346
347 // Current interval properties.
348 Sk4f fDc; // local color gradient (dc/dt)
349 Sk4f fDcDx; // dst color gradient (dc/dx)
350 Sk4f fCc; // current color, interpolated in dst
351 SkScalar fAdvX; // remaining interval advance in dst
352 bool fZeroRamp; // current interval color grad is 0
353
354 const Interval* fFirstInterval;
355 const Interval* fLastInterval;
356 const Interval* fInterval; // current interval
357 const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
358 const bool fIsVertical;
359};
fmalita7e6fcf82016-03-10 11:18:43 -0800360
361void SkLinearGradient::
362LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const {
363 SkASSERT(count > 0);
364 SkASSERT(fDstToPosClass != kLinear_MatrixClass);
365
366 SkScalar sx = x + SK_ScalarHalf;
367 const SkScalar sy = y + SK_ScalarHalf;
368 SkPoint pt;
369
370 if (fDstToPosClass != kPerspective_MatrixClass) {
371 // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline
372 const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x();
373 fDstToPosProc(fDstToPos, sx, sy, &pt);
374
375 const Sk4f dtdx4 = Sk4f(4 * dtdx);
376 Sk4f t4 = Sk4f(pt.x() + 0 * dtdx,
377 pt.x() + 1 * dtdx,
378 pt.x() + 2 * dtdx,
379 pt.x() + 3 * dtdx);
380
381 while (count >= 4) {
382 t4.store(ts);
383 t4 = t4 + dtdx4;
384 ts += 4;
385 count -= 4;
386 }
387
388 if (count & 2) {
389 *ts++ = t4[0];
390 *ts++ = t4[1];
391 t4 = SkNx_shuffle<2, 0, 1, 3>(t4);
392 }
393
394 if (count & 1) {
395 *ts++ = t4[0];
396 }
397 } else {
398 for (int i = 0; i < count; ++i) {
399 fDstToPosProc(fDstToPos, sx, sy, &pt);
400 ts[i] = pt.x();
401 sx += SK_Scalar1;
402 }
403 }
404}