Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions src/pipeline/lowp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,7 @@ pub const STAGES: &[StageFn; super::STAGES_COUNT] = &[
repeat_x1,
gradient,
evenly_spaced_2_stop_gradient,
// TODO: Can be implemented for lowp as well. The implementation is very similar to its highp
// variant.
null_fn, // XYToUnitAngle
xy_to_unit_angle,
xy_to_radius,
null_fn, // XYTo2PtConicalFocalOnCircle
null_fn, // XYTo2PtConicalWellBehaved
Expand All @@ -141,7 +139,7 @@ pub const STAGES: &[StageFn; super::STAGES_COUNT] = &[
null_fn, // Alter2PtConicalCompensateFocal
null_fn, // Alter2PtConicalUnswap
null_fn, // NegateX
null_fn, // ApplyConcentricScaleBias
apply_concentric_scale_bias,
null_fn, // GammaExpand2
null_fn, // GammaExpandDestination2
null_fn, // GammaCompress2
Expand Down Expand Up @@ -646,6 +644,35 @@ fn evenly_spaced_2_stop_gradient(p: &mut Pipeline) {
p.next_stage();
}


fn xy_to_unit_angle(p: &mut Pipeline) {
let x = join(&p.r, &p.g);
let y = join(&p.b, &p.a);
let (x_abs, y_abs) = (x.abs(), y.abs());

let slope = x_abs.min(y_abs) / x_abs.max(y_abs);
let s = slope * slope;
// Use a 7th degree polynomial to approximate atan.
// This was generated using sollya.gforge.inria.fr.
// A float optimized polynomial was generated using the following command.
// P1 = fpminimax((1/(2*Pi))*atan(x),[|1,3,5,7|],[|24...|],[2^(-40),1],relative);
let phi = slope
* (f32x16::splat(0.15912117063999176025390625)
+ s * (f32x16::splat(-5.185396969318389892578125e-2)
+ s * (f32x16::splat(2.476101927459239959716796875e-2)
+ s * (f32x16::splat(-7.0547382347285747528076171875e-3)))));
let phi = x_abs.cmp_lt(y_abs).blend(f32x16::splat(0.25) - phi, phi);
let phi = x
.cmp_lt(f32x16::splat(0.0))
.blend(f32x16::splat(0.5) - phi, phi);
let phi = y
.cmp_lt(f32x16::splat(0.0))
.blend(f32x16::splat(1.0) - phi, phi);
let phi = phi.cmp_ne(phi).blend(f32x16::splat(0.0), phi);
split(&phi, &mut p.r, &mut p.g);
p.next_stage();
}

fn xy_to_radius(p: &mut Pipeline) {
let x = join(&p.r, &p.g);
let y = join(&p.b, &p.a);
Expand All @@ -656,6 +683,17 @@ fn xy_to_radius(p: &mut Pipeline) {
p.next_stage();
}


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

let t = join(&p.r, &p.g);
let t = mad(t, f32x16::splat(ctx.p0), f32x16::splat(ctx.p1));
split(&t, &mut p.r, &mut p.g);

p.next_stage();
}

// We are using u16 for index, not u32 as Skia, to simplify the code a bit.
// The gradient creation code will not allow that many stops anyway.
fn gradient_lookup(
Expand Down
24 changes: 24 additions & 0 deletions src/wide/f32x16_t.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ impl f32x16 {
)
}

pub fn min(self, rhs: Self) -> Self {
Self(self.0.min(rhs.0), self.1.min(rhs.1))
}

pub fn max(self, rhs: Self) -> Self {
Self(self.0.max(rhs.0), self.1.max(rhs.1))
}

pub fn cmp_ne(self, rhs: Self) -> Self {
Self(self.0.cmp_ne(rhs.0), self.1.cmp_ne(rhs.1))
}

pub fn cmp_lt(self, rhs: Self) -> Self {
Self(self.0.cmp_lt(rhs.0), self.1.cmp_lt(rhs.1))
}

pub fn cmp_gt(self, rhs: &Self) -> Self {
Self(self.0.cmp_gt(rhs.0), self.1.cmp_gt(rhs.1))
}
Expand Down Expand Up @@ -136,3 +152,11 @@ impl core::ops::Mul<f32x16> for f32x16 {
Self(self.0 * rhs.0, self.1 * rhs.1)
}
}

impl core::ops::Div<f32x16> for f32x16 {
type Output = Self;

fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0, self.1 / rhs.1)
}
}
Binary file added tests/images/gradients/concentric-radial-lq.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/images/gradients/sweep-gradient-full-lq.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/images/gradients/sweep-gradient-lq.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
106 changes: 100 additions & 6 deletions tests/integration/gradients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ fn strip_gradient() {
}

