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(evenly_spaced_2_stop_gradient) \
|
||||||
M(xy_to_unit_angle) \
|
M(xy_to_unit_angle) \
|
||||||
M(xy_to_radius) \
|
M(xy_to_radius) \
|
||||||
M(xy_to_2pt_conical_quadratic) M(xy_to_2pt_conical_linear) \
|
M(xy_to_2pt_conical_quadratic_min) \
|
||||||
M(vector_scale) \
|
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(byte_tables) M(byte_tables_rgb) \
|
||||||
M(rgb_to_hsl) \
|
M(rgb_to_hsl) \
|
||||||
M(hsl_to_rgb)
|
M(hsl_to_rgb)
|
||||||
|
@ -108,11 +108,11 @@ struct SkJumper_GradientCtx {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SkJumper_2PtConicalCtx {
|
struct SkJumper_2PtConicalCtx {
|
||||||
float fMask[SkJumper_kMaxStride];
|
uint32_t fMask[SkJumper_kMaxStride];
|
||||||
float fCoeffA,
|
float fCoeffA,
|
||||||
fInvCoeffA,
|
fInvCoeffA,
|
||||||
fR0,
|
fR0,
|
||||||
fDR;
|
fDR;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif//SkJumper_DEFINED
|
#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);
|
r = sqrt_(X2 + Y2);
|
||||||
}
|
}
|
||||||
|
|
||||||
STAGE(xy_to_2pt_conical_quadratic) {
|
SI F solve_2pt_conical_quadratic(const SkJumper_2PtConicalCtx* c, F x, F y, F (*select)(F, F)) {
|
||||||
auto* c = (const SkJumper_2PtConicalCtx*)ctx;
|
|
||||||
|
|
||||||
// At this point, (x, y) is mapped into a synthetic gradient space with
|
// 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)
|
// the start circle centerd on (0, 0), and the end circle centered on (1, 0)
|
||||||
// (see the stage setup).
|
// (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
|
// 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.
|
// 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,
|
const F coeffA = c->fCoeffA,
|
||||||
coeffB = -2 * (r + c->fDR * c->fR0),
|
coeffB = -2 * (x + c->fDR*c->fR0),
|
||||||
coeffC = r * r + g * g - c->fR0 * c->fR0;
|
coeffC = x*x + y*y - c->fR0*c->fR0;
|
||||||
|
|
||||||
const F disc = mad(coeffB, coeffB, -4 * coeffA * coeffC);
|
const F disc = mad(coeffB, coeffB, -4 * coeffA * coeffC);
|
||||||
// SkASSERT(disc >= 0);
|
|
||||||
const F sqrt_disc = sqrt_(disc);
|
const F sqrt_disc = sqrt_(disc);
|
||||||
|
|
||||||
const F invCoeffA = c->fInvCoeffA;
|
const F invCoeffA = c->fInvCoeffA;
|
||||||
// We want the larger root, per spec:
|
return select((-coeffB + sqrt_disc) * (invCoeffA * 0.5f),
|
||||||
// "For all values of ω where r(ω) > 0, starting with the value of ω nearest
|
(-coeffB - sqrt_disc) * (invCoeffA * 0.5f));
|
||||||
// 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
|
STAGE(xy_to_2pt_conical_quadratic_max) {
|
||||||
// bitmap that have not yet been painted on by earlier circles in this step for
|
r = solve_2pt_conical_quadratic(ctx, r, g, max);
|
||||||
// this rendering of the gradient."
|
}
|
||||||
// (https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createradialgradient)
|
|
||||||
r = max((-coeffB + sqrt_disc) * invCoeffA * .5f,
|
STAGE(xy_to_2pt_conical_quadratic_min) {
|
||||||
(-coeffB - sqrt_disc) * invCoeffA * .5f);
|
r = solve_2pt_conical_quadratic(ctx, r, g, min);
|
||||||
}
|
}
|
||||||
|
|
||||||
STAGE(xy_to_2pt_conical_linear) {
|
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),
|
const F coeffB = -2 * (r + c->fDR*c->fR0),
|
||||||
coeffC = r * r + g * g - c->fR0 * c->fR0;
|
coeffC = r*r + g*g - c->fR0*c->fR0;
|
||||||
|
|
||||||
r = -coeffC / coeffB;
|
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) {
|
STAGE(mask_2pt_conical_degenerates) {
|
||||||
const F scale = unaligned_load<F>((const float*)ctx);
|
auto* c = (SkJumper_2PtConicalCtx*)ctx;
|
||||||
|
|
||||||
r = r * scale;
|
// Compute and save a mask for degenerate values.
|
||||||
g = g * scale;
|
U32 mask = 0xffffffff;
|
||||||
b = b * scale;
|
|
||||||
a = a * scale;
|
// 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) {
|
STAGE(save_xy) {
|
||||||
|
@ -453,11 +453,6 @@ bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
|||||||
return true;
|
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)
|
// To simplify the stage math, we transform the universe (translate/scale/rotate)
|
||||||
// such that fCenter1 -> (0, 0) and fCenter2 -> (1, 0).
|
// such that fCenter1 -> (0, 0) and fCenter2 -> (1, 0).
|
||||||
SkMatrix map_to_unit_vector;
|
SkMatrix map_to_unit_vector;
|
||||||
@ -475,14 +470,37 @@ bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
|||||||
ctx->fR0 = fRadius1 / dCenter;
|
ctx->fR0 = fRadius1 / dCenter;
|
||||||
ctx->fDR = dRadius / dCenter;
|
ctx->fDR = dRadius / dCenter;
|
||||||
|
|
||||||
|
// Is the solver guaranteed to not produce degenerates?
|
||||||
|
bool isWellBehaved = true;
|
||||||
|
|
||||||
if (SkScalarNearlyZero(coeffA)) {
|
if (SkScalarNearlyZero(coeffA)) {
|
||||||
// The focal point is on the edge of the end circle.
|
// The focal point is on the edge of the end circle.
|
||||||
p->append(SkRasterPipeline::xy_to_2pt_conical_linear, ctx);
|
p->append(SkRasterPipeline::xy_to_2pt_conical_linear, ctx);
|
||||||
// To handle degenerate values (NaN, r < 0), the t stage sets up a scale/mask
|
isWellBehaved = false;
|
||||||
// context, which we post-apply to force transparent black.
|
|
||||||
postPipeline->append(SkRasterPipeline::vector_scale, &ctx->fMask);
|
|
||||||
} else {
|
} 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;
|
return true;
|
||||||
|
Loading…
Reference in New Issue
Block a user