Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(917)

Unified Diff: src/effects/gradients/SkLinearGradient.cpp

Issue 1436663003: Implement multi-color-stops in linear gradients using Sk4f (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/effects/gradients/SkLinearGradient.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
+ }
+ }
+}
+
« no previous file with comments | « src/effects/gradients/SkLinearGradient.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698