blob: 84b266accdf144c26003b49e0118fc0fc86f27fb [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 "SkUtils.h"
10#include "SkXfermode.h"
fmalitabc590c02016-02-22 09:12:33 -080011
12namespace {
13
fmalitaa928b282016-03-18 10:28:23 -070014template<typename DstType, SkColorProfileType, ApplyPremul>
fmalitabc590c02016-02-22 09:12:33 -080015void fill(const Sk4f& c, DstType* dst, int n);
16
17template<>
fmalitaa928b282016-03-18 10:28:23 -070018void fill<SkPM4f, kLinear_SkColorProfileType, ApplyPremul::False>
19 (const Sk4f& c, SkPM4f* dst, int n) {
fmalitabc590c02016-02-22 09:12:33 -080020 while (n > 0) {
21 c.store(dst++);
22 n--;
23 }
24}
25
26template<>
fmalitaa928b282016-03-18 10:28:23 -070027void fill<SkPM4f, kLinear_SkColorProfileType, ApplyPremul::True>
28 (const Sk4f& c, SkPM4f* dst, int n) {
29 fill<SkPM4f, kLinear_SkColorProfileType, ApplyPremul::False>(premul_4f(c), dst, n);
fmalitabc590c02016-02-22 09:12:33 -080030}
31
32template<>
fmalitaa928b282016-03-18 10:28:23 -070033void fill<SkPMColor, kLinear_SkColorProfileType, ApplyPremul::False>
34 (const Sk4f& c, SkPMColor* dst, int n) {
35 sk_memset32(dst, trunc_from_4f_255<ApplyPremul::False>(c), n);
fmalitabc590c02016-02-22 09:12:33 -080036}
37
38template<>
fmalitaa928b282016-03-18 10:28:23 -070039void fill<SkPMColor, kLinear_SkColorProfileType, ApplyPremul::True>
40 (const Sk4f& c, SkPMColor* dst, int n) {
41 sk_memset32(dst, trunc_from_4f_255<ApplyPremul::True>(c), n);
fmalitabc590c02016-02-22 09:12:33 -080042}
43
fmalitaa928b282016-03-18 10:28:23 -070044template<>
45void fill<SkPMColor, kSRGB_SkColorProfileType, ApplyPremul::False>
46 (const Sk4f& c, SkPMColor* dst, int n) {
47 // FIXME: this assumes opaque colors. Handle unpremultiplication.
48 sk_memset32(dst, Sk4f_toS32(c), n);
49}
50
51template<>
52void fill<SkPMColor, kSRGB_SkColorProfileType, ApplyPremul::True>
53 (const Sk4f& c, SkPMColor* dst, int n) {
54 sk_memset32(dst, Sk4f_toS32(premul_4f(c)), n);
55}
56
57template<>
58void fill<uint64_t, kLinear_SkColorProfileType, ApplyPremul::False>
59 (const Sk4f& c, uint64_t* dst, int n) {
60 sk_memset64(dst, SkFloatToHalf_01(c), n);
61}
62
63template<>
64void fill<uint64_t, kLinear_SkColorProfileType, ApplyPremul::True>
65 (const Sk4f& c, uint64_t* dst, int n) {
66 sk_memset64(dst, SkFloatToHalf_01(premul_4f(c)), n);
67}
68
69template<typename DstType, SkColorProfileType profile, ApplyPremul premul>
fmalitabc590c02016-02-22 09:12:33 -080070void ramp(const Sk4f& c, const Sk4f& dc, DstType* dst, int n) {
71 SkASSERT(n > 0);
72
73 const Sk4f dc2 = dc + dc;
74 const Sk4f dc4 = dc2 + dc2;
75
76 Sk4f c0 = c ;
77 Sk4f c1 = c + dc;
78 Sk4f c2 = c0 + dc2;
79 Sk4f c3 = c1 + dc2;
80
81 while (n >= 4) {
fmalitaa928b282016-03-18 10:28:23 -070082 store4x<DstType, profile, premul>(c0, c1, c2, c3, dst);
fmalitabc590c02016-02-22 09:12:33 -080083 dst += 4;
84
85 c0 = c0 + dc4;
86 c1 = c1 + dc4;
87 c2 = c2 + dc4;
88 c3 = c3 + dc4;
89 n -= 4;
90 }
91 if (n & 2) {
fmalitaa928b282016-03-18 10:28:23 -070092 store<DstType, profile, premul>(c0, dst++);
93 store<DstType, profile, premul>(c1, dst++);
fmalitabc590c02016-02-22 09:12:33 -080094 c0 = c0 + dc2;
95 }
96 if (n & 1) {
fmalitaa928b282016-03-18 10:28:23 -070097 store<DstType, profile, premul>(c0, dst);
fmalitabc590c02016-02-22 09:12:33 -080098 }
99}
100
101template<SkShader::TileMode>
102SkScalar pinFx(SkScalar);
103
104template<>
105SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
106 return fx;
107}
108
109template<>
110SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
111 const SkScalar f = SkScalarFraction(fx);
112 return f < 0 ? f + 1 : f;
113}
114
115template<>
116SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
117 const SkScalar f = SkScalarMod(fx, 2.0f);
118 return f < 0 ? f + 2 : f;
119}
120
fmalita7520fc42016-03-04 11:01:24 -0800121// true when x is in [k1,k2)
122bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
123 SkASSERT(k1 != k2);
124 return (k1 < k2)
125 ? (x >= k1 && x < k2)
126 : (x >= k2 && x < k1);
127}
128
fmalitabc590c02016-02-22 09:12:33 -0800129} // anonymous namespace
130
131SkLinearGradient::
132LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
133 const ContextRec& rec)
fmalita7520fc42016-03-04 11:01:24 -0800134 : INHERITED(shader, rec) {
fmalita7520fc42016-03-04 11:01:24 -0800135
fmalita7e6fcf82016-03-10 11:18:43 -0800136 // Our fast path expects interval points to be monotonically increasing in x.
137 const bool reverseIntervals = this->isFast() && fDstToPos.getScaleX() < 0;
138 this->buildIntervals(shader, rec, reverseIntervals);
fmalita7520fc42016-03-04 11:01:24 -0800139
140 SkASSERT(fIntervals.count() > 0);
141 fCachedInterval = fIntervals.begin();
142}
143
fmalita7520fc42016-03-04 11:01:24 -0800144const SkGradientShaderBase::GradientShaderBase4fContext::Interval*
145SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
146 SkASSERT(in_range(fx, fIntervals.front().fP0, fIntervals.back().fP1));
147
148 if (1) {
149 // Linear search, using the last scanline interval as a starting point.
150 SkASSERT(fCachedInterval >= fIntervals.begin());
151 SkASSERT(fCachedInterval < fIntervals.end());
152 const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
153 while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) {
154 fCachedInterval += search_dir;
155 if (fCachedInterval >= fIntervals.end()) {
156 fCachedInterval = fIntervals.begin();
157 } else if (fCachedInterval < fIntervals.begin()) {
158 fCachedInterval = fIntervals.end() - 1;
159 }
160 }
161 return fCachedInterval;
162 } else {
163 // Binary search. Seems less effective than linear + caching.
164 const Interval* i0 = fIntervals.begin();
165 const Interval* i1 = fIntervals.end() - 1;
166
167 while (i0 != i1) {
168 SkASSERT(i0 < i1);
169 SkASSERT(in_range(fx, i0->fP0, i1->fP1));
170
171 const Interval* i = i0 + ((i1 - i0) >> 1);
172
173 if (in_range(fx, i0->fP0, i->fP1)) {
174 i1 = i;
175 } else {
176 SkASSERT(in_range(fx, i->fP1, i1->fP1));
177 i0 = i + 1;
178 }
179 }
180
181 SkASSERT(in_range(fx, i0->fP0, i0->fP1));
182 return i0;
183 }
184}
fmalitabc590c02016-02-22 09:12:33 -0800185
186void SkLinearGradient::
187LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800188 if (!this->isFast()) {
189 this->INHERITED::shadeSpan(x, y, dst, count);
190 return;
191 }
192
fmalitabc590c02016-02-22 09:12:33 -0800193 // TODO: plumb dithering
194 SkASSERT(count > 0);
195 if (fColorsArePremul) {
fmalitaa928b282016-03-18 10:28:23 -0700196 this->shadePremulSpan<SkPMColor,
197 kLinear_SkColorProfileType,
198 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800199 } else {
fmalitaa928b282016-03-18 10:28:23 -0700200 this->shadePremulSpan<SkPMColor,
201 kLinear_SkColorProfileType,
202 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800203 }
204}
205
206void SkLinearGradient::
207LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800208 if (!this->isFast()) {
209 this->INHERITED::shadeSpan4f(x, y, dst, count);
210 return;
211 }
212
fmalitabc590c02016-02-22 09:12:33 -0800213 // TONOTDO: plumb dithering
214 SkASSERT(count > 0);
215 if (fColorsArePremul) {
fmalitaa928b282016-03-18 10:28:23 -0700216 this->shadePremulSpan<SkPM4f,
217 kLinear_SkColorProfileType,
218 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800219 } else {
fmalitaa928b282016-03-18 10:28:23 -0700220 this->shadePremulSpan<SkPM4f,
221 kLinear_SkColorProfileType,
222 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800223 }
224}
225
fmalitaa928b282016-03-18 10:28:23 -0700226template<typename DstType, SkColorProfileType profile, ApplyPremul premul>
fmalitabc590c02016-02-22 09:12:33 -0800227void SkLinearGradient::
228LinearGradient4fContext::shadePremulSpan(int x, int y,
229 DstType dst[],
230 int count) const {
231 const SkLinearGradient& shader =
232 static_cast<const SkLinearGradient&>(fShader);
233 switch (shader.fTileMode) {
234 case kClamp_TileMode:
235 this->shadeSpanInternal<DstType,
fmalitaa928b282016-03-18 10:28:23 -0700236 profile,
237 premul,
fmalitabc590c02016-02-22 09:12:33 -0800238 kClamp_TileMode>(x, y, dst, count);
239 break;
240 case kRepeat_TileMode:
241 this->shadeSpanInternal<DstType,
fmalitaa928b282016-03-18 10:28:23 -0700242 profile,
243 premul,
fmalitabc590c02016-02-22 09:12:33 -0800244 kRepeat_TileMode>(x, y, dst, count);
245 break;
246 case kMirror_TileMode:
247 this->shadeSpanInternal<DstType,
fmalitaa928b282016-03-18 10:28:23 -0700248 profile,
249 premul,
fmalitabc590c02016-02-22 09:12:33 -0800250 kMirror_TileMode>(x, y, dst, count);
251 break;
252 }
253}
254
fmalitaa928b282016-03-18 10:28:23 -0700255template<typename DstType, SkColorProfileType profile, ApplyPremul premul,
256 SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800257void SkLinearGradient::
258LinearGradient4fContext::shadeSpanInternal(int x, int y,
259 DstType dst[],
260 int count) const {
261 SkPoint pt;
262 fDstToPosProc(fDstToPos,
263 x + SK_ScalarHalf,
264 y + SK_ScalarHalf,
265 &pt);
266 const SkScalar fx = pinFx<tileMode>(pt.x());
267 const SkScalar dx = fDstToPos.getScaleX();
fmalitaa928b282016-03-18 10:28:23 -0700268 LinearIntervalProcessor<DstType, profile, tileMode> proc(fIntervals.begin(),
269 fIntervals.end() - 1,
270 this->findInterval(fx),
271 fx,
272 dx,
273 SkScalarNearlyZero(dx * count));
fmalitabc590c02016-02-22 09:12:33 -0800274 while (count > 0) {
275 // What we really want here is SkTPin(advance, 1, count)
276 // but that's a significant perf hit for >> stops; investigate.
277 const int n = SkScalarTruncToInt(
278 SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
279
280 // The current interval advance can be +inf (e.g. when reaching
281 // the clamp mode end intervals) - when that happens, we expect to
282 // a) consume all remaining count in one swoop
283 // b) return a zero color gradient
284 SkASSERT(SkScalarIsFinite(proc.currentAdvance())
285 || (n == count && proc.currentRampIsZero()));
286
287 if (proc.currentRampIsZero()) {
fmalitaa928b282016-03-18 10:28:23 -0700288 fill<DstType, profile, premul>(proc.currentColor(),
289 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800290 } else {
fmalitaa928b282016-03-18 10:28:23 -0700291 ramp<DstType, profile, premul>(proc.currentColor(),
292 proc.currentColorGrad(),
293 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800294 }
295
296 proc.advance(SkIntToScalar(n));
297 count -= n;
298 dst += n;
299 }
300}
301
fmalitaa928b282016-03-18 10:28:23 -0700302template<typename DstType, SkColorProfileType profile, SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800303class SkLinearGradient::
304LinearGradient4fContext::LinearIntervalProcessor {
305public:
306 LinearIntervalProcessor(const Interval* firstInterval,
307 const Interval* lastInterval,
308 const Interval* i,
309 SkScalar fx,
310 SkScalar dx,
311 bool is_vertical)
fmalitaa928b282016-03-18 10:28:23 -0700312 : fAdvX((i->fP1 - fx) / dx)
fmalitabc590c02016-02-22 09:12:33 -0800313 , fFirstInterval(firstInterval)
314 , fLastInterval(lastInterval)
315 , fInterval(i)
316 , fDx(dx)
317 , fIsVertical(is_vertical)
318 {
319 SkASSERT(firstInterval <= lastInterval);
fmalita7e6fcf82016-03-10 11:18:43 -0800320 SkASSERT(in_range(fx, i->fP0, i->fP1));
fmalitabc590c02016-02-22 09:12:33 -0800321 this->compute_interval_props(fx - i->fP0);
322 }
323
324 SkScalar currentAdvance() const {
325 SkASSERT(fAdvX >= 0);
326 SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx);
327 return fAdvX;
328 }
329
330 bool currentRampIsZero() const { return fZeroRamp; }
331 const Sk4f& currentColor() const { return fCc; }
332 const Sk4f& currentColorGrad() const { return fDcDx; }
333
334 void advance(SkScalar advX) {
335 SkASSERT(advX > 0);
336 SkASSERT(fAdvX >= 0);
337
338 if (advX >= fAdvX) {
339 advX = this->advance_interval(advX);
340 }
341 SkASSERT(advX < fAdvX);
342
343 fCc = fCc + fDcDx * Sk4f(advX);
344 fAdvX -= advX;
345 }
346
347private:
348 void compute_interval_props(SkScalar t) {
fmalitad8a4f772016-03-08 10:20:49 -0800349 fDc = dst_swizzle<DstType>(fInterval->fDc);
350 fCc = dst_swizzle<DstType>(fInterval->fC0);
fmalitabc590c02016-02-22 09:12:33 -0800351 fCc = fCc + fDc * Sk4f(t);
fmalitaa928b282016-03-18 10:28:23 -0700352 fCc = scale_for_dest<DstType, profile>(fCc);
353 fDcDx = scale_for_dest<DstType, profile>(fDc * Sk4f(fDx));
fmalitabc590c02016-02-22 09:12:33 -0800354 fZeroRamp = fIsVertical || fInterval->isZeroRamp();
355 }
356
357 const Interval* next_interval(const Interval* i) const {
358 SkASSERT(i >= fFirstInterval);
359 SkASSERT(i <= fLastInterval);
360 i++;
361
362 if (tileMode == kClamp_TileMode) {
363 SkASSERT(i <= fLastInterval);
364 return i;
365 }
366
367 return (i <= fLastInterval) ? i : fFirstInterval;
368 }
369
370 SkScalar advance_interval(SkScalar advX) {
371 SkASSERT(advX >= fAdvX);
372
373 do {
374 advX -= fAdvX;
375 fInterval = this->next_interval(fInterval);
376 fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
377 SkASSERT(fAdvX > 0);
378 } while (advX >= fAdvX);
379
380 compute_interval_props(0);
381
382 SkASSERT(advX >= 0);
383 return advX;
384 }
385
fmalitabc590c02016-02-22 09:12:33 -0800386 // Current interval properties.
387 Sk4f fDc; // local color gradient (dc/dt)
388 Sk4f fDcDx; // dst color gradient (dc/dx)
389 Sk4f fCc; // current color, interpolated in dst
390 SkScalar fAdvX; // remaining interval advance in dst
391 bool fZeroRamp; // current interval color grad is 0
392
393 const Interval* fFirstInterval;
394 const Interval* fLastInterval;
395 const Interval* fInterval; // current interval
396 const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
397 const bool fIsVertical;
398};
fmalita7e6fcf82016-03-10 11:18:43 -0800399
400void SkLinearGradient::
401LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const {
402 SkASSERT(count > 0);
403 SkASSERT(fDstToPosClass != kLinear_MatrixClass);
404
405 SkScalar sx = x + SK_ScalarHalf;
406 const SkScalar sy = y + SK_ScalarHalf;
407 SkPoint pt;
408
409 if (fDstToPosClass != kPerspective_MatrixClass) {
410 // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline
411 const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x();
412 fDstToPosProc(fDstToPos, sx, sy, &pt);
413
414 const Sk4f dtdx4 = Sk4f(4 * dtdx);
415 Sk4f t4 = Sk4f(pt.x() + 0 * dtdx,
416 pt.x() + 1 * dtdx,
417 pt.x() + 2 * dtdx,
418 pt.x() + 3 * dtdx);
419
420 while (count >= 4) {
421 t4.store(ts);
422 t4 = t4 + dtdx4;
423 ts += 4;
424 count -= 4;
425 }
426
427 if (count & 2) {
428 *ts++ = t4[0];
429 *ts++ = t4[1];
430 t4 = SkNx_shuffle<2, 0, 1, 3>(t4);
431 }
432
433 if (count & 1) {
434 *ts++ = t4[0];
435 }
436 } else {
437 for (int i = 0; i < count; ++i) {
438 fDstToPosProc(fDstToPos, sx, sy, &pt);
439 ts[i] = pt.x();
440 sx += SK_Scalar1;
441 }
442 }
443}
fmalitaa928b282016-03-18 10:28:23 -0700444
reed58fc94e2016-03-18 12:42:26 -0700445bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info,
446 BlitState* state) {
fmalitaa928b282016-03-18 10:28:23 -0700447 SkXfermode::Mode mode;
448 if (!SkXfermode::AsMode(state->fXfer, &mode)) {
reed58fc94e2016-03-18 12:42:26 -0700449 return false;
fmalitaa928b282016-03-18 10:28:23 -0700450 }
451
452 const SkGradientShaderBase& shader = static_cast<const SkGradientShaderBase&>(fShader);
453 if (mode != SkXfermode::kSrc_Mode &&
454 !(mode == SkXfermode::kSrcOver_Mode && shader.colorsAreOpaque())) {
reed58fc94e2016-03-18 12:42:26 -0700455 return false;
fmalitaa928b282016-03-18 10:28:23 -0700456 }
457
458 switch (info.colorType()) {
459 case kN32_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700460 state->fBlitBW = D32_BlitBW;
461 return true;
fmalitaa928b282016-03-18 10:28:23 -0700462 case kRGBA_F16_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700463 state->fBlitBW = D64_BlitBW;
464 return true;
fmalitaa928b282016-03-18 10:28:23 -0700465 default:
reed58fc94e2016-03-18 12:42:26 -0700466 return false;
fmalitaa928b282016-03-18 10:28:23 -0700467 }
468}
469
470void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700471LinearGradient4fContext::D32_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
472 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700473 // FIXME: ignoring coverage for now
474 const LinearGradient4fContext* ctx =
475 static_cast<const LinearGradient4fContext*>(state->fCtx);
476
477 if (dst.info().isLinear()) {
478 if (ctx->fColorsArePremul) {
479 ctx->shadePremulSpan<SkPMColor, kLinear_SkColorProfileType, ApplyPremul::False>(
480 x, y, dst.writable_addr32(x, y), count);
481 } else {
482 ctx->shadePremulSpan<SkPMColor, kLinear_SkColorProfileType, ApplyPremul::True>(
483 x, y, dst.writable_addr32(x, y), count);
484 }
485 } else {
486 if (ctx->fColorsArePremul) {
487 ctx->shadePremulSpan<SkPMColor, kSRGB_SkColorProfileType, ApplyPremul::False>(
488 x, y, dst.writable_addr32(x, y), count);
489 } else {
490 ctx->shadePremulSpan<SkPMColor, kSRGB_SkColorProfileType, ApplyPremul::True>(
491 x, y, dst.writable_addr32(x, y), count);
492 }
493 }
494}
495
496void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700497LinearGradient4fContext::D64_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
498 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700499 // FIXME: ignoring coverage for now
500 const LinearGradient4fContext* ctx =
501 static_cast<const LinearGradient4fContext*>(state->fCtx);
502
503 if (ctx->fColorsArePremul) {
504 ctx->shadePremulSpan<uint64_t, kLinear_SkColorProfileType, ApplyPremul::False>(
505 x, y, dst.writable_addr64(x, y), count);
506 } else {
507 ctx->shadePremulSpan<uint64_t, kLinear_SkColorProfileType, ApplyPremul::True>(
508 x, y, dst.writable_addr64(x, y), count);
509 }
510}