aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/effects
diff options
context:
space:
mode:
authorGravatar Herb Derby <herb@google.com>2017-05-02 19:04:39 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-05-03 16:45:07 +0000
commit7eb86981a954c500fa4a4d8425496a5beb789e5d (patch)
tree256a7d08f2f6d5ee8c2fd3eec369758dddf33d47 /src/effects
parent86d64a8bcc44eb9b7d6a0abe3a384ea69e10828b (diff)
Add sweep gradient to SkRasterPipeline
This is a prototype. I have remove the tiling, but maybe I should add it back in. I thinking about factoring out the common code with linear gradient in its own CL. I think radial gradient will be very close to this. Change-Id: I1dfcb4f944138ee623afdf10b2a8befde797c604 Reviewed-on: https://skia-review.googlesource.com/13766 Reviewed-by: Mike Klein <mtklein@chromium.org> Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Herb Derby <herb@google.com>
Diffstat (limited to 'src/effects')
-rw-r--r--src/effects/gradients/SkSweepGradient.cpp151
-rw-r--r--src/effects/gradients/SkSweepGradient.h4
2 files changed, 155 insertions, 0 deletions
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
index 23f77b3ab4..81fdfe481b 100644
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -11,6 +11,9 @@
#include <algorithm>
#include <cmath>
+#include "SkPM4fPriv.h"
+#include "SkRasterPipeline.h"
+
static SkMatrix translate(SkScalar dx, SkScalar dy) {
SkMatrix matrix;
matrix.setTranslate(dx, dy);
@@ -307,4 +310,152 @@ void SkSweepGradient::toString(SkString* str) const {
str->append(")");
}
+
+bool SkSweepGradient::onAppendStages(SkRasterPipeline* p,
+ SkColorSpace* dstCS,
+ SkArenaAlloc* alloc,
+ const SkMatrix& ctm,
+ const SkPaint& paint,
+ const SkMatrix* localM) const {
+ // Local matrix not supported currently. Remove once we have a generic RP wrapper.
+ if (localM || !getLocalMatrix().isIdentity()) {
+ return false;
+ }
+
+ SkMatrix dstToSrc;
+ if (!ctm.invert(&dstToSrc)) {
+ return false;
+ }
+
+ const auto dstToCenter = SkMatrix::Concat(
+ SkMatrix::MakeTrans(-fCenter.fX, -fCenter.fY), dstToSrc);
+
+ auto* m = alloc->makeArrayDefault<float>(9);
+ if (dstToCenter.asAffine(m)) {
+ // TODO: mapping y is not needed; split the matrix stages to save some math?
+ p->append(SkRasterPipeline::matrix_2x3, m);
+ } else {
+ dstToCenter.get9(m);
+ p->append(SkRasterPipeline::matrix_perspective, m);
+ }
+
+ p->append(SkRasterPipeline::xy_to_polar_unit);
+
+ const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
+ auto prepareColor = [premulGrad, dstCS, this](int i) {
+ SkColor4f c = dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS)
+ : SkColor4f_from_SkColor(fOrigColors[i], nullptr);
+ return premulGrad ? c.premul()
+ : SkPM4f::From4f(Sk4f::Load(&c));
+ };
+
+ // The two-stop case with stops at 0 and 1.
+ if (fColorCount == 2 && fOrigPos == nullptr) {
+ const SkPM4f c_l = prepareColor(0),
+ c_r = prepareColor(1);
+
+ // See F and B below.
+ auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
+ f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
+ f_and_b[1] = c_l;
+
+ p->append(SkRasterPipeline::linear_gradient_2stops, f_and_b);
+ } else {
+
+ struct Stop { float t; SkPM4f f, b; };
+ struct Ctx { size_t n; Stop* stops; SkPM4f start; };
+
+ auto* ctx = alloc->make<Ctx>();
+ ctx->start = prepareColor(0);
+
+ // For each stop we calculate a bias B and a scale factor F, such that
+ // for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
+ auto init_stop = [](float t_l, float t_r, SkPM4f c_l, SkPM4f c_r, Stop *stop) {
+ auto F = SkPM4f::From4f((c_r.to4f() - c_l.to4f()) / (t_r - t_l));
+ auto B = SkPM4f::From4f(c_l.to4f() - (F.to4f() * t_l));
+ *stop = {t_l, F, B};
+ };
+
+ if (fOrigPos == nullptr) {
+ // Handle evenly distributed stops.
+
+ float dt = 1.0f / (fColorCount - 1);
+ // In the evenly distributed case, fColorCount is the number of stops. There are no
+ // dummy entries.
+ auto* stopsArray = alloc->makeArrayDefault<Stop>(fColorCount);
+
+ float t_l = 0;
+ SkPM4f c_l = ctx->start;
+ for (int i = 0; i < fColorCount - 1; i++) {
+ // Use multiply instead of accumulating error using repeated addition.
+ float t_r = (i + 1) * dt;
+ SkPM4f c_r = prepareColor(i + 1);
+ init_stop(t_l, t_r, c_l, c_r, &stopsArray[i]);
+
+ t_l = t_r;
+ c_l = c_r;
+ }
+
+ // Force the last stop.
+ stopsArray[fColorCount - 1].t = 1;
+ stopsArray[fColorCount - 1].f = SkPM4f::From4f(Sk4f{0});
+ stopsArray[fColorCount - 1].b = prepareColor(fColorCount - 1);
+
+ ctx->n = fColorCount;
+ ctx->stops = stopsArray;
+ } else {
+ // Handle arbitrary stops.
+
+ // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
+ // because they are naturally handled by the search method.
+ int firstStop;
+ int lastStop;
+ if (fColorCount > 2) {
+ firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
+ lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
+ ? fColorCount - 1 : fColorCount - 2;
+ } else {
+ firstStop = 0;
+ lastStop = 1;
+ }
+ int realCount = lastStop - firstStop + 1;
+
+ // This is the maximum number of stops. There may be fewer stops because the duplicate
+ // points of hard stops are removed.
+ auto* stopsArray = alloc->makeArrayDefault<Stop>(realCount);
+
+ size_t stopCount = 0;
+ float t_l = fOrigPos[firstStop];
+ SkPM4f c_l = prepareColor(firstStop);
+ // N.B. lastStop is the index of the last stop, not one after.
+ for (int i = firstStop; i < lastStop; i++) {
+ float t_r = fOrigPos[i + 1];
+ SkPM4f c_r = prepareColor(i + 1);
+ if (t_l < t_r) {
+ init_stop(t_l, t_r, c_l, c_r, &stopsArray[stopCount]);
+ stopCount += 1;
+ }
+ t_l = t_r;
+ c_l = c_r;
+ }
+
+ stopsArray[stopCount].t = fOrigPos[lastStop];
+ stopsArray[stopCount].f = SkPM4f::From4f(Sk4f{0});
+ stopsArray[stopCount].b = prepareColor(lastStop);
+ stopCount += 1;
+
+ ctx->n = stopCount;
+ ctx->stops = stopsArray;
+ }
+
+ p->append(SkRasterPipeline::linear_gradient, ctx);
+ }
+
+ if (!premulGrad && !this->colorsAreOpaque()) {
+ p->append(SkRasterPipeline::premul);
+ }
+
+ return true;
+}
+
#endif
diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h
index 331e22d7b3..45c4233d88 100644
--- a/src/effects/gradients/SkSweepGradient.h
+++ b/src/effects/gradients/SkSweepGradient.h
@@ -38,6 +38,10 @@ protected:
Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+ bool onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* space, SkArenaAlloc* alloc,
+ const SkMatrix& matrix, const SkPaint& paint,
+ const SkMatrix* localM) const override;
+
private:
const SkPoint fCenter;