blob: f4bb4a7d1829d67598855354ead50a3b85dfae00 [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
8#include "Sk4fLinearGradient.h"
fmalitaa928b282016-03-18 10:28:23 -07009#include "SkXfermode.h"
fmalitabc590c02016-02-22 09:12:33 -080010
11namespace {
12
fmalitadc6c9bf2016-03-21 13:16:51 -070013template<DstType dstType, ApplyPremul premul>
14void ramp(const Sk4f& c, const Sk4f& dc, typename DstTraits<dstType, premul>::Type dst[], int n) {
fmalitabc590c02016-02-22 09:12:33 -080015 SkASSERT(n > 0);
16
17 const Sk4f dc2 = dc + dc;
18 const Sk4f dc4 = dc2 + dc2;
19
20 Sk4f c0 = c ;
21 Sk4f c1 = c + dc;
22 Sk4f c2 = c0 + dc2;
23 Sk4f c3 = c1 + dc2;
24
25 while (n >= 4) {
fmalitadc6c9bf2016-03-21 13:16:51 -070026 DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst);
fmalitabc590c02016-02-22 09:12:33 -080027 dst += 4;
28
29 c0 = c0 + dc4;
30 c1 = c1 + dc4;
31 c2 = c2 + dc4;
32 c3 = c3 + dc4;
33 n -= 4;
34 }
35 if (n & 2) {
fmalitadc6c9bf2016-03-21 13:16:51 -070036 DstTraits<dstType, premul>::store(c0, dst++);
37 DstTraits<dstType, premul>::store(c1, dst++);
fmalitabc590c02016-02-22 09:12:33 -080038 c0 = c0 + dc2;
39 }
40 if (n & 1) {
fmalitadc6c9bf2016-03-21 13:16:51 -070041 DstTraits<dstType, premul>::store(c0, dst);
fmalitabc590c02016-02-22 09:12:33 -080042 }
43}
44
45template<SkShader::TileMode>
46SkScalar pinFx(SkScalar);
47
48template<>
49SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
50 return fx;
51}
52
53template<>
54SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
55 const SkScalar f = SkScalarFraction(fx);
56 return f < 0 ? f + 1 : f;
57}
58
59template<>
60SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
61 const SkScalar f = SkScalarMod(fx, 2.0f);
62 return f < 0 ? f + 2 : f;
63}
64
fmalita7520fc42016-03-04 11:01:24 -080065// true when x is in [k1,k2)
66bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
67 SkASSERT(k1 != k2);
68 return (k1 < k2)
69 ? (x >= k1 && x < k2)
70 : (x >= k2 && x < k1);
71}
72
fmalitabc590c02016-02-22 09:12:33 -080073} // anonymous namespace
74
75SkLinearGradient::
76LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
77 const ContextRec& rec)
fmalita7520fc42016-03-04 11:01:24 -080078 : INHERITED(shader, rec) {
fmalita7520fc42016-03-04 11:01:24 -080079
fmalita7e6fcf82016-03-10 11:18:43 -080080 // Our fast path expects interval points to be monotonically increasing in x.
81 const bool reverseIntervals = this->isFast() && fDstToPos.getScaleX() < 0;
82 this->buildIntervals(shader, rec, reverseIntervals);
fmalita7520fc42016-03-04 11:01:24 -080083
84 SkASSERT(fIntervals.count() > 0);
85 fCachedInterval = fIntervals.begin();
86}
87
fmalita7520fc42016-03-04 11:01:24 -080088const SkGradientShaderBase::GradientShaderBase4fContext::Interval*
89SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
90 SkASSERT(in_range(fx, fIntervals.front().fP0, fIntervals.back().fP1));
91
92 if (1) {
93 // Linear search, using the last scanline interval as a starting point.
94 SkASSERT(fCachedInterval >= fIntervals.begin());
95 SkASSERT(fCachedInterval < fIntervals.end());
96 const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
97 while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) {
98 fCachedInterval += search_dir;
99 if (fCachedInterval >= fIntervals.end()) {
100 fCachedInterval = fIntervals.begin();
101 } else if (fCachedInterval < fIntervals.begin()) {
102 fCachedInterval = fIntervals.end() - 1;
103 }
104 }
105 return fCachedInterval;
106 } else {
107 // Binary search. Seems less effective than linear + caching.
108 const Interval* i0 = fIntervals.begin();
109 const Interval* i1 = fIntervals.end() - 1;
110
111 while (i0 != i1) {
112 SkASSERT(i0 < i1);
113 SkASSERT(in_range(fx, i0->fP0, i1->fP1));
114
115 const Interval* i = i0 + ((i1 - i0) >> 1);
116
117 if (in_range(fx, i0->fP0, i->fP1)) {
118 i1 = i;
119 } else {
120 SkASSERT(in_range(fx, i->fP1, i1->fP1));
121 i0 = i + 1;
122 }
123 }
124
125 SkASSERT(in_range(fx, i0->fP0, i0->fP1));
126 return i0;
127 }
128}
fmalitabc590c02016-02-22 09:12:33 -0800129
130void SkLinearGradient::
131LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800132 if (!this->isFast()) {
133 this->INHERITED::shadeSpan(x, y, dst, count);
134 return;
135 }
136
fmalitabc590c02016-02-22 09:12:33 -0800137 // TODO: plumb dithering
138 SkASSERT(count > 0);
139 if (fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700140 this->shadePremulSpan<DstType::L32,
fmalitaa928b282016-03-18 10:28:23 -0700141 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800142 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700143 this->shadePremulSpan<DstType::L32,
fmalitaa928b282016-03-18 10:28:23 -0700144 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800145 }
146}
147
148void SkLinearGradient::
149LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800150 if (!this->isFast()) {
151 this->INHERITED::shadeSpan4f(x, y, dst, count);
152 return;
153 }
154
fmalitabc590c02016-02-22 09:12:33 -0800155 // TONOTDO: plumb dithering
156 SkASSERT(count > 0);
157 if (fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700158 this->shadePremulSpan<DstType::F32,
fmalitaa928b282016-03-18 10:28:23 -0700159 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800160 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700161 this->shadePremulSpan<DstType::F32,
fmalitaa928b282016-03-18 10:28:23 -0700162 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800163 }
164}
165
fmalitadc6c9bf2016-03-21 13:16:51 -0700166template<DstType dstType, ApplyPremul premul>
fmalitabc590c02016-02-22 09:12:33 -0800167void SkLinearGradient::
168LinearGradient4fContext::shadePremulSpan(int x, int y,
fmalitadc6c9bf2016-03-21 13:16:51 -0700169 typename DstTraits<dstType, premul>::Type dst[],
fmalitabc590c02016-02-22 09:12:33 -0800170 int count) const {
171 const SkLinearGradient& shader =
172 static_cast<const SkLinearGradient&>(fShader);
173 switch (shader.fTileMode) {
174 case kClamp_TileMode:
fmalitadc6c9bf2016-03-21 13:16:51 -0700175 this->shadeSpanInternal<dstType,
fmalitaa928b282016-03-18 10:28:23 -0700176 premul,
fmalitabc590c02016-02-22 09:12:33 -0800177 kClamp_TileMode>(x, y, dst, count);
178 break;
179 case kRepeat_TileMode:
fmalitadc6c9bf2016-03-21 13:16:51 -0700180 this->shadeSpanInternal<dstType,
fmalitaa928b282016-03-18 10:28:23 -0700181 premul,
fmalitabc590c02016-02-22 09:12:33 -0800182 kRepeat_TileMode>(x, y, dst, count);
183 break;
184 case kMirror_TileMode:
fmalitadc6c9bf2016-03-21 13:16:51 -0700185 this->shadeSpanInternal<dstType,
fmalitaa928b282016-03-18 10:28:23 -0700186 premul,
fmalitabc590c02016-02-22 09:12:33 -0800187 kMirror_TileMode>(x, y, dst, count);
188 break;
189 }
190}
191
fmalitadc6c9bf2016-03-21 13:16:51 -0700192template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800193void SkLinearGradient::
194LinearGradient4fContext::shadeSpanInternal(int x, int y,
fmalitadc6c9bf2016-03-21 13:16:51 -0700195 typename DstTraits<dstType, premul>::Type dst[],
fmalitabc590c02016-02-22 09:12:33 -0800196 int count) const {
197 SkPoint pt;
198 fDstToPosProc(fDstToPos,
199 x + SK_ScalarHalf,
200 y + SK_ScalarHalf,
201 &pt);
202 const SkScalar fx = pinFx<tileMode>(pt.x());
203 const SkScalar dx = fDstToPos.getScaleX();
fmalitadc6c9bf2016-03-21 13:16:51 -0700204 LinearIntervalProcessor<dstType, tileMode> proc(fIntervals.begin(),
205 fIntervals.end() - 1,
206 this->findInterval(fx),
207 fx,
208 dx,
209 SkScalarNearlyZero(dx * count));
fmalitabc590c02016-02-22 09:12:33 -0800210 while (count > 0) {
211 // What we really want here is SkTPin(advance, 1, count)
212 // but that's a significant perf hit for >> stops; investigate.
213 const int n = SkScalarTruncToInt(
214 SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
215
216 // The current interval advance can be +inf (e.g. when reaching
217 // the clamp mode end intervals) - when that happens, we expect to
218 // a) consume all remaining count in one swoop
219 // b) return a zero color gradient
220 SkASSERT(SkScalarIsFinite(proc.currentAdvance())
221 || (n == count && proc.currentRampIsZero()));
222
223 if (proc.currentRampIsZero()) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700224 DstTraits<dstType, premul>::store(proc.currentColor(),
225 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800226 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700227 ramp<dstType, premul>(proc.currentColor(),
228 proc.currentColorGrad(),
229 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800230 }
231
232 proc.advance(SkIntToScalar(n));
233 count -= n;
234 dst += n;
235 }
236}
237
fmalitadc6c9bf2016-03-21 13:16:51 -0700238template<DstType dstType, SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800239class SkLinearGradient::
240LinearGradient4fContext::LinearIntervalProcessor {
241public:
242 LinearIntervalProcessor(const Interval* firstInterval,
243 const Interval* lastInterval,
244 const Interval* i,
245 SkScalar fx,
246 SkScalar dx,
247 bool is_vertical)
fmalitaa928b282016-03-18 10:28:23 -0700248 : fAdvX((i->fP1 - fx) / dx)
fmalitabc590c02016-02-22 09:12:33 -0800249 , fFirstInterval(firstInterval)
250 , fLastInterval(lastInterval)
251 , fInterval(i)
252 , fDx(dx)
253 , fIsVertical(is_vertical)
254 {
255 SkASSERT(firstInterval <= lastInterval);
fmalita7e6fcf82016-03-10 11:18:43 -0800256 SkASSERT(in_range(fx, i->fP0, i->fP1));
fmalitabc590c02016-02-22 09:12:33 -0800257 this->compute_interval_props(fx - i->fP0);
258 }
259
260 SkScalar currentAdvance() const {
261 SkASSERT(fAdvX >= 0);
262 SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx);
263 return fAdvX;
264 }
265
266 bool currentRampIsZero() const { return fZeroRamp; }
267 const Sk4f& currentColor() const { return fCc; }
268 const Sk4f& currentColorGrad() const { return fDcDx; }
269
270 void advance(SkScalar advX) {
271 SkASSERT(advX > 0);
272 SkASSERT(fAdvX >= 0);
273
274 if (advX >= fAdvX) {
275 advX = this->advance_interval(advX);
276 }
277 SkASSERT(advX < fAdvX);
278
279 fCc = fCc + fDcDx * Sk4f(advX);
280 fAdvX -= advX;
281 }
282
283private:
284 void compute_interval_props(SkScalar t) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700285 const Sk4f dC = DstTraits<dstType>::load(fInterval->fDc);
286 fCc = DstTraits<dstType>::load(fInterval->fC0);
287 fCc = fCc + dC * Sk4f(t);
288 fDcDx = dC * fDx;
289 fZeroRamp = fIsVertical || fInterval->isZeroRamp();
fmalitabc590c02016-02-22 09:12:33 -0800290 }
291
292 const Interval* next_interval(const Interval* i) const {
293 SkASSERT(i >= fFirstInterval);
294 SkASSERT(i <= fLastInterval);
295 i++;
296
297 if (tileMode == kClamp_TileMode) {
298 SkASSERT(i <= fLastInterval);
299 return i;
300 }
301
302 return (i <= fLastInterval) ? i : fFirstInterval;
303 }
304
305 SkScalar advance_interval(SkScalar advX) {
306 SkASSERT(advX >= fAdvX);
307
308 do {
309 advX -= fAdvX;
310 fInterval = this->next_interval(fInterval);
311 fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
312 SkASSERT(fAdvX > 0);
313 } while (advX >= fAdvX);
314
315 compute_interval_props(0);
316
317 SkASSERT(advX >= 0);
318 return advX;
319 }
320
fmalitabc590c02016-02-22 09:12:33 -0800321 // Current interval properties.
fmalitabc590c02016-02-22 09:12:33 -0800322 Sk4f fDcDx; // dst color gradient (dc/dx)
323 Sk4f fCc; // current color, interpolated in dst
324 SkScalar fAdvX; // remaining interval advance in dst
325 bool fZeroRamp; // current interval color grad is 0
326
327 const Interval* fFirstInterval;
328 const Interval* fLastInterval;
329 const Interval* fInterval; // current interval
330 const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
331 const bool fIsVertical;
332};
fmalita7e6fcf82016-03-10 11:18:43 -0800333
334void SkLinearGradient::
335LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const {
336 SkASSERT(count > 0);
337 SkASSERT(fDstToPosClass != kLinear_MatrixClass);
338
339 SkScalar sx = x + SK_ScalarHalf;
340 const SkScalar sy = y + SK_ScalarHalf;
341 SkPoint pt;
342
343 if (fDstToPosClass != kPerspective_MatrixClass) {
344 // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline
345 const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x();
346 fDstToPosProc(fDstToPos, sx, sy, &pt);
347
348 const Sk4f dtdx4 = Sk4f(4 * dtdx);
349 Sk4f t4 = Sk4f(pt.x() + 0 * dtdx,
350 pt.x() + 1 * dtdx,
351 pt.x() + 2 * dtdx,
352 pt.x() + 3 * dtdx);
353
354 while (count >= 4) {
355 t4.store(ts);
356 t4 = t4 + dtdx4;
357 ts += 4;
358 count -= 4;
359 }
360
361 if (count & 2) {
362 *ts++ = t4[0];
363 *ts++ = t4[1];
364 t4 = SkNx_shuffle<2, 0, 1, 3>(t4);
365 }
366
367 if (count & 1) {
368 *ts++ = t4[0];
369 }
370 } else {
371 for (int i = 0; i < count; ++i) {
372 fDstToPosProc(fDstToPos, sx, sy, &pt);
373 ts[i] = pt.x();
374 sx += SK_Scalar1;
375 }
376 }
377}
fmalitaa928b282016-03-18 10:28:23 -0700378
reed58fc94e2016-03-18 12:42:26 -0700379bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info,
380 BlitState* state) {
fmalitaa928b282016-03-18 10:28:23 -0700381 SkXfermode::Mode mode;
382 if (!SkXfermode::AsMode(state->fXfer, &mode)) {
reed58fc94e2016-03-18 12:42:26 -0700383 return false;
fmalitaa928b282016-03-18 10:28:23 -0700384 }
385
386 const SkGradientShaderBase& shader = static_cast<const SkGradientShaderBase&>(fShader);
387 if (mode != SkXfermode::kSrc_Mode &&
388 !(mode == SkXfermode::kSrcOver_Mode && shader.colorsAreOpaque())) {
reed58fc94e2016-03-18 12:42:26 -0700389 return false;
fmalitaa928b282016-03-18 10:28:23 -0700390 }
391
392 switch (info.colorType()) {
393 case kN32_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700394 state->fBlitBW = D32_BlitBW;
395 return true;
fmalitaa928b282016-03-18 10:28:23 -0700396 case kRGBA_F16_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700397 state->fBlitBW = D64_BlitBW;
398 return true;
fmalitaa928b282016-03-18 10:28:23 -0700399 default:
reed58fc94e2016-03-18 12:42:26 -0700400 return false;
fmalitaa928b282016-03-18 10:28:23 -0700401 }
402}
403
404void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700405LinearGradient4fContext::D32_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
406 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700407 // FIXME: ignoring coverage for now
408 const LinearGradient4fContext* ctx =
409 static_cast<const LinearGradient4fContext*>(state->fCtx);
410
411 if (dst.info().isLinear()) {
412 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700413 ctx->shadePremulSpan<DstType::L32, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700414 x, y, dst.writable_addr32(x, y), count);
415 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700416 ctx->shadePremulSpan<DstType::L32, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700417 x, y, dst.writable_addr32(x, y), count);
418 }
419 } else {
420 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700421 ctx->shadePremulSpan<DstType::S32, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700422 x, y, dst.writable_addr32(x, y), count);
423 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700424 ctx->shadePremulSpan<DstType::S32, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700425 x, y, dst.writable_addr32(x, y), count);
426 }
427 }
428}
429
430void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700431LinearGradient4fContext::D64_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
432 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700433 // FIXME: ignoring coverage for now
434 const LinearGradient4fContext* ctx =
435 static_cast<const LinearGradient4fContext*>(state->fCtx);
436
437 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700438 ctx->shadePremulSpan<DstType::F16, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700439 x, y, dst.writable_addr64(x, y), count);
440 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700441 ctx->shadePremulSpan<DstType::F16, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700442 x, y, dst.writable_addr64(x, y), count);
443 }
444}