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 mad(F32 x, F32 y, F32 z); // x*y+z, often an FMA
F32 sqrt(F32 x); F32 sqrt(F32 x);
F32 negate(F32 x) {
return sub(splat(0.0f), x);
}
F32 lerp(F32 lo, F32 hi, F32 t) { F32 lerp(F32 lo, F32 hi, F32 t) {
return mad(sub(hi,lo), t, lo); return mad(sub(hi,lo), t, lo);
} }
F32 clamp(F32 x, F32 lo, F32 hi) { F32 clamp(F32 x, F32 lo, F32 hi) {
return max(lo, min(x, 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 eq (F32 x, F32 y);
I32 neq(F32 x, F32 y); I32 neq(F32 x, F32 y);
@ -447,15 +459,6 @@ namespace skvm {
I32 round(F32 x); I32 round(F32 x);
I32 bit_cast(F32 x) { return {x.id}; } 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. // int math, comparisons, etc.
I32 add(I32 x, I32 y); I32 add(I32 x, I32 y);
I32 sub(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); SkShaderBase::ApplyMatrix(p, inv, &x,&y,uniforms);
skvm::I32 keep; skvm::I32 mask = p->splat(~0);
skvm::F32 t; skvm::F32 t = this->transformT(p,uniforms, x,y, &mask);
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
// Perhaps unexpectedly, clamping is handled naturally by our search, so we // 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 // 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; break;
case SkTileMode::kDecal: 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; break;
case SkTileMode::kRepeat: case SkTileMode::kRepeat:
@ -590,10 +584,10 @@ bool SkGradientShaderBase::onProgram(skvm::Builder* p,
p->premul(r,g,b,*a); p->premul(r,g,b,*a);
} }
*r = p->bit_cast(p->bit_and(keep, p->bit_cast(*r))); *r = p->bit_cast(p->bit_and(mask, p->bit_cast(*r)));
*g = p->bit_cast(p->bit_and(keep, p->bit_cast(*g))); *g = p->bit_cast(p->bit_and(mask, p->bit_cast(*g)));
*b = p->bit_cast(p->bit_and(keep, p->bit_cast(*b))); *b = p->bit_cast(p->bit_and(mask, p->bit_cast(*b)));
*a = p->bit_cast(p->bit_and(keep, p->bit_cast(*a))); *a = p->bit_cast(p->bit_and(mask, p->bit_cast(*a)));
return true; return true;
} }

View File

@ -89,9 +89,9 @@ protected:
virtual void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline, virtual void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const = 0; SkRasterPipeline* postPipeline) const = 0;
enum class MaskNeeded { None, NaNs, Degens, NotYetImplemented }; // Produce t from (x,y), modifying mask if it should be anything other than ~0.
virtual MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*, virtual skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const = 0; skvm::F32 x, skvm::F32 y, skvm::I32* mask) const = 0;
template <typename T, typename... Args> template <typename T, typename... Args>
static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... 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. // No extra stage needed for linear gradients.
} }
SkGradientShaderBase::MaskNeeded skvm::F32 SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*, skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
// We've baked getting t in x into the matrix, so this is pretty trivial. // We've baked getting t in x into the matrix, so this is pretty trivial.
*t = x; return x;
return MaskNeeded::None;
} }
SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {

View File

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

View File

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

View File

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

View File

@ -68,9 +68,9 @@ void SkSweepGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline
SkMatrix::MakeTrans(fTBias , 0))); SkMatrix::MakeTrans(fTBias , 0)));
} }
SkGradientShaderBase::MaskNeeded
SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms, skvm::F32 SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const { skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
skvm::F32 xabs = p->abs(x), skvm::F32 xabs = p->abs(x),
yabs = p->abs(y), yabs = p->abs(y),
slope = p->div(p->min(xabs, yabs), 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 (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); 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))); p->eq(phi, phi)));
if (fTScale != 1.0f || fTBias != 0.0f) { 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))); , p->uniformF(uniforms->pushF(fTScale*fTBias)));
} }
return MaskNeeded::None; return t;
} }
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////

View File

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

View File

@ -234,32 +234,55 @@ void SkTwoPointConicalGradient::appendGradientStages(SkArenaAlloc* alloc, SkRast
} }
} }
SkGradientShaderBase::MaskNeeded skvm::F32 SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms, skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
// See https://skia.org/dev/design/conical, and onAppendStages() above. // 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) { 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), float denom = 1.0f / (fRadius2 - fRadius1),
scale = SkTMax(fRadius1, fRadius2) * denom, scale = SkTMax(fRadius1, fRadius2) * denom,
bias = -fRadius1 * 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 ))); , p->uniformF(uniforms->pushF(bias )));
return MaskNeeded::None;
} }
if (fType == Type::kStrip) { if (fType == Type::kStrip) {
float r = fRadius1 / this->getCenterX1(); 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)))); 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, void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const override; SkRasterPipeline* postPipeline) const override;
MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*, skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const final; skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
private: private:
SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient) SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient)