2pt conical stage for focal-point-outside case
A couple of annoyances here: 1) the prev vector_scale stage is not usable for masking, as NaN values can propagate through => switch to actual masking 2) for the outside case, we must select the min root when the gradient is flipped => split into two templated stages (_min, _max) (I'm not convinced that we need to flip the gradient for RP at all; we can investigate later) Change-Id: I0283812d613a53124f2987d1aea1f26e4533655e Reviewed-on: https://skia-review.googlesource.com/21162 Reviewed-by: Mike Klein <mtklein@chromium.org> Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
762466e9fe
commit
9026fe13a7
@ -107,8 +107,10 @@ struct SkJumper_constants;
|
||||
M(evenly_spaced_2_stop_gradient) \
|
||||
M(xy_to_unit_angle) \
|
||||
M(xy_to_radius) \
|
||||
M(xy_to_2pt_conical_quadratic) M(xy_to_2pt_conical_linear) \
|
||||
M(vector_scale) \
|
||||
M(xy_to_2pt_conical_quadratic_min) \
|
||||
M(xy_to_2pt_conical_quadratic_max) \
|
||||
M(xy_to_2pt_conical_linear) \
|
||||
M(mask_2pt_conical_degenerates) M(apply_vector_mask) \
|
||||
M(byte_tables) M(byte_tables_rgb) \
|
||||
M(rgb_to_hsl) \
|
||||
M(hsl_to_rgb)
|
||||
|
@ -108,11 +108,11 @@ struct SkJumper_GradientCtx {
|
||||
};
|
||||
|
||||
struct SkJumper_2PtConicalCtx {
|
||||
float fMask[SkJumper_kMaxStride];
|
||||
float fCoeffA,
|
||||
fInvCoeffA,
|
||||
fR0,
|
||||
fDR;
|
||||
uint32_t fMask[SkJumper_kMaxStride];
|
||||
float fCoeffA,
|
||||
fInvCoeffA,
|
||||
fR0,
|
||||
fDR;
|
||||
};
|
||||
|
||||
#endif//SkJumper_DEFINED
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1208,9 +1208,7 @@ STAGE(xy_to_radius) {
|
||||
r = sqrt_(X2 + Y2);
|
||||
}
|
||||
|
||||
STAGE(xy_to_2pt_conical_quadratic) {
|
||||
auto* c = (const SkJumper_2PtConicalCtx*)ctx;
|
||||
|
||||
SI F solve_2pt_conical_quadratic(const SkJumper_2PtConicalCtx* c, F x, F y, F (*select)(F, F)) {
|
||||
// At this point, (x, y) is mapped into a synthetic gradient space with
|
||||
// the start circle centerd on (0, 0), and the end circle centered on (1, 0)
|
||||
// (see the stage setup).
|
||||
@ -1230,54 +1228,62 @@ STAGE(xy_to_2pt_conical_quadratic) {
|
||||
//
|
||||
// Since the start/end circle centers are the extremes of the [0, 1] interval
|
||||
// on the X axis, the solution (x') is exactly the t we are looking for.
|
||||
//
|
||||
// The setup code also ensures that we only use this stage when the discriminant
|
||||
// is positive. So off we go...
|
||||
|
||||
const F coeffA = c->fCoeffA,
|
||||
coeffB = -2 * (r + c->fDR * c->fR0),
|
||||
coeffC = r * r + g * g - c->fR0 * c->fR0;
|
||||
coeffB = -2 * (x + c->fDR*c->fR0),
|
||||
coeffC = x*x + y*y - c->fR0*c->fR0;
|
||||
|
||||
const F disc = mad(coeffB, coeffB, -4 * coeffA * coeffC);
|
||||
// SkASSERT(disc >= 0);
|
||||
const F disc = mad(coeffB, coeffB, -4 * coeffA * coeffC);
|
||||
const F sqrt_disc = sqrt_(disc);
|
||||
|
||||
const F invCoeffA = c->fInvCoeffA;
|
||||
// We want the larger root, per spec:
|
||||
// "For all values of ω where r(ω) > 0, starting with the value of ω nearest
|
||||
// to positive infinity and ending with the value of ω nearest to negative
|
||||
// infinity, draw the circumference of the circle with radius r(ω) at position
|
||||
// (x(ω), y(ω)), with the color at ω, but only painting on the parts of the
|
||||
// bitmap that have not yet been painted on by earlier circles in this step for
|
||||
// this rendering of the gradient."
|
||||
// (https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createradialgradient)
|
||||
r = max((-coeffB + sqrt_disc) * invCoeffA * .5f,
|
||||
(-coeffB - sqrt_disc) * invCoeffA * .5f);
|
||||
return select((-coeffB + sqrt_disc) * (invCoeffA * 0.5f),
|
||||
(-coeffB - sqrt_disc) * (invCoeffA * 0.5f));
|
||||
}
|
||||
|
||||
STAGE(xy_to_2pt_conical_quadratic_max) {
|
||||
r = solve_2pt_conical_quadratic(ctx, r, g, max);
|
||||
}
|
||||
|
||||
STAGE(xy_to_2pt_conical_quadratic_min) {
|
||||
r = solve_2pt_conical_quadratic(ctx, r, g, min);
|
||||
}
|
||||
|
||||
STAGE(xy_to_2pt_conical_linear) {
|
||||
auto* c = (SkJumper_2PtConicalCtx*)ctx;
|
||||
auto* c = (const SkJumper_2PtConicalCtx*)ctx;
|
||||
|
||||
const F coeffB = -2 * (r + c->fDR * c->fR0),
|
||||
coeffC = r * r + g * g - c->fR0 * c->fR0;
|
||||
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);
|
||||
STAGE(mask_2pt_conical_degenerates) {
|
||||
auto* c = (SkJumper_2PtConicalCtx*)ctx;
|
||||
|
||||
r = r * scale;
|
||||
g = g * scale;
|
||||
b = b * scale;
|
||||
a = a * scale;
|
||||
// Compute and save a mask for degenerate values.
|
||||
U32 mask = 0xffffffff;
|
||||
|
||||
// TODO: mtklein kindly volunteered to revisit this at some point.
|
||||
#if defined(JUMPER)
|
||||
// Vector comparisons set all bits, so we can use something like this.
|
||||
mask = mask & (mad(r, c->fDR, c->fR0) >= 0); // R(t) >= 0
|
||||
mask = mask & (r == r); // t != NaN
|
||||
#else
|
||||
// The portable version is more involved, 'cause we only get one bit back.
|
||||
mask = mask & if_then_else(mad(r, c->fDR, c->fR0) >= 0, U32(0xffffffff), U32(0)); // R(t) >= 0
|
||||
mask = mask & if_then_else(r == r, U32(0xffffffff), U32(0)); // t != NaN
|
||||
#endif
|
||||
|
||||
unaligned_store(&c->fMask, mask);
|
||||
}
|
||||
|
||||
STAGE(apply_vector_mask) {
|
||||
const U32 mask = unaligned_load<U32>((const uint32_t*)ctx);
|
||||
r = bit_cast<F>(bit_cast<U32>(r) & mask);
|
||||
g = bit_cast<F>(bit_cast<U32>(g) & mask);
|
||||
b = bit_cast<F>(bit_cast<U32>(b) & mask);
|
||||
a = bit_cast<F>(bit_cast<U32>(a) & mask);
|
||||
}
|
||||
|
||||
STAGE(save_xy) {
|
||||
|
@ -453,11 +453,6 @@ bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dCenter + fRadius1 > fRadius2) {
|
||||
// We only handle well behaved cases for now.
|
||||
return false;
|
||||
}
|
||||
|
||||
// To simplify the stage math, we transform the universe (translate/scale/rotate)
|
||||
// such that fCenter1 -> (0, 0) and fCenter2 -> (1, 0).
|
||||
SkMatrix map_to_unit_vector;
|
||||
@ -475,14 +470,37 @@ bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
ctx->fR0 = fRadius1 / dCenter;
|
||||
ctx->fDR = dRadius / dCenter;
|
||||
|
||||
// Is the solver guaranteed to not produce degenerates?
|
||||
bool isWellBehaved = true;
|
||||
|
||||
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);
|
||||
isWellBehaved = false;
|
||||
} else {
|
||||
p->append(SkRasterPipeline::xy_to_2pt_conical_quadratic, ctx);
|
||||
if (dCenter + fRadius1 > fRadius2) {
|
||||
// The focal point is outside the end circle.
|
||||
|
||||
// We want the larger root, per spec:
|
||||
// "For all values of ω where r(ω) > 0, starting with the value of ω nearest
|
||||
// to positive infinity and ending with the value of ω nearest to negative
|
||||
// infinity, draw the circumference of the circle with radius r(ω) at position
|
||||
// (x(ω), y(ω)), with the color at ω, but only painting on the parts of the
|
||||
// bitmap that have not yet been painted on by earlier circles in this step for
|
||||
// this rendering of the gradient."
|
||||
// (https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createradialgradient)
|
||||
p->append(fFlippedGrad ? SkRasterPipeline::xy_to_2pt_conical_quadratic_min
|
||||
: SkRasterPipeline::xy_to_2pt_conical_quadratic_max, ctx);
|
||||
isWellBehaved = false;
|
||||
} else {
|
||||
// The focal point is inside (well-behaved case).
|
||||
p->append(SkRasterPipeline::xy_to_2pt_conical_quadratic_max, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isWellBehaved) {
|
||||
p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
|
||||
postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
Loading…
Reference in New Issue
Block a user