Skip to content
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- 2-point conical gradient support for (`RadialGradient`).
Thanks to [@wmedrano](https://github.com/wmedrano)

### Changed
- The `RadialGradient::new` requires a start radius now. Set the second argument
to 0.0 to preserve the old behavior.


## [0.11.4] - 2024-02-04
### Fixed
Expand Down
2 changes: 2 additions & 0 deletions benches/src/gradients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ fn simple_radial_tiny_skia(bencher: &mut Bencher) {
paint.anti_alias = false;
paint.shader = RadialGradient::new(
Point::from_xy(500.0, 500.0),
0.0,
Point::from_xy(500.0, 500.0),
500.0,
vec![
Expand Down Expand Up @@ -692,6 +693,7 @@ fn two_point_radial_tiny_skia(bencher: &mut Bencher) {
paint.anti_alias = false;
paint.shader = RadialGradient::new(
Point::from_xy(400.0, 400.0),
0.0,
Point::from_xy(500.0, 500.0),
500.0,
vec![
Expand Down
5 changes: 5 additions & 0 deletions path/src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub trait Scalar {
fn invert(self) -> Self;
fn bound(self, min: Self, max: Self) -> Self;
fn is_nearly_equal(self, other: Self) -> bool;
fn is_nearly_equal_within_tolerance(self, other: Self, tolerance: Self) -> bool;
fn is_nearly_zero(self) -> bool;
fn is_nearly_zero_within_tolerance(self, tolerance: Self) -> bool;
fn almost_dequal_ulps(self, other: Self) -> bool;
Expand Down Expand Up @@ -56,6 +57,10 @@ impl Scalar for f32 {
(self - other).abs() <= SCALAR_NEARLY_ZERO
}

fn is_nearly_equal_within_tolerance(self, other: Self, tolerance: Self) -> bool {
(self - other).abs() <= tolerance
}

fn is_nearly_zero(self) -> bool {
self.is_nearly_zero_within_tolerance(SCALAR_NEARLY_ZERO)
}
Expand Down
97 changes: 84 additions & 13 deletions src/pipeline/highp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,16 @@ pub const STAGES: &[StageFn; super::STAGES_COUNT] = &[
xy_to_radius,
xy_to_2pt_conical_focal_on_circle,
xy_to_2pt_conical_well_behaved,
xy_to_2pt_conical_smaller,
xy_to_2pt_conical_greater,
xy_to_2pt_conical_strip,
mask_2pt_conical_nan,
mask_2pt_conical_degenerates,
apply_vector_mask,
alter_2pt_conical_compensate_focal,
alter_2pt_conical_unswap,
negate_x,
apply_concentric_scale_bias,
gamma_expand_2,
gamma_expand_dst_2,
gamma_compress_2,
Expand Down Expand Up @@ -1017,25 +1024,44 @@ fn xy_to_2pt_conical_greater(p: &mut Pipeline) {
p.next_stage();
}

fn xy_to_2pt_conical_smaller(p: &mut Pipeline) {
let ctx = &p.ctx.two_point_conical_gradient;

let x = p.r;
let y = p.g;
p.r = -(x * x - y * y).sqrt() - x * f32x8::splat(ctx.p0);

p.next_stage();
}

fn xy_to_2pt_conical_strip(p: &mut Pipeline) {
let ctx = &p.ctx.two_point_conical_gradient;

let x = p.r;
let y = p.g;
p.r = x + (f32x8::splat(ctx.p0) - y * y).sqrt();

p.next_stage();
}

fn mask_2pt_conical_nan(p: &mut Pipeline) {
let ctx = &mut p.ctx.two_point_conical_gradient;

let t = p.r;
let is_degenerate = t.cmp_ne(t);
p.r = is_degenerate.blend(f32x8::default(), t);
ctx.mask = cond_to_mask(!is_degenerate.to_u32x8_bitcast());

p.next_stage();
}

fn mask_2pt_conical_degenerates(p: &mut Pipeline) {
let ctx = &mut p.ctx.two_point_conical_gradient;

let t = p.r;
let is_degenerate = t.cmp_le(f32x8::default()) | t.cmp_ne(t);
p.r = is_degenerate.blend(f32x8::default(), t);

let is_not_degenerate = !is_degenerate.to_u32x8_bitcast();
let is_not_degenerate: [u32; 8] = bytemuck::cast(is_not_degenerate);
ctx.mask = bytemuck::cast([
if is_not_degenerate[0] != 0 { !0 } else { 0 },
if is_not_degenerate[1] != 0 { !0 } else { 0 },
if is_not_degenerate[2] != 0 { !0 } else { 0 },
if is_not_degenerate[3] != 0 { !0 } else { 0 },
if is_not_degenerate[4] != 0 { !0 } else { 0 },
if is_not_degenerate[5] != 0 { !0 } else { 0 },
if is_not_degenerate[6] != 0 { !0 } else { 0 },
if is_not_degenerate[7] != 0 { !0 } else { 0 },
]);
ctx.mask = cond_to_mask(!is_degenerate.to_u32x8_bitcast());

p.next_stage();
}
Expand All @@ -1051,6 +1077,36 @@ fn apply_vector_mask(p: &mut Pipeline) {
p.next_stage();
}

fn alter_2pt_conical_compensate_focal(p: &mut Pipeline) {
let ctx = &p.ctx.two_point_conical_gradient;

p.r = p.r + f32x8::splat(ctx.p1);

p.next_stage();
}

fn alter_2pt_conical_unswap(p: &mut Pipeline) {
p.r = f32x8::splat(1.0) - p.r;

p.next_stage();
}

fn negate_x(p: &mut Pipeline) {
p.r = -p.r;

p.next_stage();
}

fn apply_concentric_scale_bias(p: &mut Pipeline) {
let ctx = &p.ctx.two_point_conical_gradient;

// Apply t = t * scale + bias for concentric gradients
let x = p.r;
p.r = x * f32x8::splat(ctx.p0) + f32x8::splat(ctx.p1);

p.next_stage();
}

fn gamma_expand_2(p: &mut Pipeline) {
p.r = p.r * p.r;
p.g = p.g * p.g;
Expand Down Expand Up @@ -1141,6 +1197,21 @@ pub fn just_return(_: &mut Pipeline) {
// Ends the loop.
}

#[inline(always)]
fn cond_to_mask(cond: u32x8) -> u32x8 {
let cond: [u32; 8] = bytemuck::cast(cond);
bytemuck::cast([
if cond[0] != 0 { !0 } else { 0 },
if cond[1] != 0 { !0 } else { 0 },
if cond[2] != 0 { !0 } else { 0 },
if cond[3] != 0 { !0 } else { 0 },
if cond[4] != 0 { !0 } else { 0 },
if cond[5] != 0 { !0 } else { 0 },
if cond[6] != 0 { !0 } else { 0 },
if cond[7] != 0 { !0 } else { 0 },
])
}

#[inline(always)]
fn load_8888(
data: &[PremultipliedColorU8; STAGE_WIDTH],
Expand Down
7 changes: 7 additions & 0 deletions src/pipeline/lowp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,16 @@ pub const STAGES: &[StageFn; super::STAGES_COUNT] = &[
xy_to_radius,
null_fn, // XYTo2PtConicalFocalOnCircle
null_fn, // XYTo2PtConicalWellBehaved
null_fn, // XYTo2PtConicalSmaller
null_fn, // XYTo2PtConicalGreater
null_fn, // XYTo2PtConicalStrip
null_fn, // Mask2PtConicalNan
null_fn, // Mask2PtConicalDegenerates
null_fn, // ApplyVectorMask
null_fn, // Alter2PtConicalCompensateFocal
null_fn, // Alter2PtConicalUnswap
null_fn, // NegateX
null_fn, // ApplyConcentricScaleBias
null_fn, // GammaExpand2
null_fn, // GammaExpandDestination2
null_fn, // GammaCompress2
Expand Down
8 changes: 8 additions & 0 deletions src/pipeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,16 @@ pub enum Stage {
XYToRadius,
XYTo2PtConicalFocalOnCircle,
XYTo2PtConicalWellBehaved,
XYTo2PtConicalSmaller,
XYTo2PtConicalGreater,
XYTo2PtConicalStrip,
Mask2PtConicalNan,
Mask2PtConicalDegenerates,
ApplyVectorMask,
Alter2PtConicalCompensateFocal,
Alter2PtConicalUnswap,
NegateX,
ApplyConcentricScaleBias,
GammaExpand2,
GammaExpandDestination2,
GammaCompress2,
Expand Down Expand Up @@ -327,6 +334,7 @@ pub struct TwoPointConicalGradientCtx {
// This context is used only in highp, where we use Tx4.
pub mask: u32x8,
pub p0: f32,
pub p1: f32,
}

#[derive(Copy, Clone, Default, Debug)]
Expand Down
Loading