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>
This commit is contained in:
Herb Derby 2017-05-02 19:04:39 -04:00 committed by Skia Commit-Bot
parent 86d64a8bcc
commit 7eb86981a9
6 changed files with 3237 additions and 2399 deletions

View File

@ -97,6 +97,7 @@
M(save_xy) M(accumulate) \
M(linear_gradient) \
M(linear_gradient_2stops) \
M(xy_to_polar_unit) \
M(byte_tables) M(byte_tables_rgb) \
M(rgb_to_hsl) \
M(hsl_to_rgb)

View File

@ -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

View File

@ -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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -945,6 +945,32 @@ STAGE(linear_gradient_2stops) {
a = mad(t, c->f[3], c->b[3]);
}
STAGE(xy_to_polar_unit) {
F X = r,
Y = g;
F xabs = abs_(X),
yabs = abs_(Y);
F slope = min(xabs, yabs)/max(xabs, yabs);
F s = slope * slope;
// Use a 7th degree polynomial to approximate atan.
// This was generated using sollya.gforge.inria.fr.
// A float optimized polynomial was generated using the following command.
// P1 = fpminimax((1/(2*Pi))*atan(x),[|1,3,5,7|],[|24...|],[2^(-40),1],relative);
F phi = slope
* (0.15912117063999176025390625f + s
* (-5.185396969318389892578125e-2f + s
* (2.476101927459239959716796875e-2f + s
* (-7.0547382347285747528076171875e-3f))));
phi = if_then_else(xabs < yabs, 1.0f/4.0f - phi, phi);
phi = if_then_else(X < 0.0f , 1.0f/2.0f - phi, phi);
phi = if_then_else(Y < 0.0f , 1.0f - phi , phi);
phi = if_then_else(phi != phi , 0 , phi); // Check for NaN.
r = phi;
}
STAGE(save_xy) {
auto c = (SkJumper_SamplerCtx*)ctx;