2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkLinearGradient.h"
10 static const float kInv255Float = 1.0f / 255;
12 static inline int repeat_bits(int x, const int bits) {
13 return x & ((1 << bits) - 1);
16 static inline int repeat_8bits(int x) {
20 // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
21 // See http://code.google.com/p/skia/issues/detail?id=472
22 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
23 #pragma optimize("", off)
26 static inline int mirror_bits(int x, const int bits) {
27 if (x & (1 << bits)) {
30 return x & ((1 << bits) - 1);
33 static inline int mirror_8bits(int x) {
40 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
41 #pragma optimize("", on)
44 static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
45 SkVector vec = pts[1] - pts[0];
46 SkScalar mag = vec.length();
47 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
51 matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
52 matrix.postTranslate(-pts[0].fX, -pts[0].fY);
53 matrix.postScale(inv, inv);
57 ///////////////////////////////////////////////////////////////////////////////
59 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
60 : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
65 SkFlattenable* SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
67 if (!desc.unflatten(buffer)) {
71 pts[0] = buffer.readPoint();
72 pts[1] = buffer.readPoint();
73 return SkGradientShader::CreateLinear(pts, desc.fColors, desc.fPos, desc.fCount,
74 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
77 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
78 this->INHERITED::flatten(buffer);
79 buffer.writePoint(fStart);
80 buffer.writePoint(fEnd);
83 size_t SkLinearGradient::contextSize() const {
84 return sizeof(LinearGradientContext);
87 SkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void* storage) const {
88 return new (storage) LinearGradientContext(*this, rec);
91 // This swizzles SkColor into the same component order as SkPMColor, but does not actually
92 // "pre" multiply the color components.
94 // This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
95 // SkPMColor from the floats, without having to swizzle each time.
97 static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
98 return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
101 SkLinearGradient::LinearGradientContext::LinearGradientContext(
102 const SkLinearGradient& shader, const ContextRec& ctx)
103 : INHERITED(shader, ctx)
105 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
106 if ((fDstToIndex.getType() & ~mask) == 0) {
107 // when we dither, we are (usually) not const-in-Y
108 if ((fFlags & SkShader::kHasSpan16_Flag) && !ctx.fPaint->isDither()) {
109 // only claim this if we do have a 16bit mode (i.e. none of our
110 // colors have alpha), and if we are not dithering (which obviously
111 // is not const in Y).
112 fFlags |= SkShader::kConstInY16_Flag;
117 int count = shader.fColorCount;
118 fRecs.setCount(count);
119 Rec* rec = fRecs.begin();
120 if (shader.fOrigPos) {
122 SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;) // should never get used
123 for (int i = 1; i < count; ++i) {
124 rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
125 rec[i].fPosScale = 1.0f / (rec[i].fPos - rec[i - 1].fPos);
127 rec[count - 1].fPos = 1; // overwrite the last value just to be sure we end at 1.0
129 // no pos specified, so we compute evenly spaced values
130 const float scale = float(count - 1);
131 float invScale = 1.0f / scale;
132 for (int i = 0; i < count; ++i) {
133 rec[i].fPos = i * invScale;
134 rec[i].fPosScale = scale;
138 fApplyAlphaAfterInterp = true;
139 if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
140 shader.colorsAreOpaque())
142 fApplyAlphaAfterInterp = false;
145 if (fApplyAlphaAfterInterp) {
146 // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
147 // interpolate in unpremultiplied space first, and then scale by alpha right before we
148 // convert to SkPMColor bytes.
149 const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
150 const Sk4f scale(1, 1, 1, paintAlpha);
151 for (int i = 0; i < count; ++i) {
152 uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
153 rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&c) * scale;
155 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
159 // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
160 // of converting the floats down to bytes.
161 unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
162 for (int i = 0; i < count; ++i) {
163 SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
164 pmc = SkAlphaMulQ(pmc, alphaScale);
165 rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&pmc);
167 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
173 #define NO_CHECK_ITER \
175 unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
176 SkASSERT(fi <= 0xFF); \
178 *dstC++ = cache[toggle + fi]; \
179 toggle = next_dither_toggle(toggle); \
184 typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
185 SkPMColor* dstC, const SkPMColor* cache,
186 int toggle, int count);
188 // Linear interpolation (lerp) is unnecessary if there are no sharp
189 // discontinuities in the gradient - which must be true if there are
190 // only 2 colors - but it's cheap.
191 void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
192 SkPMColor* SK_RESTRICT dstC,
193 const SkPMColor* SK_RESTRICT cache,
194 int toggle, int count) {
195 // We're a vertical gradient, so no change in a span.
196 // If colors change sharply across the gradient, dithering is
197 // insufficient (it subsamples the color space) and we need to lerp.
198 unsigned fullIndex = proc(SkGradFixedToFixed(fx));
199 unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
200 unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
202 int index0 = fi + toggle;
204 if (fi < SkGradientShaderBase::kCache32Count - 1) {
207 SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
208 index0 ^= SkGradientShaderBase::kDitherStride32;
209 index1 ^= SkGradientShaderBase::kDitherStride32;
210 SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
211 sk_memset32_dither(dstC, lerp, dlerp, count);
214 void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
215 SkPMColor* SK_RESTRICT dstC,
216 const SkPMColor* SK_RESTRICT cache,
217 int toggle, int count) {
219 range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
220 range.validate(count);
222 if ((count = range.fCount0) > 0) {
223 sk_memset32_dither(dstC,
224 cache[toggle + range.fV0],
225 cache[next_dither_toggle(toggle) + range.fV0],
229 if ((count = range.fCount1) > 0) {
230 int unroll = count >> 3;
232 for (int i = 0; i < unroll; i++) {
233 NO_CHECK_ITER; NO_CHECK_ITER;
234 NO_CHECK_ITER; NO_CHECK_ITER;
235 NO_CHECK_ITER; NO_CHECK_ITER;
236 NO_CHECK_ITER; NO_CHECK_ITER;
238 if ((count &= 7) > 0) {
241 } while (--count != 0);
244 if ((count = range.fCount2) > 0) {
245 sk_memset32_dither(dstC,
246 cache[toggle + range.fV1],
247 cache[next_dither_toggle(toggle) + range.fV1],
252 void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
253 SkPMColor* SK_RESTRICT dstC,
254 const SkPMColor* SK_RESTRICT cache,
255 int toggle, int count) {
257 unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
258 SkASSERT(fi <= 0xFF);
260 *dstC++ = cache[toggle + fi];
261 toggle = next_dither_toggle(toggle);
262 } while (--count != 0);
265 void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
266 SkPMColor* SK_RESTRICT dstC,
267 const SkPMColor* SK_RESTRICT cache,
268 int toggle, int count) {
270 unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
271 SkASSERT(fi <= 0xFF);
273 *dstC++ = cache[toggle + fi];
274 toggle = next_dither_toggle(toggle);
275 } while (--count != 0);
280 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
283 const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
285 // Only use the Sk4f impl when known to be fast.
286 #if defined(SKNX_IS_FAST)
287 if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
288 kLinear_MatrixClass == fDstToIndexClass)
290 this->shade4_clamp(x, y, dstC, count);
296 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
297 TileProc proc = linearGradient.fTileProc;
298 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
299 int toggle = init_dither_toggle(x, y);
301 if (fDstToIndexClass != kPerspective_MatrixClass) {
302 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
303 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
304 SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX);
306 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
307 SkFixed dxStorage[1];
308 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, nullptr);
309 // todo: do we need a real/high-precision value for dx here?
310 dx = SkFixedToGradFixed(dxStorage[0]);
312 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
313 dx = SkScalarToGradFixed(fDstToIndex.getScaleX());
316 LinearShadeProc shadeProc = shadeSpan_linear_repeat;
318 shadeProc = shadeSpan_linear_vertical_lerp;
319 } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
320 shadeProc = shadeSpan_linear_clamp;
321 } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
322 shadeProc = shadeSpan_linear_mirror;
324 SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
326 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
328 SkScalar dstX = SkIntToScalar(x);
329 SkScalar dstY = SkIntToScalar(y);
331 dstProc(fDstToIndex, dstX, dstY, &srcPt);
332 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
333 SkASSERT(fi <= 0xFFFF);
334 *dstC++ = cache[toggle + (fi >> kCache32Shift)];
335 toggle = next_dither_toggle(toggle);
337 } while (--count != 0);
341 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
343 commonAsAGradient(info);
344 info->fPoint[0] = fStart;
345 info->fPoint[1] = fEnd;
347 return kLinear_GradientType;
350 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
352 if (reinterpret_cast<uintptr_t>(dst) & 2) {
355 SkTSwap(value, other);
358 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
361 dst[count - 1] = value;
365 #define NO_CHECK_ITER_16 \
367 unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift; \
368 SkASSERT(fi < SkGradientShaderBase::kCache16Count); \
370 *dstC++ = cache[toggle + fi]; \
371 toggle = next_dither_toggle16(toggle); \
376 typedef void (*LinearShade16Proc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
377 uint16_t* dstC, const uint16_t* cache,
378 int toggle, int count);
380 void shadeSpan16_linear_vertical(TileProc proc, SkGradFixed dx, SkGradFixed fx,
381 uint16_t* SK_RESTRICT dstC,
382 const uint16_t* SK_RESTRICT cache,
383 int toggle, int count) {
384 // we're a vertical gradient, so no change in a span
385 unsigned fi = proc(SkGradFixedToFixed(fx)) >> SkGradientShaderBase::kCache16Shift;
386 SkASSERT(fi < SkGradientShaderBase::kCache16Count);
387 dither_memset16(dstC, cache[toggle + fi],
388 cache[next_dither_toggle16(toggle) + fi], count);
391 void shadeSpan16_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
392 uint16_t* SK_RESTRICT dstC,
393 const uint16_t* SK_RESTRICT cache,
394 int toggle, int count) {
396 range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
397 range.validate(count);
399 if ((count = range.fCount0) > 0) {
400 dither_memset16(dstC,
401 cache[toggle + range.fV0],
402 cache[next_dither_toggle16(toggle) + range.fV0],
406 if ((count = range.fCount1) > 0) {
407 int unroll = count >> 3;
409 for (int i = 0; i < unroll; i++) {
410 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
411 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
412 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
413 NO_CHECK_ITER_16; NO_CHECK_ITER_16;
415 if ((count &= 7) > 0) {
418 } while (--count != 0);
421 if ((count = range.fCount2) > 0) {
422 dither_memset16(dstC,
423 cache[toggle + range.fV1],
424 cache[next_dither_toggle16(toggle) + range.fV1],
429 void shadeSpan16_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
430 uint16_t* SK_RESTRICT dstC,
431 const uint16_t* SK_RESTRICT cache,
432 int toggle, int count) {
434 unsigned fi = mirror_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift,
435 SkGradientShaderBase::kCache16Bits);
436 SkASSERT(fi < SkGradientShaderBase::kCache16Count);
438 *dstC++ = cache[toggle + fi];
439 toggle = next_dither_toggle16(toggle);
440 } while (--count != 0);
443 void shadeSpan16_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
444 uint16_t* SK_RESTRICT dstC,
445 const uint16_t* SK_RESTRICT cache,
446 int toggle, int count) {
448 unsigned fi = repeat_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift,
449 SkGradientShaderBase::kCache16Bits);
450 SkASSERT(fi < SkGradientShaderBase::kCache16Count);
452 *dstC++ = cache[toggle + fi];
453 toggle = next_dither_toggle16(toggle);
454 } while (--count != 0);
458 static bool fixed_nearly_zero(SkFixed x) {
459 return SkAbs32(x) < (SK_Fixed1 >> 12);
462 void SkLinearGradient::LinearGradientContext::shadeSpan16(int x, int y,
463 uint16_t* SK_RESTRICT dstC, int count) {
466 const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
469 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
470 TileProc proc = linearGradient.fTileProc;
471 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
472 int toggle = init_dither_toggle16(x, y);
474 if (fDstToIndexClass != kPerspective_MatrixClass) {
475 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
476 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
477 SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX);
479 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
480 SkFixed dxStorage[1];
481 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, nullptr);
482 // todo: do we need a real/high-precision value for dx here?
483 dx = SkFixedToGradFixed(dxStorage[0]);
485 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
486 dx = SkScalarToGradFixed(fDstToIndex.getScaleX());
489 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
490 if (fixed_nearly_zero(SkGradFixedToFixed(dx))) {
491 shadeProc = shadeSpan16_linear_vertical;
492 } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
493 shadeProc = shadeSpan16_linear_clamp;
494 } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
495 shadeProc = shadeSpan16_linear_mirror;
497 SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
499 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
501 SkScalar dstX = SkIntToScalar(x);
502 SkScalar dstY = SkIntToScalar(y);
504 dstProc(fDstToIndex, dstX, dstY, &srcPt);
505 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
506 SkASSERT(fi <= 0xFFFF);
508 int index = fi >> kCache16Shift;
509 *dstC++ = cache[toggle + index];
510 toggle = next_dither_toggle16(toggle);
513 } while (--count != 0);
519 #include "gl/builders/GrGLProgramBuilder.h"
522 /////////////////////////////////////////////////////////////////////
524 class GrGLLinearGradient : public GrGLGradientEffect {
527 GrGLLinearGradient(const GrProcessor&) {}
529 virtual ~GrGLLinearGradient() { }
531 virtual void emitCode(EmitArgs&) override;
533 static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
534 b->add32(GenBaseGradientKey(processor));
539 typedef GrGLGradientEffect INHERITED;
542 /////////////////////////////////////////////////////////////////////
544 class GrLinearGradient : public GrGradientEffect {
547 static GrFragmentProcessor* Create(GrContext* ctx,
548 const SkLinearGradient& shader,
549 const SkMatrix& matrix,
550 SkShader::TileMode tm) {
551 return new GrLinearGradient(ctx, shader, matrix, tm);
554 virtual ~GrLinearGradient() { }
556 const char* name() const override { return "Linear Gradient"; }
559 GrLinearGradient(GrContext* ctx,
560 const SkLinearGradient& shader,
561 const SkMatrix& matrix,
562 SkShader::TileMode tm)
563 : INHERITED(ctx, shader, matrix, tm) {
564 this->initClassID<GrLinearGradient>();
567 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
568 return new GrGLLinearGradient(*this);
571 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
572 GrProcessorKeyBuilder* b) const override {
573 GrGLLinearGradient::GenKey(*this, caps, b);
576 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
578 typedef GrGradientEffect INHERITED;
581 /////////////////////////////////////////////////////////////////////
583 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
585 const GrFragmentProcessor* GrLinearGradient::TestCreate(GrProcessorTestData* d) {
586 SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
587 {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
589 SkColor colors[kMaxRandomGradientColors];
590 SkScalar stopsArray[kMaxRandomGradientColors];
591 SkScalar* stops = stopsArray;
592 SkShader::TileMode tm;
593 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
594 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
595 colors, stops, colorCount,
597 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
598 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
603 /////////////////////////////////////////////////////////////////////
605 void GrGLLinearGradient::emitCode(EmitArgs& args) {
606 const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
607 this->emitUniforms(args.fBuilder, ge);
608 SkString t = args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0);
610 this->emitColor(args.fBuilder,
619 /////////////////////////////////////////////////////////////////////
621 const GrFragmentProcessor* SkLinearGradient::asFragmentProcessor(
623 const SkMatrix& viewm,
624 const SkMatrix* localMatrix,
625 SkFilterQuality) const {
629 if (!this->getLocalMatrix().invert(&matrix)) {
634 if (!localMatrix->invert(&inv)) {
637 matrix.postConcat(inv);
639 matrix.postConcat(fPtsToUnit);
641 SkAutoTUnref<const GrFragmentProcessor> inner(
642 GrLinearGradient::Create(context, *this, matrix, fTileMode));
643 return GrFragmentProcessor::MulOutputByInputAlpha(inner);
649 #ifndef SK_IGNORE_TO_STRING
650 void SkLinearGradient::toString(SkString* str) const {
651 str->append("SkLinearGradient (");
653 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
654 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
656 this->INHERITED::toString(str);
662 ///////////////////////////////////////////////////////////////////////////////////////////////////
666 static const SkLinearGradient::LinearGradientContext::Rec*
667 find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
668 SkASSERT(tiledX >= 0 && tiledX <= 1);
670 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
671 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
672 SkASSERT(rec[0].fPos <= rec[1].fPos);
674 while (rec->fPos < tiledX) {
675 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
676 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
677 SkASSERT(rec[0].fPos <= rec[1].fPos);
683 static const SkLinearGradient::LinearGradientContext::Rec*
684 find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
685 SkASSERT(tiledX >= 0 && tiledX <= 1);
687 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
688 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
689 SkASSERT(rec[0].fPos <= rec[1].fPos);
690 while (tiledX < rec->fPos) {
692 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
693 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
694 SkASSERT(rec[0].fPos <= rec[1].fPos);
699 template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x) {
701 x.toBytes((uint8_t*)&c);
703 c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c),
704 SkGetPackedG32(c), SkGetPackedB32(c));
709 template <bool apply_alpha> void fill(SkPMColor dst[], int count,
710 const Sk4f& c4, const Sk4f& c4other) {
711 sk_memset32_dither(dst, trunc_from_255<apply_alpha>(c4),
712 trunc_from_255<apply_alpha>(c4other), count);
715 template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
716 // Assumes that c4 does not need to be dithered.
717 sk_memset32(dst, trunc_from_255<apply_alpha>(c4), count);
724 * - interp before or after premul
727 * - use fixed (32bit or 16bit) instead of floats?
730 static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
731 SkASSERT(fx >= rec[0].fPos);
732 SkASSERT(fx <= rec[1].fPos);
734 const float p0 = rec[0].fPos;
735 const Sk4f c0 = rec[0].fColor;
736 const Sk4f c1 = rec[1].fColor;
737 const Sk4f diffc = c1 - c0;
738 const float scale = rec[1].fPosScale;
739 const float t = (fx - p0) * scale;
740 return c0 + Sk4f(t) * diffc;
743 template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
744 const Sk4f& dither0, const Sk4f& dither1) {
746 Sk4f dc4 = dc2 + dc2;
747 Sk4f cd0 = c + dither0;
748 Sk4f cd1 = c + dc + dither1;
749 Sk4f cd2 = cd0 + dc2;
750 Sk4f cd3 = cd1 + dc2;
753 Sk4f::ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
756 *dstC++ = trunc_from_255<apply_alpha>(cd0);
757 *dstC++ = trunc_from_255<apply_alpha>(cd1);
758 *dstC++ = trunc_from_255<apply_alpha>(cd2);
759 *dstC++ = trunc_from_255<apply_alpha>(cd3);
768 *dstC++ = trunc_from_255<apply_alpha>(cd0);
769 *dstC++ = trunc_from_255<apply_alpha>(cd1);
773 *dstC++ = trunc_from_255<apply_alpha>(cd0);
777 template <bool apply_alpha, bool dx_is_pos>
778 void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
779 float fx, float dx, float invDx,
780 const float dither[2]) {
781 Sk4f dither0(dither[0]);
782 Sk4f dither1(dither[1]);
783 const Rec* rec = fRecs.begin();
785 const Sk4f dx4 = Sk4f(dx);
786 SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
790 int n = SkTMin(SkFloatToIntFloor(-fx * invDx) + 1, count);
791 fill<apply_alpha>(dstC, n, rec[0].fColor);
795 SkASSERT(0 == count || fx >= 0);
797 SkTSwap(dither0, dither1);
802 int n = SkTMin(SkFloatToIntFloor((1 - fx) * invDx) + 1, count);
803 fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
807 SkASSERT(0 == count || fx <= 1);
809 SkTSwap(dither0, dither1);
813 SkASSERT(count >= 0);
817 r = fRecs.begin(); // start at the beginning
819 r = fRecs.begin() + fRecs.count() - 2; // start at the end
825 fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
830 fill<apply_alpha>(dstC, count, rec[0].fColor);
836 r = find_forward(r, fx);
838 r = find_backward(r, fx);
840 SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
842 const float p0 = r[0].fPos;
843 const Sk4f c0 = r[0].fColor;
844 const float p1 = r[1].fPos;
845 const Sk4f diffc = Sk4f(r[1].fColor) - c0;
846 const float scale = r[1].fPosScale;
847 const float t = (fx - p0) * scale;
848 const Sk4f c = c0 + Sk4f(t) * diffc;
849 const Sk4f dc = diffc * dx4 * Sk4f(scale);
853 n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
855 n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
860 SkASSERT(count >= 0);
862 SkASSERT(0 == count || fx >= p1);
864 SkASSERT(0 == count || fx <= p0);
867 ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
869 SkASSERT(dstC <= endDstC);
872 SkTSwap(dither0, dither1);
877 void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
880 SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
883 fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
884 float fx = srcPt.x();
885 const float dx = fDstToIndex.getScaleX();
887 // Default our dither bias values to 1/2, (rounding), which is no dithering
888 float dither0 = 0.5f;
889 float dither1 = 0.5f;
891 const float ditherCell[] = {
895 const int rowIndex = (y & 1) << 1;
896 dither0 = ditherCell[rowIndex];
897 dither1 = ditherCell[rowIndex + 1];
899 SkTSwap(dither0, dither1);
902 const float dither[2] = { dither0, dither1 };
903 const float invDx = 1 / dx;
905 if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
906 const float pinFx = SkTPin(fx, 0.0f, 1.0f);
907 Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
908 if (fApplyAlphaAfterInterp) {
909 fill<true>(dstC, count, c + dither0, c + dither1);
911 fill<false>(dstC, count, c + dither0, c + dither1);
917 if (fApplyAlphaAfterInterp) {
918 this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
920 this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
923 if (fApplyAlphaAfterInterp) {
924 this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
926 this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);