| Index: src/effects/gradients/SkLinearGradient.cpp
|
| diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
|
| index ada9d70b6a19af49520d1eaaebd9fad6678704f0..fdc016c0d2cf601714d556380f97b691e8610d88 100644
|
| --- a/src/effects/gradients/SkLinearGradient.cpp
|
| +++ b/src/effects/gradients/SkLinearGradient.cpp
|
| @@ -7,6 +7,8 @@
|
|
|
| #include "SkLinearGradient.h"
|
|
|
| +static const float kInv255Float = 1.0f / 255;
|
| +
|
| static inline int repeat_bits(int x, const int bits) {
|
| return x & ((1 << bits) - 1);
|
| }
|
| @@ -86,20 +88,86 @@ SkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void
|
| return new (storage) LinearGradientContext(*this, rec);
|
| }
|
|
|
| +// This swizzles SkColor into the same component order as SkPMColor, but does not actually
|
| +// "pre" multiply the color components.
|
| +//
|
| +// This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
|
| +// SkPMColor from the floats, without having to swizzle each time.
|
| +//
|
| +static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
|
| + return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
|
| +}
|
| +
|
| SkLinearGradient::LinearGradientContext::LinearGradientContext(
|
| - const SkLinearGradient& shader, const ContextRec& rec)
|
| - : INHERITED(shader, rec)
|
| + const SkLinearGradient& shader, const ContextRec& ctx)
|
| + : INHERITED(shader, ctx)
|
| {
|
| unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
|
| if ((fDstToIndex.getType() & ~mask) == 0) {
|
| // when we dither, we are (usually) not const-in-Y
|
| - if ((fFlags & SkShader::kHasSpan16_Flag) && !rec.fPaint->isDither()) {
|
| + if ((fFlags & SkShader::kHasSpan16_Flag) && !ctx.fPaint->isDither()) {
|
| // only claim this if we do have a 16bit mode (i.e. none of our
|
| // colors have alpha), and if we are not dithering (which obviously
|
| // is not const in Y).
|
| fFlags |= SkShader::kConstInY16_Flag;
|
| }
|
| }
|
| +
|
| + // setup for Sk4f
|
| + int count = shader.fColorCount;
|
| + fRecs.setCount(count);
|
| + Rec* rec = fRecs.begin();
|
| + if (shader.fOrigPos) {
|
| + rec[0].fPos = 0;
|
| + SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;) // should never get used
|
| + for (int i = 1; i < count; ++i) {
|
| + rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
|
| + rec[i].fPosScale = 1.0f / (rec[i].fPos - rec[i - 1].fPos);
|
| + }
|
| + rec[count - 1].fPos = 1; // overwrite the last value just to be sure we end at 1.0
|
| + } else {
|
| + // no pos specified, so we compute evenly spaced values
|
| + const float scale = float(count - 1);
|
| + float invScale = 1.0f / scale;
|
| + for (int i = 0; i < count; ++i) {
|
| + rec[i].fPos = i * invScale;
|
| + rec[i].fPosScale = scale;
|
| + }
|
| + }
|
| +
|
| + fApplyAlphaAfterInterp = true;
|
| + if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
|
| + shader.colorsAreOpaque())
|
| + {
|
| + fApplyAlphaAfterInterp = false;
|
| + }
|
| +
|
| + if (fApplyAlphaAfterInterp) {
|
| + // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
|
| + // interpolate in unpremultiplied space first, and then scale by alpha right before we
|
| + // convert to SkPMColor bytes.
|
| + const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
|
| + const Sk4f scale(1, 1, 1, paintAlpha);
|
| + for (int i = 0; i < count; ++i) {
|
| + uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
|
| + rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&c) * scale;
|
| + if (i > 0) {
|
| + SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
|
| + }
|
| + }
|
| + } else {
|
| + // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
|
| + // of converting the floats down to bytes.
|
| + unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
|
| + for (int i = 0; i < count; ++i) {
|
| + SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
|
| + pmc = SkAlphaMulQ(pmc, alphaScale);
|
| + rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&pmc);
|
| + if (i > 0) {
|
| + SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
| #define NO_CHECK_ITER \
|
| @@ -212,9 +280,17 @@ void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
|
| void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
|
| int count) {
|
| SkASSERT(count > 0);
|
| -
|
| const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
|
|
|
| +#ifndef SK_SUPPORT_LEGACY_LINEAR_GRADIENT_TABLE
|
| + if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
|
| + kLinear_MatrixClass == fDstToIndexClass)
|
| + {
|
| + this->shade4_clamp(x, y, dstC, count);
|
| + return;
|
| + }
|
| +#endif
|
| +
|
| SkPoint srcPt;
|
| SkMatrix::MapXYProc dstProc = fDstToIndexProc;
|
| TileProc proc = linearGradient.fTileProc;
|
| @@ -576,3 +652,264 @@ void SkLinearGradient::toString(SkString* str) const {
|
| str->append(")");
|
| }
|
| #endif
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +#include "SkNx.h"
|
| +
|
| +static const SkLinearGradient::LinearGradientContext::Rec*
|
| +find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
|
| + SkASSERT(tiledX >= 0 && tiledX <= 1);
|
| +
|
| + SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
|
| + SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
|
| + SkASSERT(rec[0].fPos <= rec[1].fPos);
|
| + rec += 1;
|
| + while (rec->fPos < tiledX) {
|
| + SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
|
| + SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
|
| + SkASSERT(rec[0].fPos <= rec[1].fPos);
|
| + rec += 1;
|
| + }
|
| + return rec - 1;
|
| +}
|
| +
|
| +static const SkLinearGradient::LinearGradientContext::Rec*
|
| +find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
|
| + SkASSERT(tiledX >= 0 && tiledX <= 1);
|
| +
|
| + SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
|
| + SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
|
| + SkASSERT(rec[0].fPos <= rec[1].fPos);
|
| + while (tiledX < rec->fPos) {
|
| + rec -= 1;
|
| + SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
|
| + SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
|
| + SkASSERT(rec[0].fPos <= rec[1].fPos);
|
| + }
|
| + return rec;
|
| +}
|
| +
|
| +template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x) {
|
| + SkPMColor c;
|
| + x.toBytes((uint8_t*)&c);
|
| + if (apply_alpha) {
|
| + c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c),
|
| + SkGetPackedG32(c), SkGetPackedB32(c));
|
| + }
|
| + return c;
|
| +}
|
| +
|
| +template <bool apply_alpha> void fill(SkPMColor dst[], int count,
|
| + const Sk4f& c4, const Sk4f& c4other) {
|
| + sk_memset32_dither(dst, trunc_from_255<apply_alpha>(c4),
|
| + trunc_from_255<apply_alpha>(c4other), count);
|
| +}
|
| +
|
| +template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
|
| + // Assumes that c4 does not need to be dithered.
|
| + sk_memset32(dst, trunc_from_255<apply_alpha>(c4), count);
|
| +}
|
| +
|
| +/*
|
| + * TODOs
|
| + *
|
| + * - tilemodes
|
| + * - interp before or after premul
|
| + * - perspective
|
| + * - optimizations
|
| + * - use fixed (32bit or 16bit) instead of floats?
|
| + */
|
| +
|
| +static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
|
| + const float p0 = rec[0].fPos;
|
| + const Sk4f c0 = rec[0].fColor;
|
| + const Sk4f c1 = rec[1].fColor;
|
| + const Sk4f diffc = c1 - c0;
|
| + const float scale = rec[1].fPosScale;
|
| + const float t = (fx - p0) * scale;
|
| + return c0 + Sk4f(t) * diffc;
|
| +}
|
| +
|
| +template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
|
| + const Sk4f& dither0, const Sk4f& dither1) {
|
| + Sk4f dc2 = dc + dc;
|
| + Sk4f dc4 = dc2 + dc2;
|
| + Sk4f cd0 = c + dither0;
|
| + Sk4f cd1 = c + dc + dither1;
|
| + Sk4f cd2 = cd0 + dc2;
|
| + Sk4f cd3 = cd1 + dc2;
|
| + while (n >= 4) {
|
| + *dstC++ = trunc_from_255<apply_alpha>(cd0);
|
| + *dstC++ = trunc_from_255<apply_alpha>(cd1);
|
| + *dstC++ = trunc_from_255<apply_alpha>(cd2);
|
| + *dstC++ = trunc_from_255<apply_alpha>(cd3);
|
| + cd0 = cd0 + dc4;
|
| + cd1 = cd1 + dc4;
|
| + cd2 = cd2 + dc4;
|
| + cd3 = cd3 + dc4;
|
| + n -= 4;
|
| + }
|
| + if (n & 2) {
|
| + *dstC++ = trunc_from_255<apply_alpha>(cd0);
|
| + *dstC++ = trunc_from_255<apply_alpha>(cd1);
|
| + cd0 = cd0 + dc2;
|
| + }
|
| + if (n & 1) {
|
| + *dstC++ = trunc_from_255<apply_alpha>(cd0);
|
| + }
|
| +}
|
| +
|
| +template <bool apply_alpha, bool dx_is_pos>
|
| +void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
|
| + float fx, float dx, float invDx,
|
| + const float dither[2]) {
|
| + Sk4f dither0(dither[0]);
|
| + Sk4f dither1(dither[1]);
|
| + const Rec* rec = fRecs.begin();
|
| +
|
| + const Sk4f dx4 = Sk4f(dx);
|
| + SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
|
| +
|
| + if (dx_is_pos) {
|
| + if (fx < 0) {
|
| + int n = SkTMin(SkFloatToIntFloor(-fx * invDx) + 1, count);
|
| + fill<apply_alpha>(dstC, n, rec[0].fColor);
|
| + count -= n;
|
| + dstC += n;
|
| + fx += n * dx;
|
| + SkASSERT(0 == count || fx >= 0);
|
| + if (n & 1) {
|
| + SkTSwap(dither0, dither1);
|
| + }
|
| + }
|
| + } else { // dx < 0
|
| + if (fx > 1) {
|
| + int n = SkTMin(SkFloatToIntFloor((1 - fx) * invDx) + 1, count);
|
| + fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
|
| + count -= n;
|
| + dstC += n;
|
| + fx += n * dx;
|
| + SkASSERT(0 == count || fx <= 1);
|
| + if (n & 1) {
|
| + SkTSwap(dither0, dither1);
|
| + }
|
| + }
|
| + }
|
| + SkASSERT(count >= 0);
|
| +
|
| + const Rec* r;
|
| + if (dx_is_pos) {
|
| + r = fRecs.begin(); // start at the beginning
|
| + } else {
|
| + r = fRecs.begin() + fRecs.count() - 2; // start at the end
|
| + }
|
| +
|
| + while (count > 0) {
|
| + if (dx_is_pos) {
|
| + if (fx >= 1) {
|
| + fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
|
| + return;
|
| + }
|
| + } else { // dx < 0
|
| + if (fx <= 0) {
|
| + fill<apply_alpha>(dstC, count, rec[0].fColor);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + if (dx_is_pos) {
|
| + r = find_forward(r, fx);
|
| + } else {
|
| + r = find_backward(r, fx);
|
| + }
|
| + SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
|
| +
|
| + const float p0 = r[0].fPos;
|
| + const Sk4f c0 = r[0].fColor;
|
| + const float p1 = r[1].fPos;
|
| + const Sk4f diffc = Sk4f(r[1].fColor) - c0;
|
| + const float scale = r[1].fPosScale;
|
| + const float t = (fx - p0) * scale;
|
| + const Sk4f c = c0 + Sk4f(t) * diffc;
|
| + const Sk4f dc = diffc * dx4 * Sk4f(scale);
|
| +
|
| + int n;
|
| + if (dx_is_pos) {
|
| + n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
|
| + } else {
|
| + n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
|
| + }
|
| +
|
| + fx += n * dx;
|
| + count -= n;
|
| + SkASSERT(count >= 0);
|
| + if (dx_is_pos) {
|
| + SkASSERT(0 == count || fx >= p1);
|
| + } else {
|
| + SkASSERT(0 == count || fx <= p0);
|
| + }
|
| +
|
| + ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
|
| + dstC += n;
|
| + SkASSERT(dstC <= endDstC);
|
| +
|
| + if (n & 1) {
|
| + SkTSwap(dither0, dither1);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
|
| + int count) {
|
| + SkASSERT(count > 0);
|
| + SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
|
| +
|
| + SkPoint srcPt;
|
| + fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
|
| + float fx = srcPt.x();
|
| + const float dx = fDstToIndex.getScaleX();
|
| +
|
| + // Default our dither bias values to 1/2, (rounding), which is no dithering
|
| + float dither0 = 0.5f;
|
| + float dither1 = 0.5f;
|
| + if (fDither) {
|
| + const float ditherCell[] = {
|
| + 1/8.0f, 5/8.0f,
|
| + 7/8.0f, 3/8.0f,
|
| + };
|
| + const int rowIndex = (y & 1) << 1;
|
| + dither0 = ditherCell[rowIndex];
|
| + dither1 = ditherCell[rowIndex + 1];
|
| + if (x & 1) {
|
| + SkTSwap(dither0, dither1);
|
| + }
|
| + }
|
| + const float dither[2] = { dither0, dither1 };
|
| + const float invDx = 1 / dx;
|
| +
|
| + if (!SkScalarIsFinite(invDx)) { // dx is effectively zero, gradient is vertical
|
| + Sk4f c = lerp_color(fx, find_forward(fRecs.begin(), SkTPin(fx, 0.0f, 1.0f)));
|
| + if (fApplyAlphaAfterInterp) {
|
| + fill<true>(dstC, count, c + dither0, c + dither1);
|
| + } else {
|
| + fill<false>(dstC, count, c + dither0, c + dither1);
|
| + }
|
| + return;
|
| + }
|
| +
|
| + if (dx > 0) {
|
| + if (fApplyAlphaAfterInterp) {
|
| + this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
|
| + } else {
|
| + this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
|
| + }
|
| + } else {
|
| + if (fApplyAlphaAfterInterp) {
|
| + this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
|
| + } else {
|
| + this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
|
| + }
|
| + }
|
| +}
|
| +
|
|
|