Chromium Code Reviews| Index: src/effects/gradients/SkLinearGradient.cpp |
| diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp |
| index 11c79f0956248e0a56f891aacdab56c7f4186847..37c88dd057a9e4c2913ada52d8f94ca0f874bd98 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; |
|
caryclark
2015/11/16 14:44:29
This is a popular pattern:
src/effects/SkColorMat
|
| + |
| static inline int repeat_bits(int x, const int bits) { |
| return x & ((1 << bits) - 1); |
| } |
| @@ -86,20 +88,73 @@ SkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void |
| return new (storage) LinearGradientContext(*this, rec); |
| } |
| +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; |
| + rec[0].fPosScale = 0; // should never get used |
|
caryclark
2015/11/16 14:44:29
SkDEBUGCODE() ?
Assign SK_ScalarNaN instead?
reed1
2015/11/16 16:43:33
Done.
|
| + for (int i = 1; i < count; ++i) { |
| + rec[i].fPos = SkTMin(SkTMax(rec[i - 1].fPos, shader.fOrigPos[i]), 1.0f); |
|
caryclark
2015/11/16 14:44:29
SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
(see com
|
| + 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 |
|
caryclark
2015/11/16 14:44:29
This seems odd -- is it OK for the last iteration
caryclark
2015/11/16 14:44:29
SkASSERT(rec[count - 2].fPos <= rec[count - 1].fPo
|
| + } else { |
| + float invScale = 1.0f / (count - 1); |
| + for (int i = 0; i < count; ++i) { |
| + rec[i].fPos = i * invScale; |
| + rec[i].fPosScale = count - 1; |
| + } |
| + } |
| + |
| + fApplyAlphaAfterInterp = true; |
| + if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) || |
| + shader.colorsAreOpaque()) |
| + { |
| + fApplyAlphaAfterInterp = false; |
| + } |
|
caryclark
2015/11/16 14:44:29
fApplyAlphaAfterInterp = !(shader.getGradFlags() &
|
| + |
| + if (fApplyAlphaAfterInterp) { |
| + 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); |
|
caryclark
2015/11/16 14:44:29
this assert looks like it would be more at home ab
|
| + } |
| + } |
| + } else { |
| + 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); |
|
caryclark
2015/11/16 14:44:29
Ditto
|
| + } |
| + } |
| + } |
| } |
| #define NO_CHECK_ITER \ |
| @@ -212,9 +267,18 @@ 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 && |
| + linearGradient.fColorCount > 2) |
| + { |
| + this->shade4_clamp(x, y, dstC, count); |
| + return; |
| + } |
| +#endif |
| + |
| SkPoint srcPt; |
| SkMatrix::MapXYProc dstProc = fDstToIndexProc; |
| TileProc proc = linearGradient.fTileProc; |
| @@ -576,3 +640,252 @@ void SkLinearGradient::toString(SkString* str) const { |
| str->append(")"); |
| } |
| #endif |
| + |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| +#include "SkNx.h" |
| + |
| +static const SkLinearGradient::LinearGradientContext::Rec* find(float tiledX, |
| + const SkLinearGradient::LinearGradientContext::Rec rec[], |
| + int count) { |
| + SkASSERT(count > 1); |
| + SkASSERT(0 == rec[0].fPos); |
| + SkASSERT(1 == rec[count - 1].fPos); |
| + SkASSERT(tiledX >= 0 && tiledX <= 1); |
| + |
| + int i = 1; |
| + while (rec[i].fPos < tiledX) { |
| + i += 1; |
| + } |
|
caryclark
2015/11/16 14:44:29
Is this array ever so large that it's worth a non-
reed1
2015/11/16 16:43:33
Could be (though my guess is it is usually between
|
| + return &rec[i - 1]; |
| +} |
| + |
| +template <bool apply_alpha> SkPMColor trunc_from_255(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, Sk4f c4, Sk4f c4other) { |
| + sk_memset32_dither(dst, trunc_from_255<apply_alpha>(c4), |
| + trunc_from_255<apply_alpha>(c4other), count); |
| +} |
| + |
| +/* |
| + * TODOs |
| + * |
| + * - tilemodes |
| + * - interp before or after premul |
| + * - perspective |
| + * - optimizations |
|
f(malita)
2015/11/16 15:16:20
How does current perf compare to existing impl?
reed1
2015/11/16 16:43:33
Slower, by 10-20% depending.
|
| + * - 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, Sk4f c, Sk4f dc, |
| + Sk4f dither0, 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); |
|
caryclark
2015/11/16 14:44:29
probably not worth it, but
if (n >= 4) {
do {
reed1
2015/11/16 16:43:33
Good observation. But it took me a full minute to
|
| + 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; |
|
caryclark
2015/11/16 14:44:29
move this into the condition below?
|
| + } |
| + if (n & 1) { |
| + *dstC++ = trunc_from_255<apply_alpha>(cd0); |
| + } |
| +} |
| + |
| +template <bool apply_alpha> void SkLinearGradient::LinearGradientContext::shade4_pos_clamp( |
| + SkPMColor dstC[], int count, |
| + float fx, float dx, float invDx, |
| + Sk4f dither0, Sk4f dither1) { |
| + const Rec* rec = fRecs.begin(); |
| + |
| + const Sk4f dx4 = Sk4f(dx); |
| + SkDEBUGCODE(SkPMColor* endDstC = dstC + count;) |
| + |
| + if (fx < 0) { |
| + int n = SkTMin(SkFloatToIntFloor(-fx * invDx) + 1, count); |
| + fill<apply_alpha>(dstC, n, rec[0].fColor + dither0, rec[0].fColor + dither1); |
| + count -= n; |
| + SkASSERT(count >= 0); |
| + dstC += n; |
| + fx += n * dx; |
| + SkASSERT(0 == count || fx >= 0); |
| + if (n & 1) { |
| + SkTSwap(dither0, dither1); |
| + } |
| + } |
| + |
| + while (count > 0) { |
| + if (fx >= 1) { |
| + Sk4f color = rec[fRecs.count() - 1].fColor; |
| + fill<apply_alpha>(dstC, count, color + dither0, color + dither1); |
| + return; |
| + } |
| + |
| + const Rec* r = find(fx, rec, fRecs.count()); |
|
caryclark
2015/11/16 14:44:29
add
const float p0 = r[0].fPos;
to mirr
f(malita)
2015/11/16 15:16:20
Since we're peeking at r[1] below, I would SkASSER
reed1
2015/11/16 17:31:24
Done.
reed1
2015/11/16 17:31:24
Done.
|
| + const Sk4f c0 = r[0].fColor; |
| + const Sk4f diffc = r[1].fColor - c0; |
| + const float scale = r[1].fPosScale; |
| + const float t = (fx - r[0].fPos) * scale; |
|
caryclark
2015/11/16 14:44:29
const float t = (fx - p0) * scale;
|
| + Sk4f c = c0 + Sk4f(t) * diffc; |
| + |
| + const Sk4f dc = diffc * dx4 * Sk4f(scale); |
| + const float p1 = r[1].fPos; |
| + |
| + int n = SkTMin((int)((p1-fx)*invDx) + 1, count); |
|
caryclark
2015/11/16 14:44:29
Is this the same as the SkFloatToIntFloor in the <
|
| + |
| + fx += n * dx; |
| + count -= n; |
| + SkASSERT(0 == count || fx >= p1); |
| + SkASSERT(count >= 0); |
|
caryclark
2015/11/16 14:44:29
may be worth changing either the <0 condition or t
|
| + |
| + ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1); |
| + dstC += n; |
| + SkASSERT(dstC <= endDstC); |
| + |
| + if (n & 1) { |
| + SkTSwap(dither0, dither1); |
| + } |
| + } |
| +} |
| + |
| +template <bool apply_alpha>void SkLinearGradient::LinearGradientContext::shade4_neg_clamp( |
|
f(malita)
2015/11/16 15:16:20
shade4_neg_clamp & shade4_pos_clamp look pretty si
|
| + SkPMColor dstC[], int count, |
| + float fx, float dx, float invDx, |
| + Sk4f dither0, Sk4f dither1) { |
| + const Rec* rec = fRecs.begin(); |
| + |
| + const Sk4f dx4 = Sk4f(dx); |
| + SkDEBUGCODE(SkPMColor* endDstC = dstC + count;) |
| + |
| + if (fx > 1) { |
| + int n = SkTMin(SkFloatToIntFloor((1 - fx) * invDx) + 1, count); |
| + Sk4f c = rec[fRecs.count() - 1].fColor; |
| + fill<apply_alpha>(dstC, n, c + dither0, c + dither1); |
| + count -= n; |
| + SkASSERT(count >= 0); |
| + dstC += n; |
| + fx += n * dx; |
| + SkASSERT(0 == count || fx <= 1); |
| + if (n & 1) { |
| + SkTSwap(dither0, dither1); |
| + } |
| + } |
| + |
| + while (count > 0) { |
| + if (fx <= 0) { |
| + fill<apply_alpha>(dstC, count, rec[0].fColor + dither0, rec[0].fColor + dither1); |
|
caryclark
2015/11/16 14:44:29
maybe
Sk4f color = rec[0].fColor;
reed1
2015/11/16 16:43:33
Done.
|
| + return; |
| + } |
| + |
| + const Rec* r = find(fx, rec, fRecs.count()); |
| + const float p0 = r[0].fPos; |
| + const Sk4f c0 = r[0].fColor; |
| + const Sk4f diffc = r[1].fColor - c0; |
| + const float scale = r[1].fPosScale; |
| + const float t = (fx - p0) * scale; |
| + Sk4f c = c0 + Sk4f(t) * diffc; |
| + |
| + const Sk4f dc = diffc * dx4 * Sk4f(scale); |
| + |
| + int n = SkTMin((int)((p0 - fx)*invDx) + 1, count); |
| + |
| + fx += n * dx; |
| + count -= n; |
| + SkASSERT(0 == count || fx <= p0); |
| + SkASSERT(count >= 0); |
| + |
| + 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(); |
| + |
| + Sk4f dither0, dither1; |
| + if (fDither) { |
| + // [ 1/8 5/8 ] |
| + // [ 7/8 3/8 ] |
| + const float ditherCell[] = { |
| + 0.125f, 0.625f, |
| + 0.875f, 0.375f, |
| + }; |
|
caryclark
2015/11/16 14:44:29
Out of curiosity, could this be
1.0
reed1
2015/11/16 16:43:33
Very good idea. Done.
|
| + const int rowIndex = (y & 1) << 1; |
| + dither0 = Sk4f(ditherCell[rowIndex]); |
| + dither1 = Sk4f(ditherCell[rowIndex + 1]); |
| + } else { |
| + dither0 = dither1 = Sk4f(0.5f); |
| + } |
| + |
| + const float invDx = 1 / dx; |
| + |
| + if (!SkScalarIsFinite(invDx)) { // dx is effectively zero, gradient is vertical |
| + fx = SkTMin(SkTMax(fx, 0.0f), 1.0f); |
|
caryclark
2015/11/16 14:44:28
SkTPin(fx, 0.0f, 1.0f)
reed1
2015/11/16 16:43:33
Done.
|
| + Sk4f c = lerp_color(fx, find(fx, fRecs.begin(), fRecs.count())); |
| + 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_pos_clamp<true>(dstC, count, fx, dx, invDx, dither0, dither1); |
| + } else { |
| + this->shade4_pos_clamp<false>(dstC, count, fx, dx, invDx, dither0, dither1); |
| + } |
| + } else { |
| + if (fApplyAlphaAfterInterp) { |
| + this->shade4_neg_clamp<true>(dstC, count, fx, dx, invDx, dither0, dither1); |
| + } else { |
| + this->shade4_neg_clamp<false>(dstC, count, fx, dx, invDx, dither0, dither1); |
| + } |
| + } |
| +} |
| + |