#[test]
fn concentric_radial() {
fn concentric_radial_lq() {
// Same center, non-zero start radius (concentric gradient)
let mut paint = Paint::default();
paint.anti_alias = false;
Expand All @@ -491,7 +491,35 @@ fn concentric_radial() {
let mut pixmap = Pixmap::new(200, 200).unwrap();
pixmap.fill_path(&path, &paint, FillRule::Winding, Transform::identity(), None);

let expected = Pixmap::load_png("tests/images/gradients/concentric-radial.png").unwrap();
let expected = Pixmap::load_png("tests/images/gradients/concentric-radial-lq.png").unwrap();
assert_eq!(pixmap, expected);
}

#[test]
fn concentric_radial_hq() {
// Same center, non-zero start radius (concentric gradient)
let mut paint = Paint::default();
paint.anti_alias = false;
paint.force_hq_pipeline = true;
paint.shader = RadialGradient::new(
Point::from_xy(100.0, 100.0),
30.0,
Point::from_xy(100.0, 100.0),
90.0,
vec![
GradientStop::new(0.0, Color::from_rgba8(50, 127, 150, 200)),
GradientStop::new(1.0, Color::from_rgba8(220, 140, 75, 180)),
],
SpreadMode::Pad,
Transform::identity(),
).unwrap();

let path = PathBuilder::from_rect(Rect::from_ltrb(10.0, 10.0, 190.0, 190.0).unwrap());

let mut pixmap = Pixmap::new(200, 200).unwrap();
pixmap.fill_path(&path, &paint, FillRule::Winding, Transform::identity(), None);

let expected = Pixmap::load_png("tests/images/gradients/concentric-radial-hq.png").unwrap();
assert_eq!(pixmap, expected);
}

Expand Down Expand Up @@ -527,9 +555,74 @@ fn conical_smaller_radial() {
}

#[test]
fn sweep_gradient() {
fn sweep_gradient_lq() {
let mut paint = Paint::default();
paint.anti_alias = false;
paint.shader = SweepGradient::new(
Point::from_xy(100.0, 100.0),
135.0,
225.0,
vec![
GradientStop::new(0.0, Color::from_rgba8(50, 127, 150, 200)),
GradientStop::new(1.0, Color::from_rgba8(220, 140, 75, 180)),
],
SpreadMode::Pad,
Transform::identity(),
)
.unwrap();

let path = PathBuilder::from_rect(Rect::from_ltrb(10.0, 10.0, 190.0, 190.0).unwrap());

let mut pixmap = Pixmap::new(200, 200).unwrap();
pixmap.fill_path(
&path,
&paint,
FillRule::Winding,
Transform::identity(),
None,
);

let expected = Pixmap::load_png("tests/images/gradients/sweep-gradient-lq.png").unwrap();
assert_eq!(pixmap, expected);
}

#[test]
fn sweep_gradient_full_lq() {
let mut paint = Paint::default();
paint.anti_alias = false;
paint.shader = SweepGradient::new(
Point::from_xy(100.0, 100.0),
0.0,
360.0,
vec![
GradientStop::new(0.0, Color::from_rgba8(50, 127, 150, 200)),
GradientStop::new(1.0, Color::from_rgba8(220, 140, 75, 180)),
],
SpreadMode::Pad,
Transform::identity(),
)
.unwrap();

let path = PathBuilder::from_rect(Rect::from_ltrb(10.0, 10.0, 190.0, 190.0).unwrap());

let mut pixmap = Pixmap::new(200, 200).unwrap();
pixmap.fill_path(
&path,
&paint,
FillRule::Winding,
Transform::identity(),
None,
);

let expected = Pixmap::load_png("tests/images/gradients/sweep-gradient-full-lq.png").unwrap();
assert_eq!(pixmap, expected);
}

#[test]
fn sweep_gradient_hq() {
let mut paint = Paint::default();
paint.anti_alias = false;
paint.force_hq_pipeline = true;
paint.shader = SweepGradient::new(
Point::from_xy(100.0, 100.0),
135.0,
Expand All @@ -554,14 +647,15 @@ fn sweep_gradient() {
None,
);

let expected = Pixmap::load_png("tests/images/gradients/sweep-gradient.png").unwrap();
let expected = Pixmap::load_png("tests/images/gradients/sweep-gradient-hq.png").unwrap();
assert_eq!(pixmap, expected);
}

#[test]
fn sweep_gradient_full() {
fn sweep_gradient_full_hq() {
let mut paint = Paint::default();
paint.anti_alias = false;
paint.force_hq_pipeline = true;
paint.shader = SweepGradient::new(
Point::from_xy(100.0, 100.0),
0.0,
Expand All @@ -586,6 +680,6 @@ fn sweep_gradient_full() {
None,
);

let expected = Pixmap::load_png("tests/images/gradients/sweep-gradient-full.png").unwrap();
let expected = Pixmap::load_png("tests/images/gradients/sweep-gradient-full-hq.png").unwrap();
assert_eq!(pixmap, expected);
}