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

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: fix win warnings 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 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);
+ }
+ }
+}
+
« 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