diff options
author | Herb Derby <herb@google.com> | 2017-05-02 19:04:39 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-05-03 16:45:07 +0000 |
commit | 7eb86981a954c500fa4a4d8425496a5beb789e5d (patch) | |
tree | 256a7d08f2f6d5ee8c2fd3eec369758dddf33d47 /src/effects | |
parent | 86d64a8bcc44eb9b7d6a0abe3a384ea69e10828b (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.cpp | 151 | ||||
-rw-r--r-- | src/effects/gradients/SkSweepGradient.h | 4 |
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; |