finish up 2pt conicals

In the end it turned out best to let the subclasses
modify the mask, rather than return how to do it.
This gave more flexibility about how to calcualte it.

Add negate(x), norm(x,y).

Change-Id: Ie17050037f0441becf06897fbe31587d6709009d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/267456
Auto-Submit: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
Mike Klein 2020-01-29 09:47:44 -06:00 committed by Skia Commit-Bot
parent 5df08e55e1
commit ce9e060b48
11 changed files with 82 additions and 68 deletions

View File

@ -427,13 +427,25 @@ namespace skvm {
F32 mad(F32 x, F32 y, F32 z); // x*y+z, often an FMA
F32 sqrt(F32 x);
F32 negate(F32 x) {
return sub(splat(0.0f), x);
}
F32 lerp(F32 lo, F32 hi, F32 t) {
return mad(sub(hi,lo), t, lo);
}
F32 clamp(F32 x, F32 lo, F32 hi) {
return max(lo, min(x, hi));
}
F32 abs(F32 x) {
return bit_cast(bit_and(bit_cast(x),
splat(0x7fffffff)));
}
F32 fract(F32 x) {
return sub(x, floor(x));
}
F32 norm(F32 x, F32 y) {
return sqrt(mad(x,x, mul(y,y)));
}
I32 eq (F32 x, F32 y);
I32 neq(F32 x, F32 y);
@ -447,15 +459,6 @@ namespace skvm {
I32 round(F32 x);
I32 bit_cast(F32 x) { return {x.id}; }
F32 abs(F32 x) {
return bit_cast(bit_and(bit_cast(x),
splat(0x7fffffff)));
}
F32 fract(F32 x) {
return sub(x, floor(x));
}
// int math, comparisons, etc.
I32 add(I32 x, I32 y);
I32 sub(I32 x, I32 y);

View File

@ -432,14 +432,8 @@ bool SkGradientShaderBase::onProgram(skvm::Builder* p,
SkShaderBase::ApplyMatrix(p, inv, &x,&y,uniforms);
skvm::I32 keep;
skvm::F32 t;
switch (this->transformT(p,uniforms, x,y, &t)) {
case MaskNeeded::None: keep = p->splat(~0); break;
case MaskNeeded::NaNs: keep = p->eq(t,t); break;
default: return false;
}
t = p->bit_cast(p->bit_and(keep, p->bit_cast(t))); // if (!keep) t = 0
skvm::I32 mask = p->splat(~0);
skvm::F32 t = this->transformT(p,uniforms, x,y, &mask);
// Perhaps unexpectedly, clamping is handled naturally by our search, so we
// don't explicitly clamp t to [0,1]. That clamp would break hard stops
@ -450,7 +444,7 @@ bool SkGradientShaderBase::onProgram(skvm::Builder* p,
break;
case SkTileMode::kDecal:
keep = p->bit_and(keep, p->eq(t, p->clamp(t, p->splat(0.0f), p->splat(1.0f))));
mask = p->bit_and(mask, p->eq(t, p->clamp(t, p->splat(0.0f), p->splat(1.0f))));
break;
case SkTileMode::kRepeat:
@ -590,10 +584,10 @@ bool SkGradientShaderBase::onProgram(skvm::Builder* p,
p->premul(r,g,b,*a);
}
*r = p->bit_cast(p->bit_and(keep, p->bit_cast(*r)));
*g = p->bit_cast(p->bit_and(keep, p->bit_cast(*g)));
*b = p->bit_cast(p->bit_and(keep, p->bit_cast(*b)));
*a = p->bit_cast(p->bit_and(keep, p->bit_cast(*a)));
*r = p->bit_cast(p->bit_and(mask, p->bit_cast(*r)));
*g = p->bit_cast(p->bit_and(mask, p->bit_cast(*g)));
*b = p->bit_cast(p->bit_and(mask, p->bit_cast(*b)));
*a = p->bit_cast(p->bit_and(mask, p->bit_cast(*a)));
return true;
}

View File

@ -89,9 +89,9 @@ protected:
virtual void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const = 0;
enum class MaskNeeded { None, NaNs, Degens, NotYetImplemented };
virtual MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const = 0;
// Produce t from (x,y), modifying mask if it should be anything other than ~0.
virtual skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const = 0;
template <typename T, typename... Args>
static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {

View File

@ -75,12 +75,10 @@ void SkLinearGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline*,
// No extra stage needed for linear gradients.
}
SkGradientShaderBase::MaskNeeded
SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
skvm::F32 SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
// We've baked getting t in x into the matrix, so this is pretty trivial.
*t = x;
return MaskNeeded::None;
return x;
}
SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {

View File

@ -29,9 +29,8 @@ protected:
void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const final;
MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const final;
skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
private:
SK_FLATTENABLE_HOOKS(SkLinearGradient)

View File

@ -63,11 +63,9 @@ void SkRadialGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline* p,
p->append(SkRasterPipeline::xy_to_radius);
}
SkGradientShaderBase::MaskNeeded
SkRadialGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
*t = p->sqrt(p->mad(x,x, p->mul(y,y)));
return MaskNeeded::None;
skvm::F32 SkRadialGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
return p->norm(x,y);
}
/////////////////////////////////////////////////////////////////////

View File

@ -26,8 +26,8 @@ protected:
void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const override;
MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const final;
skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
private:
SK_FLATTENABLE_HOOKS(SkRadialGradient)

View File

@ -68,9 +68,9 @@ void SkSweepGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline
SkMatrix::MakeTrans(fTBias , 0)));
}
SkGradientShaderBase::MaskNeeded
SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
skvm::F32 SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
skvm::F32 xabs = p->abs(x),
yabs = p->abs(y),
slope = p->div(p->min(xabs, yabs),
@ -95,14 +95,14 @@ SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
phi = p->select(p->lt (x, p->splat(0.0f)), p->sub(p->splat(1/2.0f), phi), phi);
phi = p->select(p->lt (y, p->splat(0.0f)), p->sub(p->splat(1/1.0f), phi), phi);
*t = p->bit_cast(p->bit_and(p->bit_cast(phi), // t = phi if phi != NaN
skvm::F32 t = p->bit_cast(p->bit_and(p->bit_cast(phi), // t = phi if phi != NaN
p->eq(phi, phi)));
if (fTScale != 1.0f || fTBias != 0.0f) {
*t = p->mad(*t, p->uniformF(uniforms->pushF(fTScale))
t = p->mad(t, p->uniformF(uniforms->pushF(fTScale))
, p->uniformF(uniforms->pushF(fTScale*fTBias)));
}
return MaskNeeded::None;
return t;
}
/////////////////////////////////////////////////////////////////////

View File

@ -30,9 +30,8 @@ protected:
void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const override;
MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const final;
skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
private:
SK_FLATTENABLE_HOOKS(SkSweepGradient)

View File

@ -234,32 +234,55 @@ void SkTwoPointConicalGradient::appendGradientStages(SkArenaAlloc* alloc, SkRast
}
}
SkGradientShaderBase::MaskNeeded
SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
skvm::F32 SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
// See https://skia.org/dev/design/conical, and onAppendStages() above.
// There's a lot going on here, and I'm not really sure what's independent
// or disjoint, what can be reordered, simplified, etc. Tweak carefully.
if (fType == Type::kRadial) {
// As if ordinary radial for [0,r2].
skvm::F32 r = p->sqrt(p->mad(x,x, p->mul(y,y)));
// Rescale to [r1,r2]
float denom = 1.0f / (fRadius2 - fRadius1),
scale = SkTMax(fRadius1, fRadius2) * denom,
bias = -fRadius1 * denom;
*t = p->mad(r, p->uniformF(uniforms->pushF(scale))
return p->mad(p->norm(x,y), p->uniformF(uniforms->pushF(scale))
, p->uniformF(uniforms->pushF(bias )));
return MaskNeeded::None;
}
if (fType == Type::kStrip) {
float r = fRadius1 / this->getCenterX1();
*t = p->add(x, p->sqrt(p->sub(p->splat(r*r),
skvm::F32 t = p->add(x, p->sqrt(p->sub(p->splat(r*r),
p->mul(y,y))));
return MaskNeeded::NaNs;
*mask = p->eq(t,t); // t != NaN
return t;
}
return MaskNeeded::NotYetImplemented;
const skvm::F32 invR1 = p->uniformF(uniforms->pushF(1 / fFocalData.fR1));
skvm::F32 t;
if (fFocalData.isFocalOnCircle()) {
t = p->mad(p->div(y,x),y,x); // (x^2 + y^2) / x ~~> x + y^2/x ~~> y/x * y + x
} else if (fFocalData.isWellBehaved()) {
t = p->sub(p->norm(x,y), p->mul(x, invR1));
} else {
skvm::F32 k = p->sqrt(p->sub(p->mul(x,x),
p->mul(y,y)));
if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
k = p->negate(k);
}
t = p->sub(k, p->mul(x, invR1));
}
if (!fFocalData.isWellBehaved()) {
// TODO: not sure why we consider t == 0 degenerate
*mask = p->gt(t, p->splat(0.0f)); // t > 0 and implicitly, t != NaN
}
const skvm::F32 focalX = p->uniformF(uniforms->pushF(fFocalData.fFocalX));
if (1 - fFocalData.fFocalX < 0) { t = p->negate(t); }
if (!fFocalData.isNativelyFocal()) { t = p->add(t, focalX); }
if (fFocalData.isSwapped()) { t = p->sub(p->splat(1.0f), t); }
return t;
}
/////////////////////////////////////////////////////////////////////

View File

@ -69,8 +69,8 @@ protected:
void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const override;
MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const final;
skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
private:
SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient)