start on 2pt conical gradients

Couple ways to do this masking, but since we basically
need the same for decal, seems easiest to send it up to
the common code and handle it all together.

Change-Id: Idf806d7feab12a9caf339febd30dd3a2432ec038
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/267244
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
Mike Klein 2020-01-28 16:11:34 -06:00 committed by Skia Commit-Bot
parent dcc8c5431b
commit caf5ee4451
10 changed files with 81 additions and 38 deletions

View File

@ -431,20 +431,32 @@ bool SkGradientShaderBase::onProgram(skvm::Builder* p,
inv.normalizePerspective();
SkShaderBase::ApplyMatrix(p, inv, &x,&y,uniforms);
skvm::F32 t;
if (!this->transformT(p,uniforms, x,y, &t)) { // Hook into subclasses for linear, radial, etc.
return false;
}
// Most tiling happens here, with kDecal doing its work at the end.
// Perhaps unexpectedly, all clamping is handled by our search, so
// we don't explicitly clamp t to [0,1]. That clamp would break
// hard stops right at 0 or 1 boundaries in kClamp mode.
// (kRepeat and kMirror always produce values in [0,1].)
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
// 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
// right at 0 or 1 boundaries in kClamp mode. (kRepeat and kMirror always
// produce values in [0,1].)
switch(fTileMode) {
case SkTileMode::kDecal: break;
case SkTileMode::kClamp: break;
case SkTileMode::kRepeat: t = p->sub(t, p->floor(t)); break;
case SkTileMode::kClamp:
break;
case SkTileMode::kDecal:
keep = p->bit_and(keep, p->eq(t, p->clamp(t, p->splat(0.0f), p->splat(1.0f))));
break;
case SkTileMode::kRepeat:
t = p->sub(t, p->floor(t));
break;
case SkTileMode::kMirror: {
// t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 |
// {-A-} {--------B-------}
@ -578,15 +590,10 @@ bool SkGradientShaderBase::onProgram(skvm::Builder* p,
p->premul(r,g,b,*a);
}
// Mask away any pixels that we tried to sample outside the bounds in kDecal.
if (fTileMode == SkTileMode::kDecal) {
skvm::I32 in_bounds = p->eq(t, p->clamp(t, p->splat(0.0f), p->splat(1.0f)));
*r = p->bit_cast(p->bit_and(in_bounds, p->bit_cast(*r)));
*g = p->bit_cast(p->bit_and(in_bounds, p->bit_cast(*g)));
*b = p->bit_cast(p->bit_and(in_bounds, p->bit_cast(*b)));
*a = p->bit_cast(p->bit_and(in_bounds, p->bit_cast(*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)));
return true;
}

View File

@ -89,8 +89,9 @@ protected:
virtual void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const = 0;
virtual bool transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const { return false; }
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;
template <typename T, typename... Args>
static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {

View File

@ -75,11 +75,12 @@ void SkLinearGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline*,
// No extra stage needed for linear gradients.
}
bool SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
SkGradientShaderBase::MaskNeeded
SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
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.
*t = x;
return true;
return MaskNeeded::None;
}
SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {

View File

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

View File

@ -63,10 +63,11 @@ void SkRadialGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline* p,
p->append(SkRasterPipeline::xy_to_radius);
}
bool SkRadialGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
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 true;
return MaskNeeded::None;
}
/////////////////////////////////////////////////////////////////////

View File

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

View File

@ -68,8 +68,9 @@ void SkSweepGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline
SkMatrix::MakeTrans(fTBias , 0)));
}
bool SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
SkGradientShaderBase::MaskNeeded
SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
skvm::F32 xabs = p->abs(x),
yabs = p->abs(y),
slope = p->div(p->min(xabs, yabs),
@ -101,7 +102,7 @@ bool SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
*t = p->mad(*t, p->uniformF(uniforms->pushF(fTScale))
, p->uniformF(uniforms->pushF(fTScale*fTBias)));
}
return true;
return MaskNeeded::None;
}
/////////////////////////////////////////////////////////////////////

View File

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

View File

@ -234,6 +234,34 @@ 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 {
// See https://skia.org/dev/design/conical, and onAppendStages() above.
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;
}
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;
}
return MaskNeeded::NotYetImplemented;
}
/////////////////////////////////////////////////////////////////////
#if SK_SUPPORT_GPU

View File

@ -69,6 +69,9 @@ 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;
private:
SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient)