blob: 4ea025d0d2781b995ee03e42fcc80bfdcd0d83ad [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"
fmalita83aa9202016-03-23 12:28:14 -07009#include "Sk4x4f.h"
fmalitaa928b282016-03-18 10:28:23 -070010#include "SkXfermode.h"
fmalitabc590c02016-02-22 09:12:33 -080011
12namespace {
13
fmalitadc6c9bf2016-03-21 13:16:51 -070014template<DstType dstType, ApplyPremul premul>
15void ramp(const Sk4f& c, const Sk4f& dc, typename DstTraits<dstType, premul>::Type dst[], int n) {
fmalitabc590c02016-02-22 09:12:33 -080016 SkASSERT(n > 0);
17
18 const Sk4f dc2 = dc + dc;
19 const Sk4f dc4 = dc2 + dc2;
20
21 Sk4f c0 = c ;
22 Sk4f c1 = c + dc;
23 Sk4f c2 = c0 + dc2;
24 Sk4f c3 = c1 + dc2;
25
26 while (n >= 4) {
fmalitadc6c9bf2016-03-21 13:16:51 -070027 DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst);
fmalitabc590c02016-02-22 09:12:33 -080028 dst += 4;
29
30 c0 = c0 + dc4;
31 c1 = c1 + dc4;
32 c2 = c2 + dc4;
33 c3 = c3 + dc4;
34 n -= 4;
35 }
36 if (n & 2) {
fmalitadc6c9bf2016-03-21 13:16:51 -070037 DstTraits<dstType, premul>::store(c0, dst++);
38 DstTraits<dstType, premul>::store(c1, dst++);
fmalitabc590c02016-02-22 09:12:33 -080039 c0 = c0 + dc2;
40 }
41 if (n & 1) {
fmalitadc6c9bf2016-03-21 13:16:51 -070042 DstTraits<dstType, premul>::store(c0, dst);
fmalitabc590c02016-02-22 09:12:33 -080043 }
44}
45
fmalita83aa9202016-03-23 12:28:14 -070046// Planar version of ramp (S32 no-premul only).
47template<>
48void ramp<DstType::S32, ApplyPremul::False>(const Sk4f& c, const Sk4f& dc, SkPMColor dst[], int n) {
49 SkASSERT(n > 0);
50
51 const Sk4f dc4 = dc * 4;
52 const Sk4x4f dc4x = { Sk4f(dc4[0]), Sk4f(dc4[1]), Sk4f(dc4[2]), Sk4f(dc4[3]) };
53 Sk4x4f c4x = Sk4x4f::Transpose(c, c + dc, c + dc * 2, c + dc * 3);
54
55 while (n >= 4) {
56 const Sk4x4f cx4s32 = { c4x.r.sqrt(), c4x.g.sqrt(), c4x.b.sqrt(), c4x.a };
57 cx4s32.transpose((uint8_t*)dst);
58
59 c4x.r += dc4x.r;
60 c4x.g += dc4x.g;
61 c4x.b += dc4x.b;
62 c4x.a += dc4x.a;
63
64 dst += 4;
65 n -= 4;
66 }
67
68 if (n & 2) {
69 DstTraits<DstType::S32, ApplyPremul::False>
70 ::store(Sk4f(c4x.r[0], c4x.g[0], c4x.b[0], c4x.a[0]), dst++);
71 DstTraits<DstType::S32, ApplyPremul::False>
72 ::store(Sk4f(c4x.r[1], c4x.g[1], c4x.b[1], c4x.a[1]), dst++);
73 }
74
75 if (n & 1) {
76 DstTraits<DstType::S32, ApplyPremul::False>
77 ::store(Sk4f(c4x.r[n & 2], c4x.g[n & 2], c4x.b[n & 2], c4x.a[n & 2]), dst);
78 }
79}
80
fmalitabc590c02016-02-22 09:12:33 -080081template<SkShader::TileMode>
82SkScalar pinFx(SkScalar);
83
84template<>
85SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
86 return fx;
87}
88
89template<>
90SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
91 const SkScalar f = SkScalarFraction(fx);
92 return f < 0 ? f + 1 : f;
93}
94
95template<>
96SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
97 const SkScalar f = SkScalarMod(fx, 2.0f);
98 return f < 0 ? f + 2 : f;
99}
100
fmalita7520fc42016-03-04 11:01:24 -0800101// true when x is in [k1,k2)
102bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
103 SkASSERT(k1 != k2);
104 return (k1 < k2)
105 ? (x >= k1 && x < k2)
106 : (x >= k2 && x < k1);
107}
108
fmalitabc590c02016-02-22 09:12:33 -0800109} // anonymous namespace
110
111SkLinearGradient::
112LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
113 const ContextRec& rec)
fmalita7520fc42016-03-04 11:01:24 -0800114 : INHERITED(shader, rec) {
fmalita7520fc42016-03-04 11:01:24 -0800115
fmalita7e6fcf82016-03-10 11:18:43 -0800116 // Our fast path expects interval points to be monotonically increasing in x.
117 const bool reverseIntervals = this->isFast() && fDstToPos.getScaleX() < 0;
118 this->buildIntervals(shader, rec, reverseIntervals);
fmalita7520fc42016-03-04 11:01:24 -0800119
120 SkASSERT(fIntervals.count() > 0);
121 fCachedInterval = fIntervals.begin();
122}
123
fmalita7520fc42016-03-04 11:01:24 -0800124const SkGradientShaderBase::GradientShaderBase4fContext::Interval*
125SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
126 SkASSERT(in_range(fx, fIntervals.front().fP0, fIntervals.back().fP1));
127
128 if (1) {
129 // Linear search, using the last scanline interval as a starting point.
130 SkASSERT(fCachedInterval >= fIntervals.begin());
131 SkASSERT(fCachedInterval < fIntervals.end());
132 const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
133 while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) {
134 fCachedInterval += search_dir;
135 if (fCachedInterval >= fIntervals.end()) {
136 fCachedInterval = fIntervals.begin();
137 } else if (fCachedInterval < fIntervals.begin()) {
138 fCachedInterval = fIntervals.end() - 1;
139 }
140 }
141 return fCachedInterval;
142 } else {
143 // Binary search. Seems less effective than linear + caching.
144 const Interval* i0 = fIntervals.begin();
145 const Interval* i1 = fIntervals.end() - 1;
146
147 while (i0 != i1) {
148 SkASSERT(i0 < i1);
149 SkASSERT(in_range(fx, i0->fP0, i1->fP1));
150
151 const Interval* i = i0 + ((i1 - i0) >> 1);
152
153 if (in_range(fx, i0->fP0, i->fP1)) {
154 i1 = i;
155 } else {
156 SkASSERT(in_range(fx, i->fP1, i1->fP1));
157 i0 = i + 1;
158 }
159 }
160
161 SkASSERT(in_range(fx, i0->fP0, i0->fP1));
162 return i0;
163 }
164}
fmalitabc590c02016-02-22 09:12:33 -0800165
166void SkLinearGradient::
167LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800168 if (!this->isFast()) {
169 this->INHERITED::shadeSpan(x, y, dst, count);
170 return;
171 }
172
fmalitabc590c02016-02-22 09:12:33 -0800173 // TODO: plumb dithering
174 SkASSERT(count > 0);
175 if (fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700176 this->shadePremulSpan<DstType::L32,
fmalitaa928b282016-03-18 10:28:23 -0700177 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800178 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700179 this->shadePremulSpan<DstType::L32,
fmalitaa928b282016-03-18 10:28:23 -0700180 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800181 }
182}
183
184void SkLinearGradient::
185LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800186 if (!this->isFast()) {
187 this->INHERITED::shadeSpan4f(x, y, dst, count);
188 return;
189 }
190
fmalitabc590c02016-02-22 09:12:33 -0800191 // TONOTDO: plumb dithering
192 SkASSERT(count > 0);
193 if (fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700194 this->shadePremulSpan<DstType::F32,
fmalitaa928b282016-03-18 10:28:23 -0700195 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800196 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700197 this->shadePremulSpan<DstType::F32,
fmalitaa928b282016-03-18 10:28:23 -0700198 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800199 }
200}
201
fmalitadc6c9bf2016-03-21 13:16:51 -0700202template<DstType dstType, ApplyPremul premul>
fmalitabc590c02016-02-22 09:12:33 -0800203void SkLinearGradient::
204LinearGradient4fContext::shadePremulSpan(int x, int y,
fmalitadc6c9bf2016-03-21 13:16:51 -0700205 typename DstTraits<dstType, premul>::Type dst[],
fmalitabc590c02016-02-22 09:12:33 -0800206 int count) const {
207 const SkLinearGradient& shader =
208 static_cast<const SkLinearGradient&>(fShader);
209 switch (shader.fTileMode) {
210 case kClamp_TileMode:
fmalitadc6c9bf2016-03-21 13:16:51 -0700211 this->shadeSpanInternal<dstType,
fmalitaa928b282016-03-18 10:28:23 -0700212 premul,
fmalitabc590c02016-02-22 09:12:33 -0800213 kClamp_TileMode>(x, y, dst, count);
214 break;
215 case kRepeat_TileMode:
fmalitadc6c9bf2016-03-21 13:16:51 -0700216 this->shadeSpanInternal<dstType,
fmalitaa928b282016-03-18 10:28:23 -0700217 premul,
fmalitabc590c02016-02-22 09:12:33 -0800218 kRepeat_TileMode>(x, y, dst, count);
219 break;
220 case kMirror_TileMode:
fmalitadc6c9bf2016-03-21 13:16:51 -0700221 this->shadeSpanInternal<dstType,
fmalitaa928b282016-03-18 10:28:23 -0700222 premul,
fmalitabc590c02016-02-22 09:12:33 -0800223 kMirror_TileMode>(x, y, dst, count);
224 break;
225 }
226}
227
fmalitadc6c9bf2016-03-21 13:16:51 -0700228template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800229void SkLinearGradient::
230LinearGradient4fContext::shadeSpanInternal(int x, int y,
fmalitadc6c9bf2016-03-21 13:16:51 -0700231 typename DstTraits<dstType, premul>::Type dst[],
fmalitabc590c02016-02-22 09:12:33 -0800232 int count) const {
233 SkPoint pt;
234 fDstToPosProc(fDstToPos,
235 x + SK_ScalarHalf,
236 y + SK_ScalarHalf,
237 &pt);
238 const SkScalar fx = pinFx<tileMode>(pt.x());
239 const SkScalar dx = fDstToPos.getScaleX();
fmalitadc6c9bf2016-03-21 13:16:51 -0700240 LinearIntervalProcessor<dstType, tileMode> proc(fIntervals.begin(),
241 fIntervals.end() - 1,
242 this->findInterval(fx),
243 fx,
244 dx,
245 SkScalarNearlyZero(dx * count));
fmalitabc590c02016-02-22 09:12:33 -0800246 while (count > 0) {
247 // What we really want here is SkTPin(advance, 1, count)
248 // but that's a significant perf hit for >> stops; investigate.
249 const int n = SkScalarTruncToInt(
250 SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
251
252 // The current interval advance can be +inf (e.g. when reaching
253 // the clamp mode end intervals) - when that happens, we expect to
254 // a) consume all remaining count in one swoop
255 // b) return a zero color gradient
256 SkASSERT(SkScalarIsFinite(proc.currentAdvance())
257 || (n == count && proc.currentRampIsZero()));
258
259 if (proc.currentRampIsZero()) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700260 DstTraits<dstType, premul>::store(proc.currentColor(),
261 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800262 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700263 ramp<dstType, premul>(proc.currentColor(),
264 proc.currentColorGrad(),
265 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800266 }
267
268 proc.advance(SkIntToScalar(n));
269 count -= n;
270 dst += n;
271 }
272}
273
fmalitadc6c9bf2016-03-21 13:16:51 -0700274template<DstType dstType, SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800275class SkLinearGradient::
276LinearGradient4fContext::LinearIntervalProcessor {
277public:
278 LinearIntervalProcessor(const Interval* firstInterval,
279 const Interval* lastInterval,
280 const Interval* i,
281 SkScalar fx,
282 SkScalar dx,
283 bool is_vertical)
fmalitaa928b282016-03-18 10:28:23 -0700284 : fAdvX((i->fP1 - fx) / dx)
fmalitabc590c02016-02-22 09:12:33 -0800285 , fFirstInterval(firstInterval)
286 , fLastInterval(lastInterval)
287 , fInterval(i)
288 , fDx(dx)
289 , fIsVertical(is_vertical)
290 {
291 SkASSERT(firstInterval <= lastInterval);
fmalita7e6fcf82016-03-10 11:18:43 -0800292 SkASSERT(in_range(fx, i->fP0, i->fP1));
fmalitabc590c02016-02-22 09:12:33 -0800293 this->compute_interval_props(fx - i->fP0);
294 }
295
296 SkScalar currentAdvance() const {
297 SkASSERT(fAdvX >= 0);
298 SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx);
299 return fAdvX;
300 }
301
302 bool currentRampIsZero() const { return fZeroRamp; }
303 const Sk4f& currentColor() const { return fCc; }
304 const Sk4f& currentColorGrad() const { return fDcDx; }
305
306 void advance(SkScalar advX) {
307 SkASSERT(advX > 0);
308 SkASSERT(fAdvX >= 0);
309
310 if (advX >= fAdvX) {
311 advX = this->advance_interval(advX);
312 }
313 SkASSERT(advX < fAdvX);
314
315 fCc = fCc + fDcDx * Sk4f(advX);
316 fAdvX -= advX;
317 }
318
319private:
320 void compute_interval_props(SkScalar t) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700321 const Sk4f dC = DstTraits<dstType>::load(fInterval->fDc);
322 fCc = DstTraits<dstType>::load(fInterval->fC0);
323 fCc = fCc + dC * Sk4f(t);
324 fDcDx = dC * fDx;
325 fZeroRamp = fIsVertical || fInterval->isZeroRamp();
fmalitabc590c02016-02-22 09:12:33 -0800326 }
327
328 const Interval* next_interval(const Interval* i) const {
329 SkASSERT(i >= fFirstInterval);
330 SkASSERT(i <= fLastInterval);
331 i++;
332
333 if (tileMode == kClamp_TileMode) {
334 SkASSERT(i <= fLastInterval);
335 return i;
336 }
337
338 return (i <= fLastInterval) ? i : fFirstInterval;
339 }
340
341 SkScalar advance_interval(SkScalar advX) {
342 SkASSERT(advX >= fAdvX);
343
344 do {
345 advX -= fAdvX;
346 fInterval = this->next_interval(fInterval);
347 fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
348 SkASSERT(fAdvX > 0);
349 } while (advX >= fAdvX);
350
351 compute_interval_props(0);
352
353 SkASSERT(advX >= 0);
354 return advX;
355 }
356
fmalitabc590c02016-02-22 09:12:33 -0800357 // Current interval properties.
fmalitabc590c02016-02-22 09:12:33 -0800358 Sk4f fDcDx; // dst color gradient (dc/dx)
359 Sk4f fCc; // current color, interpolated in dst
360 SkScalar fAdvX; // remaining interval advance in dst
361 bool fZeroRamp; // current interval color grad is 0
362
363 const Interval* fFirstInterval;
364 const Interval* fLastInterval;
365 const Interval* fInterval; // current interval
366 const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
367 const bool fIsVertical;
368};
fmalita7e6fcf82016-03-10 11:18:43 -0800369
370void SkLinearGradient::
371LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const {
372 SkASSERT(count > 0);
373 SkASSERT(fDstToPosClass != kLinear_MatrixClass);
374
375 SkScalar sx = x + SK_ScalarHalf;
376 const SkScalar sy = y + SK_ScalarHalf;
377 SkPoint pt;
378
379 if (fDstToPosClass != kPerspective_MatrixClass) {
380 // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline
381 const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x();
382 fDstToPosProc(fDstToPos, sx, sy, &pt);
383
384 const Sk4f dtdx4 = Sk4f(4 * dtdx);
385 Sk4f t4 = Sk4f(pt.x() + 0 * dtdx,
386 pt.x() + 1 * dtdx,
387 pt.x() + 2 * dtdx,
388 pt.x() + 3 * dtdx);
389
390 while (count >= 4) {
391 t4.store(ts);
392 t4 = t4 + dtdx4;
393 ts += 4;
394 count -= 4;
395 }
396
397 if (count & 2) {
398 *ts++ = t4[0];
399 *ts++ = t4[1];
400 t4 = SkNx_shuffle<2, 0, 1, 3>(t4);
401 }
402
403 if (count & 1) {
404 *ts++ = t4[0];
405 }
406 } else {
407 for (int i = 0; i < count; ++i) {
408 fDstToPosProc(fDstToPos, sx, sy, &pt);
409 ts[i] = pt.x();
410 sx += SK_Scalar1;
411 }
412 }
413}
fmalitaa928b282016-03-18 10:28:23 -0700414
reed58fc94e2016-03-18 12:42:26 -0700415bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info,
416 BlitState* state) {
fmalitaa928b282016-03-18 10:28:23 -0700417 SkXfermode::Mode mode;
418 if (!SkXfermode::AsMode(state->fXfer, &mode)) {
reed58fc94e2016-03-18 12:42:26 -0700419 return false;
fmalitaa928b282016-03-18 10:28:23 -0700420 }
421
422 const SkGradientShaderBase& shader = static_cast<const SkGradientShaderBase&>(fShader);
423 if (mode != SkXfermode::kSrc_Mode &&
424 !(mode == SkXfermode::kSrcOver_Mode && shader.colorsAreOpaque())) {
reed58fc94e2016-03-18 12:42:26 -0700425 return false;
fmalitaa928b282016-03-18 10:28:23 -0700426 }
427
428 switch (info.colorType()) {
429 case kN32_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700430 state->fBlitBW = D32_BlitBW;
431 return true;
fmalitaa928b282016-03-18 10:28:23 -0700432 case kRGBA_F16_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700433 state->fBlitBW = D64_BlitBW;
434 return true;
fmalitaa928b282016-03-18 10:28:23 -0700435 default:
reed58fc94e2016-03-18 12:42:26 -0700436 return false;
fmalitaa928b282016-03-18 10:28:23 -0700437 }
438}
439
440void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700441LinearGradient4fContext::D32_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
442 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700443 // FIXME: ignoring coverage for now
444 const LinearGradient4fContext* ctx =
445 static_cast<const LinearGradient4fContext*>(state->fCtx);
446
447 if (dst.info().isLinear()) {
448 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700449 ctx->shadePremulSpan<DstType::L32, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700450 x, y, dst.writable_addr32(x, y), count);
451 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700452 ctx->shadePremulSpan<DstType::L32, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700453 x, y, dst.writable_addr32(x, y), count);
454 }
455 } else {
456 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700457 ctx->shadePremulSpan<DstType::S32, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700458 x, y, dst.writable_addr32(x, y), count);
459 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700460 ctx->shadePremulSpan<DstType::S32, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700461 x, y, dst.writable_addr32(x, y), count);
462 }
463 }
464}
465
466void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700467LinearGradient4fContext::D64_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
468 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700469 // FIXME: ignoring coverage for now
470 const LinearGradient4fContext* ctx =
471 static_cast<const LinearGradient4fContext*>(state->fCtx);
472
473 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700474 ctx->shadePremulSpan<DstType::F16, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700475 x, y, dst.writable_addr64(x, y), count);
476 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700477 ctx->shadePremulSpan<DstType::F16, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700478 x, y, dst.writable_addr64(x, y), count);
479 }
480}