2pt conical stage for focal-pt-on-edge case

When the focal point is on the edge of the end circle, the quadratic
equation devolves to linear.  Add a stage to handle this case.

As a complication, this case can produce "degenerate" values:

 1) t == NaN
 2) R(t) < 0

For these, we're supposed to draw transparent black - which means
overwriting the color from the gradient stage.  To support this, build
a 0/1 vector mask in the context, and apply it post-gradient-stage.

Change-Id: Ice4e3243abfd8c784bb810f6c310aed7a4ac7dc8
Reviewed-on: https://skia-review.googlesource.com/21111
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
Florin Malita 2017-06-28 14:46:54 -04:00 committed by Skia Commit-Bot
parent c02de0b844
commit 2e409009fb
15 changed files with 4721 additions and 3963 deletions

View File

@ -107,7 +107,8 @@ struct SkJumper_constants;
M(evenly_spaced_2_stop_gradient) \
M(xy_to_unit_angle) \
M(xy_to_radius) \
M(xy_to_2pt_conical) \
M(xy_to_2pt_conical_quadratic) M(xy_to_2pt_conical_linear) \
M(vector_scale) \
M(byte_tables) M(byte_tables_rgb) \
M(rgb_to_hsl) \
M(hsl_to_rgb)

View File

@ -108,6 +108,7 @@ struct SkJumper_GradientCtx {
};
struct SkJumper_2PtConicalCtx {
float fMask[SkJumper_kMaxStride];
float fCoeffA,
fInvCoeffA,
fR0,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1208,7 +1208,7 @@ STAGE(xy_to_radius) {
r = sqrt_(X2 + Y2);
}
STAGE(xy_to_2pt_conical) {
STAGE(xy_to_2pt_conical_quadratic) {
auto* c = (const SkJumper_2PtConicalCtx*)ctx;
// At this point, (x, y) is mapped into a synthetic gradient space with
@ -1255,6 +1255,31 @@ STAGE(xy_to_2pt_conical) {
(-coeffB - sqrt_disc) * invCoeffA * .5f);
}
STAGE(xy_to_2pt_conical_linear) {
auto* c = (SkJumper_2PtConicalCtx*)ctx;
const F coeffB = -2 * (r + c->fDR * c->fR0),
coeffC = r * r + g * g - c->fR0 * c->fR0;
r = -coeffC / coeffB;
// Compute and save a mask for degenerate values.
g = 1.0f;
g = if_then_else(mad(r, c->fDR, c->fR0) < 0, F(0), g); // R(t) < 0
g = if_then_else(r != r , F(0), g); // NaN
unaligned_store(&c->fMask, g);
}
STAGE(vector_scale) {
const F scale = unaligned_load<F>((const float*)ctx);
r = r * scale;
g = g * scale;
b = b * scale;
a = a * scale;
}
STAGE(save_xy) {
auto c = (SkJumper_SamplerCtx*)ctx;

View File

@ -375,8 +375,9 @@ bool SkGradientShaderBase::onAppendStages(SkRasterPipeline* p,
return false;
}
SkRasterPipeline_<256> subclass;
if (!this->adjustMatrixAndAppendStages(alloc, &matrix, &subclass)) {
SkRasterPipeline_<256> tPipeline;
SkRasterPipeline_<256> postPipeline;
if (!this->adjustMatrixAndAppendStages(alloc, &matrix, &tPipeline, &postPipeline)) {
return this->INHERITED::onAppendStages(p, dstCS, alloc, ctm, paint, localM);
}
@ -390,7 +391,7 @@ bool SkGradientShaderBase::onAppendStages(SkRasterPipeline* p,
p->append(SkRasterPipeline::matrix_perspective, m);
}
p->extend(subclass);
p->extend(tPipeline);
switch(fTileMode) {
case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x_1); break;
@ -496,6 +497,8 @@ bool SkGradientShaderBase::onAppendStages(SkRasterPipeline* p,
p->append(SkRasterPipeline::premul);
}
p->extend(postPipeline);
return true;
}

View File

@ -239,7 +239,10 @@ protected:
virtual bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const { return false; }
SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const {
return false;
}
template <typename T, typename... Args>
static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {

View File

@ -81,7 +81,8 @@ SkShaderBase::Context* SkLinearGradient::onMakeBurstPipelineContext(
bool SkLinearGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const {
SkRasterPipeline* p,
SkRasterPipeline*) const {
*matrix = SkMatrix::Concat(fPtsToUnit, *matrix);
// If the gradient is less than a quarter of a pixel, this falls into the
// subpixel gradient code handled on a different path.

View File

@ -66,7 +66,8 @@ protected:
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const final;
SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const final;
sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;

View File

@ -382,7 +382,8 @@ sk_sp<SkShader> SkRadialGradient::onMakeColorSpace(SkColorSpaceXformer* xformer)
bool SkRadialGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const {
SkRasterPipeline* p,
SkRasterPipeline*) const {
matrix->postTranslate(-fCenter.fX, -fCenter.fY);
matrix->postScale(1/fRadius, 1/fRadius);

View File

@ -40,7 +40,8 @@ protected:
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const final;
SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const final;
private:
const SkPoint fCenter;

View File

@ -301,7 +301,8 @@ void SkSweepGradient::toString(SkString* str) const {
bool SkSweepGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const {
SkRasterPipeline* p,
SkRasterPipeline*) const {
matrix->postTranslate(-fCenter.fX, -fCenter.fY);
p->append(SkRasterPipeline::xy_to_unit_angle);

View File

@ -40,7 +40,8 @@ protected:
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const final;
SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const final;
bool isRasterPipelineOnly() const final;

View File

@ -428,7 +428,8 @@ void SkTwoPointConicalGradient::toString(SkString* str) const {
bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const {
SkRasterPipeline* p,
SkRasterPipeline* postPipeline) const {
const auto dCenter = (fCenter1 - fCenter2).length();
const auto dRadius = fRadius2 - fRadius1;
SkASSERT(dRadius >= 0);
@ -468,17 +469,21 @@ bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
// Since we've squashed the centers into a unit vector, we must also scale
// all the coefficient variables by (1 / dCenter).
const auto coeffA = 1 - dRadius * dRadius / (dCenter * dCenter);
if (SkScalarNearlyZero(coeffA)) {
// We only handle well behaved quadratic cases for now.
return false;
}
auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
ctx->fCoeffA = coeffA;
ctx->fInvCoeffA = 1 / coeffA;
ctx->fR0 = fRadius1 / dCenter;
ctx->fDR = dRadius / dCenter;
p->append(SkRasterPipeline::xy_to_2pt_conical, ctx);
if (SkScalarNearlyZero(coeffA)) {
// The focal point is on the edge of the end circle.
p->append(SkRasterPipeline::xy_to_2pt_conical_linear, ctx);
// To handle degenerate values (NaN, r < 0), the t stage sets up a scale/mask
// context, which we post-apply to force transparent black.
postPipeline->append(SkRasterPipeline::vector_scale, &ctx->fMask);
} else {
p->append(SkRasterPipeline::xy_to_2pt_conical_quadratic, ctx);
}
return true;
}

View File

@ -81,7 +81,8 @@ protected:
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
SkMatrix* matrix,
SkRasterPipeline* p) const final;
SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const final;
private:
SkPoint fCenter1;