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 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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user