Index: src/effects/gradients/SkLinearGradient.cpp |
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp |
index 11c79f0956248e0a56f891aacdab56c7f4186847..404207e7190268d17c7ecd0d59ad58c6a3c38911 100644 |
--- a/src/effects/gradients/SkLinearGradient.cpp |
+++ b/src/effects/gradients/SkLinearGradient.cpp |
@@ -100,6 +100,40 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext( |
fFlags |= SkShader::kConstInY16_Flag; |
} |
} |
+ |
+ // setup for Sk4f |
+ int count = shader.fColorCount; |
+ fColorCount = count; |
+ fColors4 = new Sk4f[count]; |
+ fPos = new float[count * 2]; |
+ fPosScale = fPos + count; |
+ if (shader.fOrigPos) { |
+ fPos[0] = 0; |
+ fPosScale[0] = 0; // should never get used |
+ for (int i = 1; i < count; ++i) { |
+ fPos[i] = SkTMin(SkTMax(fPos[i - 1], shader.fOrigPos[i]), 1.0f); |
+ fPosScale[i] = 1.0f / (fPos[i] - fPos[i - 1]); |
+ } |
+ fPos[count - 1] = 1; // overwrite the last value just to be sure we end at 1.0 |
+ } else { |
+ float invScale = 1.0f / (count - 1); |
+ for (int i = 0; i < count; ++i) { |
+ fPos[i] = i * invScale; |
+ fPosScale[i] = count - 1; |
+ } |
+ } |
+ for (int i = 0; i < count; ++i) { |
+ SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]); |
+ fColors4[i] = Sk4f::FromBytes((const uint8_t*)&pmc); |
+ if (i > 0) { |
+ SkASSERT(fPos[i - 1] <= fPos[i]); |
+ } |
+ } |
+} |
+ |
+SkLinearGradient::LinearGradientContext::~LinearGradientContext() { |
+ delete[] fPos; |
+ delete[] fColors4; |
} |
#define NO_CHECK_ITER \ |
@@ -211,6 +245,11 @@ void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx, |
void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, |
int count) { |
+ if (true) { |
+ this->shadeSpan4(x, y, dstC, count); |
+ return; |
+ } |
+ |
SkASSERT(count > 0); |
const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader); |
@@ -576,3 +615,111 @@ void SkLinearGradient::toString(SkString* str) const { |
str->append(")"); |
} |
#endif |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+#include "SkNx.h" |
+ |
+#if 0 |
+static Sk4f premul(const Sk4f& x) { |
+ return x * scale_rgb(x.kth<SK_A32_SHIFT/8>()); |
+} |
+ |
+static Sk4f unpremul(const Sk4f& x) { |
+ return x * scale_rgb(1 / x.kth<SK_A32_SHIFT/8>()); // TODO: fast/approx invert? |
+} |
+ |
+static Sk4f clamp_0_1(const Sk4f& x) { |
+ return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0)); |
+} |
+#endif |
+ |
+static SkPMColor round_from_255(const Sk4f& x) { |
+ SkPMColor c; |
+ (x + Sk4f(0.5f)).toBytes((uint8_t*)&c); |
+ return c; |
+} |
+ |
+static Sk4f lerp(Sk4f a, Sk4f b, float t) { |
+ return a + Sk4f(t) * (b - a); |
+} |
+ |
+static int find(float tiledX, const float pos[], int count) { |
+ SkASSERT(count > 1); |
+ SkASSERT(0 == pos[0]); |
+ SkASSERT(1 == pos[count - 1]); |
+ SkASSERT(tiledX >= 0 && tiledX <= 1); |
+ |
+ int i = 1; |
+ while (pos[i] < tiledX) { |
+ i += 1; |
+ } |
+ return i - 1; |
+} |
+ |
+/* |
+ * TODOs |
+ * |
+ * - tilemodes |
+ * - interp before or after premul |
+ * - apply paint's alpha (available when we create the context |
+ * - perspective |
+ * - dithering |
+ * - optimizations |
+ * - even spacing (how common is that?) |
+ * - use fixed (32bit or 16bit) instead of floats? |
+ */ |
+void SkLinearGradient::LinearGradientContext::shadeSpan4(int x, int y, SkPMColor* SK_RESTRICT dstC, |
+ int count) { |
+ SkASSERT(count > 0); |
+ |
+ const Sk4f* colors4 = fColors4; |
+ const float* pos = fPos; |
+ const float* posScale = fPosScale; |
+ |
+ SkPoint srcPt; |
+ SkMatrix::MapXYProc dstProc = fDstToIndexProc; |
+// int toggle = init_dither_toggle(x, y); |
+ |
+ float dx, fx; |
+ if (fDstToIndexClass != kPerspective_MatrixClass || true) { |
+ dstProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt); |
+ fx = srcPt.x(); |
+ |
+ if (fDstToIndexClass == kFixedStepInX_MatrixClass) { |
+ SkFixed dxStorage[1]; |
+ (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, nullptr); |
+ // todo: do we need a real/high-precision value for dx here? |
+ dx = SkFixedToFloat(dxStorage[0]); |
+ } else { |
+ SkASSERT(fDstToIndexClass == kLinear_MatrixClass); |
+ dx = fDstToIndex.getScaleX(); |
+ } |
+ } |
+ |
+ int prevIndex = 0; |
+ for (int i = 0; i < count; ++i) { |
+ float tiledX = fx; |
+ |
+ // Apply the TileMode |
+ if (tiledX < 0) { |
+ tiledX = 0; |
+ } |
+ if (tiledX > 1) { |
+ tiledX = 1; |
+ } |
+ |
+ // Are we in the same relative interval, or do we need to search |
+ if (pos[prevIndex] > tiledX || tiledX > pos[prevIndex + 1]) { |
f(malita)
2015/11/11 05:14:16
What if instead of projecting the span pixels onto
|
+ prevIndex = find(tiledX, pos, fColorCount); |
+ } |
+ |
+ // Compute the color |
+ float t = (fx - pos[prevIndex]) * posScale[prevIndex + 1]; |
+ Sk4f c = lerp(colors4[prevIndex], colors4[prevIndex + 1], t); |
+ dstC[i] = round_from_255(c); |
+ |
+ fx += dx; |
+ } |
+} |
+ |