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:
parent
5df08e55e1
commit
ce9e060b48
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
p->eq(phi, phi)));
|
||||
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))
|
||||
, p->uniformF(uniforms->pushF(fTScale*fTBias)));
|
||||
t = p->mad(t, p->uniformF(uniforms->pushF(fTScale))
|
||||
, p->uniformF(uniforms->pushF(fTScale*fTBias)));
|
||||
}
|
||||
return MaskNeeded::None;
|
||||
return t;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
, p->uniformF(uniforms->pushF(bias )));
|
||||
return MaskNeeded::None;
|
||||
return p->mad(p->norm(x,y), p->uniformF(uniforms->pushF(scale))
|
||||
, p->uniformF(uniforms->pushF(bias )));
|
||||
}
|
||||
|
||||
if (fType == Type::kStrip) {
|
||||
float r = fRadius1 / this->getCenterX1();
|
||||
*t = p->add(x, p->sqrt(p->sub(p->splat(r*r),
|
||||
p->mul(y,y))));
|
||||
return MaskNeeded::NaNs;
|
||||
skvm::F32 t = p->add(x, p->sqrt(p->sub(p->splat(r*r),
|
||||
p->mul(y,y))));
|
||||
|
||||
*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;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user