blob: 29d908842504de67ee5f9cf434a75e2601f9336a [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
Ben Wagner8a1036c2016-11-09 15:00:49 -050012#include <cmath>
13
fmalitabc590c02016-02-22 09:12:33 -080014namespace {
15
fmalitadc6c9bf2016-03-21 13:16:51 -070016template<DstType dstType, ApplyPremul premul>
17void ramp(const Sk4f& c, const Sk4f& dc, typename DstTraits<dstType, premul>::Type dst[], int n) {
fmalitabc590c02016-02-22 09:12:33 -080018 SkASSERT(n > 0);
19
20 const Sk4f dc2 = dc + dc;
21 const Sk4f dc4 = dc2 + dc2;
22
23 Sk4f c0 = c ;
24 Sk4f c1 = c + dc;
25 Sk4f c2 = c0 + dc2;
26 Sk4f c3 = c1 + dc2;
27
28 while (n >= 4) {
fmalitadc6c9bf2016-03-21 13:16:51 -070029 DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst);
fmalitabc590c02016-02-22 09:12:33 -080030 dst += 4;
31
32 c0 = c0 + dc4;
33 c1 = c1 + dc4;
34 c2 = c2 + dc4;
35 c3 = c3 + dc4;
36 n -= 4;
37 }
38 if (n & 2) {
fmalitadc6c9bf2016-03-21 13:16:51 -070039 DstTraits<dstType, premul>::store(c0, dst++);
40 DstTraits<dstType, premul>::store(c1, dst++);
fmalitabc590c02016-02-22 09:12:33 -080041 c0 = c0 + dc2;
42 }
43 if (n & 1) {
fmalitadc6c9bf2016-03-21 13:16:51 -070044 DstTraits<dstType, premul>::store(c0, dst);
fmalitabc590c02016-02-22 09:12:33 -080045 }
46}
47
fmalita83aa9202016-03-23 12:28:14 -070048// Planar version of ramp (S32 no-premul only).
49template<>
50void ramp<DstType::S32, ApplyPremul::False>(const Sk4f& c, const Sk4f& dc, SkPMColor dst[], int n) {
51 SkASSERT(n > 0);
52
53 const Sk4f dc4 = dc * 4;
54 const Sk4x4f dc4x = { Sk4f(dc4[0]), Sk4f(dc4[1]), Sk4f(dc4[2]), Sk4f(dc4[3]) };
55 Sk4x4f c4x = Sk4x4f::Transpose(c, c + dc, c + dc * 2, c + dc * 3);
56
57 while (n >= 4) {
mtklein0c902472016-07-20 18:10:07 -070058 ( sk_linear_to_srgb(c4x.r) << 0
59 | sk_linear_to_srgb(c4x.g) << 8
60 | sk_linear_to_srgb(c4x.b) << 16
61 | Sk4f_round(255.0f*c4x.a) << 24).store(dst);
fmalita83aa9202016-03-23 12:28:14 -070062
63 c4x.r += dc4x.r;
64 c4x.g += dc4x.g;
65 c4x.b += dc4x.b;
66 c4x.a += dc4x.a;
67
68 dst += 4;
69 n -= 4;
70 }
71
72 if (n & 2) {
73 DstTraits<DstType::S32, ApplyPremul::False>
74 ::store(Sk4f(c4x.r[0], c4x.g[0], c4x.b[0], c4x.a[0]), dst++);
75 DstTraits<DstType::S32, ApplyPremul::False>
76 ::store(Sk4f(c4x.r[1], c4x.g[1], c4x.b[1], c4x.a[1]), dst++);
77 }
78
79 if (n & 1) {
80 DstTraits<DstType::S32, ApplyPremul::False>
81 ::store(Sk4f(c4x.r[n & 2], c4x.g[n & 2], c4x.b[n & 2], c4x.a[n & 2]), dst);
82 }
83}
84
fmalitabc590c02016-02-22 09:12:33 -080085template<SkShader::TileMode>
86SkScalar pinFx(SkScalar);
87
88template<>
89SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
90 return fx;
91}
92
93template<>
94SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
95 const SkScalar f = SkScalarFraction(fx);
96 return f < 0 ? f + 1 : f;
97}
98
99template<>
100SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
101 const SkScalar f = SkScalarMod(fx, 2.0f);
102 return f < 0 ? f + 2 : f;
103}
104
fmalita6d7e4e82016-09-20 06:55:16 -0700105// true when x is in [k1,k2), or [k2, k1) when the interval is reversed.
106// TODO(fmalita): hoist the reversed interval check out of this helper.
fmalita7520fc42016-03-04 11:01:24 -0800107bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
108 SkASSERT(k1 != k2);
109 return (k1 < k2)
fmalitaafac5812016-11-01 13:41:34 -0700110 ? (x >= k1 && x < k2)
111 : (x >= k2 && x < k1);
fmalita7520fc42016-03-04 11:01:24 -0800112}
113
fmalitabc590c02016-02-22 09:12:33 -0800114} // anonymous namespace
115
116SkLinearGradient::
117LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
118 const ContextRec& rec)
fmalita7520fc42016-03-04 11:01:24 -0800119 : INHERITED(shader, rec) {
fmalita7520fc42016-03-04 11:01:24 -0800120
fmalita7e6fcf82016-03-10 11:18:43 -0800121 // Our fast path expects interval points to be monotonically increasing in x.
Ben Wagner8a1036c2016-11-09 15:00:49 -0500122 const bool reverseIntervals = this->isFast() && std::signbit(fDstToPos.getScaleX());
fmalita7e6fcf82016-03-10 11:18:43 -0800123 this->buildIntervals(shader, rec, reverseIntervals);
fmalita7520fc42016-03-04 11:01:24 -0800124
125 SkASSERT(fIntervals.count() > 0);
126 fCachedInterval = fIntervals.begin();
127}
128
fmalita7520fc42016-03-04 11:01:24 -0800129const SkGradientShaderBase::GradientShaderBase4fContext::Interval*
130SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
131 SkASSERT(in_range(fx, fIntervals.front().fP0, fIntervals.back().fP1));
132
133 if (1) {
134 // Linear search, using the last scanline interval as a starting point.
135 SkASSERT(fCachedInterval >= fIntervals.begin());
136 SkASSERT(fCachedInterval < fIntervals.end());
137 const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
138 while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) {
139 fCachedInterval += search_dir;
140 if (fCachedInterval >= fIntervals.end()) {
141 fCachedInterval = fIntervals.begin();
142 } else if (fCachedInterval < fIntervals.begin()) {
143 fCachedInterval = fIntervals.end() - 1;
144 }
145 }
146 return fCachedInterval;
147 } else {
148 // Binary search. Seems less effective than linear + caching.
149 const Interval* i0 = fIntervals.begin();
150 const Interval* i1 = fIntervals.end() - 1;
151
152 while (i0 != i1) {
153 SkASSERT(i0 < i1);
154 SkASSERT(in_range(fx, i0->fP0, i1->fP1));
155
156 const Interval* i = i0 + ((i1 - i0) >> 1);
157
158 if (in_range(fx, i0->fP0, i->fP1)) {
159 i1 = i;
160 } else {
161 SkASSERT(in_range(fx, i->fP1, i1->fP1));
162 i0 = i + 1;
163 }
164 }
165
166 SkASSERT(in_range(fx, i0->fP0, i0->fP1));
167 return i0;
168 }
169}
fmalitabc590c02016-02-22 09:12:33 -0800170
171void SkLinearGradient::
172LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800173 if (!this->isFast()) {
174 this->INHERITED::shadeSpan(x, y, dst, count);
175 return;
176 }
177
fmalitabc590c02016-02-22 09:12:33 -0800178 // TODO: plumb dithering
179 SkASSERT(count > 0);
180 if (fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700181 this->shadePremulSpan<DstType::L32,
fmalitaa928b282016-03-18 10:28:23 -0700182 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800183 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700184 this->shadePremulSpan<DstType::L32,
fmalitaa928b282016-03-18 10:28:23 -0700185 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800186 }
187}
188
189void SkLinearGradient::
190LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
fmalita7e6fcf82016-03-10 11:18:43 -0800191 if (!this->isFast()) {
192 this->INHERITED::shadeSpan4f(x, y, dst, count);
193 return;
194 }
195
fmalitabc590c02016-02-22 09:12:33 -0800196 // TONOTDO: plumb dithering
197 SkASSERT(count > 0);
198 if (fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700199 this->shadePremulSpan<DstType::F32,
fmalitaa928b282016-03-18 10:28:23 -0700200 ApplyPremul::False>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800201 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700202 this->shadePremulSpan<DstType::F32,
fmalitaa928b282016-03-18 10:28:23 -0700203 ApplyPremul::True>(x, y, dst, count);
fmalitabc590c02016-02-22 09:12:33 -0800204 }
205}
206
fmalitadc6c9bf2016-03-21 13:16:51 -0700207template<DstType dstType, ApplyPremul premul>
fmalitabc590c02016-02-22 09:12:33 -0800208void SkLinearGradient::
209LinearGradient4fContext::shadePremulSpan(int x, int y,
fmalitadc6c9bf2016-03-21 13:16:51 -0700210 typename DstTraits<dstType, premul>::Type dst[],
fmalitabc590c02016-02-22 09:12:33 -0800211 int count) const {
212 const SkLinearGradient& shader =
213 static_cast<const SkLinearGradient&>(fShader);
214 switch (shader.fTileMode) {
215 case kClamp_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 kClamp_TileMode>(x, y, dst, count);
219 break;
220 case kRepeat_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 kRepeat_TileMode>(x, y, dst, count);
224 break;
225 case kMirror_TileMode:
fmalitadc6c9bf2016-03-21 13:16:51 -0700226 this->shadeSpanInternal<dstType,
fmalitaa928b282016-03-18 10:28:23 -0700227 premul,
fmalitabc590c02016-02-22 09:12:33 -0800228 kMirror_TileMode>(x, y, dst, count);
229 break;
230 }
231}
232
fmalitadc6c9bf2016-03-21 13:16:51 -0700233template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800234void SkLinearGradient::
235LinearGradient4fContext::shadeSpanInternal(int x, int y,
fmalitadc6c9bf2016-03-21 13:16:51 -0700236 typename DstTraits<dstType, premul>::Type dst[],
fmalitabc590c02016-02-22 09:12:33 -0800237 int count) const {
238 SkPoint pt;
239 fDstToPosProc(fDstToPos,
240 x + SK_ScalarHalf,
241 y + SK_ScalarHalf,
242 &pt);
243 const SkScalar fx = pinFx<tileMode>(pt.x());
244 const SkScalar dx = fDstToPos.getScaleX();
fmalita3a2e45a2016-10-14 08:18:24 -0700245 LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals.begin(),
246 fIntervals.end() - 1,
247 this->findInterval(fx),
248 fx,
249 dx,
250 SkScalarNearlyZero(dx * count));
fmalitabc590c02016-02-22 09:12:33 -0800251 while (count > 0) {
252 // What we really want here is SkTPin(advance, 1, count)
253 // but that's a significant perf hit for >> stops; investigate.
254 const int n = SkScalarTruncToInt(
255 SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
256
257 // The current interval advance can be +inf (e.g. when reaching
258 // the clamp mode end intervals) - when that happens, we expect to
259 // a) consume all remaining count in one swoop
260 // b) return a zero color gradient
261 SkASSERT(SkScalarIsFinite(proc.currentAdvance())
262 || (n == count && proc.currentRampIsZero()));
263
264 if (proc.currentRampIsZero()) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700265 DstTraits<dstType, premul>::store(proc.currentColor(),
266 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800267 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700268 ramp<dstType, premul>(proc.currentColor(),
269 proc.currentColorGrad(),
270 dst, n);
fmalitabc590c02016-02-22 09:12:33 -0800271 }
272
273 proc.advance(SkIntToScalar(n));
274 count -= n;
275 dst += n;
276 }
277}
278
fmalita3a2e45a2016-10-14 08:18:24 -0700279template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
fmalitabc590c02016-02-22 09:12:33 -0800280class SkLinearGradient::
281LinearGradient4fContext::LinearIntervalProcessor {
282public:
283 LinearIntervalProcessor(const Interval* firstInterval,
284 const Interval* lastInterval,
285 const Interval* i,
286 SkScalar fx,
287 SkScalar dx,
288 bool is_vertical)
fmalitaa928b282016-03-18 10:28:23 -0700289 : fAdvX((i->fP1 - fx) / dx)
fmalitabc590c02016-02-22 09:12:33 -0800290 , fFirstInterval(firstInterval)
291 , fLastInterval(lastInterval)
292 , fInterval(i)
293 , fDx(dx)
294 , fIsVertical(is_vertical)
295 {
fmalita6d7e4e82016-09-20 06:55:16 -0700296 SkASSERT(fAdvX >= 0);
fmalitabc590c02016-02-22 09:12:33 -0800297 SkASSERT(firstInterval <= lastInterval);
fmalita7e6fcf82016-03-10 11:18:43 -0800298 SkASSERT(in_range(fx, i->fP0, i->fP1));
fmalitaafac5812016-11-01 13:41:34 -0700299
300 if (tileMode != kClamp_TileMode && !is_vertical) {
301 const auto spanX = (lastInterval->fP1 - firstInterval->fP0) / dx;
302 SkASSERT(spanX >= 0);
303
304 // If we're in a repeating tile mode and the whole gradient is compressed into a
305 // fraction of a pixel, we just use the average color in zero-ramp mode.
306 // This also avoids cases where we make no progress due to interval advances being
307 // close to zero.
308 static constexpr SkScalar kMinSpanX = .25f;
309 if (spanX < kMinSpanX) {
310 this->init_average_props();
311 return;
312 }
313 }
314
fmalitabc590c02016-02-22 09:12:33 -0800315 this->compute_interval_props(fx - i->fP0);
316 }
317
318 SkScalar currentAdvance() const {
319 SkASSERT(fAdvX >= 0);
Ben Wagner8a1036c2016-11-09 15:00:49 -0500320 SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx || !std::isfinite(fAdvX));
fmalitabc590c02016-02-22 09:12:33 -0800321 return fAdvX;
322 }
323
324 bool currentRampIsZero() const { return fZeroRamp; }
325 const Sk4f& currentColor() const { return fCc; }
326 const Sk4f& currentColorGrad() const { return fDcDx; }
327
328 void advance(SkScalar advX) {
329 SkASSERT(advX > 0);
330 SkASSERT(fAdvX >= 0);
331
332 if (advX >= fAdvX) {
333 advX = this->advance_interval(advX);
334 }
335 SkASSERT(advX < fAdvX);
336
337 fCc = fCc + fDcDx * Sk4f(advX);
338 fAdvX -= advX;
339 }
340
341private:
342 void compute_interval_props(SkScalar t) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700343 fZeroRamp = fIsVertical || fInterval->isZeroRamp();
fmalita8f457592016-10-21 06:02:22 -0700344 fCc = DstTraits<dstType, premul>::load(fInterval->fC0);
345
346 if (fInterval->isZeroRamp()) {
347 fDcDx = 0;
348 } else {
349 const Sk4f dC = DstTraits<dstType, premul>::load(fInterval->fDc);
350 fCc = fCc + dC * Sk4f(t);
351 fDcDx = dC * fDx;
352 }
fmalitabc590c02016-02-22 09:12:33 -0800353 }
354
fmalitaafac5812016-11-01 13:41:34 -0700355 void init_average_props() {
356 fAdvX = SK_ScalarInfinity;
357 fZeroRamp = true;
358 fDcDx = 0;
359 fCc = Sk4f(0);
360
361 // TODO: precompute the average at interval setup time?
362 for (const auto* i = fFirstInterval; i <= fLastInterval; ++i) {
363 // Each interval contributes its average color to the total/weighted average:
364 //
365 // C = (c0 + c1) / 2 = (c0 + c0 + dc * (p1 - p0)) / 2
366 //
367 // Avg += C * (p1 - p0)
368 //
369 const auto dp = i->fP1 - i->fP0;
370 auto c = DstTraits<dstType, premul>::load(i->fC0);
371 if (!i->fZeroRamp) {
372 c = c + DstTraits<dstType, premul>::load(i->fDc) * dp * 0.5f;
373 }
374 fCc = fCc + c * dp;
375 }
376 }
377
fmalitabc590c02016-02-22 09:12:33 -0800378 const Interval* next_interval(const Interval* i) const {
379 SkASSERT(i >= fFirstInterval);
380 SkASSERT(i <= fLastInterval);
381 i++;
382
383 if (tileMode == kClamp_TileMode) {
384 SkASSERT(i <= fLastInterval);
385 return i;
386 }
387
388 return (i <= fLastInterval) ? i : fFirstInterval;
389 }
390
391 SkScalar advance_interval(SkScalar advX) {
392 SkASSERT(advX >= fAdvX);
393
394 do {
395 advX -= fAdvX;
396 fInterval = this->next_interval(fInterval);
397 fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
398 SkASSERT(fAdvX > 0);
399 } while (advX >= fAdvX);
400
401 compute_interval_props(0);
402
403 SkASSERT(advX >= 0);
404 return advX;
405 }
406
fmalitabc590c02016-02-22 09:12:33 -0800407 // Current interval properties.
fmalitabc590c02016-02-22 09:12:33 -0800408 Sk4f fDcDx; // dst color gradient (dc/dx)
409 Sk4f fCc; // current color, interpolated in dst
410 SkScalar fAdvX; // remaining interval advance in dst
411 bool fZeroRamp; // current interval color grad is 0
412
413 const Interval* fFirstInterval;
414 const Interval* fLastInterval;
415 const Interval* fInterval; // current interval
416 const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
417 const bool fIsVertical;
418};
fmalita7e6fcf82016-03-10 11:18:43 -0800419
420void SkLinearGradient::
421LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const {
422 SkASSERT(count > 0);
423 SkASSERT(fDstToPosClass != kLinear_MatrixClass);
424
425 SkScalar sx = x + SK_ScalarHalf;
426 const SkScalar sy = y + SK_ScalarHalf;
427 SkPoint pt;
428
429 if (fDstToPosClass != kPerspective_MatrixClass) {
430 // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline
431 const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x();
432 fDstToPosProc(fDstToPos, sx, sy, &pt);
433
434 const Sk4f dtdx4 = Sk4f(4 * dtdx);
435 Sk4f t4 = Sk4f(pt.x() + 0 * dtdx,
436 pt.x() + 1 * dtdx,
437 pt.x() + 2 * dtdx,
438 pt.x() + 3 * dtdx);
439
440 while (count >= 4) {
441 t4.store(ts);
442 t4 = t4 + dtdx4;
443 ts += 4;
444 count -= 4;
445 }
446
447 if (count & 2) {
448 *ts++ = t4[0];
449 *ts++ = t4[1];
450 t4 = SkNx_shuffle<2, 0, 1, 3>(t4);
451 }
452
453 if (count & 1) {
454 *ts++ = t4[0];
455 }
456 } else {
457 for (int i = 0; i < count; ++i) {
458 fDstToPosProc(fDstToPos, sx, sy, &pt);
459 ts[i] = pt.x();
460 sx += SK_Scalar1;
461 }
462 }
463}
fmalitaa928b282016-03-18 10:28:23 -0700464
reed58fc94e2016-03-18 12:42:26 -0700465bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info,
466 BlitState* state) {
Mike Reed6a015542016-11-09 10:38:09 -0500467 if (state->fMode != SkBlendMode::kSrc &&
468 !(state->fMode == SkBlendMode::kSrcOver && (fFlags & kOpaqueAlpha_Flag))) {
reed58fc94e2016-03-18 12:42:26 -0700469 return false;
fmalitaa928b282016-03-18 10:28:23 -0700470 }
471
472 switch (info.colorType()) {
473 case kN32_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700474 state->fBlitBW = D32_BlitBW;
475 return true;
fmalitaa928b282016-03-18 10:28:23 -0700476 case kRGBA_F16_SkColorType:
reed58fc94e2016-03-18 12:42:26 -0700477 state->fBlitBW = D64_BlitBW;
478 return true;
fmalitaa928b282016-03-18 10:28:23 -0700479 default:
reed58fc94e2016-03-18 12:42:26 -0700480 return false;
fmalitaa928b282016-03-18 10:28:23 -0700481 }
482}
483
484void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700485LinearGradient4fContext::D32_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
486 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700487 // FIXME: ignoring coverage for now
488 const LinearGradient4fContext* ctx =
489 static_cast<const LinearGradient4fContext*>(state->fCtx);
490
reeddabe5d32016-06-21 10:28:14 -0700491 if (!dst.info().gammaCloseToSRGB()) {
fmalitaa928b282016-03-18 10:28:23 -0700492 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700493 ctx->shadePremulSpan<DstType::L32, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700494 x, y, dst.writable_addr32(x, y), count);
495 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700496 ctx->shadePremulSpan<DstType::L32, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700497 x, y, dst.writable_addr32(x, y), count);
498 }
499 } else {
500 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700501 ctx->shadePremulSpan<DstType::S32, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700502 x, y, dst.writable_addr32(x, y), count);
503 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700504 ctx->shadePremulSpan<DstType::S32, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700505 x, y, dst.writable_addr32(x, y), count);
506 }
507 }
508}
509
510void SkLinearGradient::
reed58fc94e2016-03-18 12:42:26 -0700511LinearGradient4fContext::D64_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
512 int count) {
fmalitaa928b282016-03-18 10:28:23 -0700513 // FIXME: ignoring coverage for now
514 const LinearGradient4fContext* ctx =
515 static_cast<const LinearGradient4fContext*>(state->fCtx);
516
517 if (ctx->fColorsArePremul) {
fmalitadc6c9bf2016-03-21 13:16:51 -0700518 ctx->shadePremulSpan<DstType::F16, ApplyPremul::False>(
fmalitaa928b282016-03-18 10:28:23 -0700519 x, y, dst.writable_addr64(x, y), count);
520 } else {
fmalitadc6c9bf2016-03-21 13:16:51 -0700521 ctx->shadePremulSpan<DstType::F16, ApplyPremul::True>(
fmalitaa928b282016-03-18 10:28:23 -0700522 x, y, dst.writable_addr64(x, y), count);
523 }
524}