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

View File

@ -89,8 +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;
virtual bool transformT(skvm::Builder*, skvm::Uniforms*, enum class MaskNeeded { None, NaNs, Degens, NotYetImplemented };
skvm::F32 x, skvm::F32 y, skvm::F32* t) const { return false; } virtual MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) 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,11 +75,12 @@ void SkLinearGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline*,
// No extra stage needed for linear gradients. // No extra stage needed for linear gradients.
} }
bool SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*, SkGradientShaderBase::MaskNeeded
skvm::F32 x, skvm::F32 y, skvm::F32* t) const { 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. // We've baked getting t in x into the matrix, so this is pretty trivial.
*t = x; *t = x;
return true; return MaskNeeded::None;
} }
SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {

View File

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

View File

@ -63,10 +63,11 @@ void SkRadialGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline* p,
p->append(SkRasterPipeline::xy_to_radius); p->append(SkRasterPipeline::xy_to_radius);
} }
bool SkRadialGradient::transformT(skvm::Builder* p, skvm::Uniforms*, SkGradientShaderBase::MaskNeeded
skvm::F32 x, skvm::F32 y, skvm::F32* t) const { 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))); *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, void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const override; SkRasterPipeline* postPipeline) const override;
bool transformT(skvm::Builder*, skvm::Uniforms*, MaskNeeded transformT(skvm::Builder*, skvm::Uniforms*,
skvm::F32 x, skvm::F32 y, skvm::F32* t) const final; skvm::F32 x, skvm::F32 y, skvm::F32* t) const final;
private: private:
SK_FLATTENABLE_HOOKS(SkRadialGradient) SK_FLATTENABLE_HOOKS(SkRadialGradient)

View File

@ -68,8 +68,9 @@ void SkSweepGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline
SkMatrix::MakeTrans(fTBias , 0))); SkMatrix::MakeTrans(fTBias , 0)));
} }
bool SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms, SkGradientShaderBase::MaskNeeded
skvm::F32 x, skvm::F32 y, skvm::F32* t) const { SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y, skvm::F32* t) 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),
@ -101,7 +102,7 @@ bool SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
*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 true; return MaskNeeded::None;
} }
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////

View File

@ -29,8 +29,9 @@ protected:
void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline, void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const override; 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: private:
SK_FLATTENABLE_HOOKS(SkSweepGradient) 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 #if SK_SUPPORT_GPU

View File

@ -69,6 +69,9 @@ 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 x, skvm::F32 y, skvm::F32* t) const final;
private: private:
SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient) SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient)