add radial gradients

This commit is contained in:
Laurenz Stampfl 2024-12-15 15:10:34 +01:00
parent 67bc4bbf71
commit 9b8c30a5f0
2 changed files with 82 additions and 24 deletions

View File

@ -362,6 +362,7 @@ pub fn handle_shape(
path_builder.line_to(l.x.to_f32(), l.y.to_f32()); path_builder.line_to(l.x.to_f32(), l.y.to_f32());
} }
Geometry::Rect(r) => { Geometry::Rect(r) => {
println!("{:?}", r);
if let Some(r) = Rect::from_xywh(0.0, 0.0, r.x.to_f32(), r.y.to_f32()) { if let Some(r) = Rect::from_xywh(0.0, 0.0, r.x.to_f32(), r.y.to_f32()) {
path_builder.push_rect(r); path_builder.push_rect(r);
} }

View File

@ -6,12 +6,15 @@ use crate::primitive::{FillRuleExt, LineCapExt, LineJoinExt, TransformExt};
use crate::AbsExt; use crate::AbsExt;
use krilla::geom::NormalizedF32; use krilla::geom::NormalizedF32;
use krilla::page::{NumberingStyle, PageLabel}; use krilla::page::{NumberingStyle, PageLabel};
use krilla::paint::SpreadMethod;
use krilla::surface::Surface; use krilla::surface::Surface;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use krilla::paint::SpreadMethod;
use typst_library::layout::{Abs, Angle, AngleUnit, Quadrant, Ratio, Size, Transform}; use typst_library::layout::{Abs, Angle, AngleUnit, Quadrant, Ratio, Size, Transform};
use typst_library::model::Numbering; use typst_library::model::Numbering;
use typst_library::visualize::{Color, ColorSpace, DashPattern, FillRule, FixedStroke, Gradient, Paint, Pattern, RatioOrAngle, RelativeTo, WeightedColor}; use typst_library::visualize::{
Color, ColorSpace, DashPattern, FillRule, FixedStroke, Gradient, Paint, Pattern,
RatioOrAngle, RelativeTo, WeightedColor,
};
use typst_utils::Numeric; use typst_utils::Numeric;
pub(crate) fn fill( pub(crate) fn fill(
@ -60,11 +63,7 @@ fn dash(dash: &DashPattern<Abs, Abs>) -> krilla::path::StrokeDash {
fn convert_color(color: &Color) -> (krilla::color::rgb::Color, u8) { fn convert_color(color: &Color) -> (krilla::color::rgb::Color, u8) {
let components = color.to_space(ColorSpace::Srgb).to_vec4_u8(); let components = color.to_space(ColorSpace::Srgb).to_vec4_u8();
( (
krilla::color::rgb::Color::new( krilla::color::rgb::Color::new(components[0], components[1], components[2])
components[0],
components[1],
components[2],
)
.into(), .into(),
components[3], components[3],
) )
@ -81,7 +80,7 @@ fn paint(
Paint::Solid(c) => { Paint::Solid(c) => {
let (c, alpha) = convert_color(c); let (c, alpha) = convert_color(c);
(c.into(), alpha) (c.into(), alpha)
}, }
Paint::Gradient(g) => convert_gradient(g, on_text, transforms), Paint::Gradient(g) => convert_gradient(g, on_text, transforms),
Paint::Pattern(p) => convert_pattern(gc, p, on_text, surface, transforms), Paint::Pattern(p) => convert_pattern(gc, p, on_text, surface, transforms),
} }
@ -208,7 +207,8 @@ fn convert_gradient(
RelativeTo::Parent => transforms.container_transform, RelativeTo::Parent => transforms.container_transform,
}; };
let angle = Gradient::correct_aspect_ratio(rotation, size.aspect_ratio()); let angle = rotation;
println!("angle: {:?}", angle);
let mut stops: Vec<krilla::paint::Stop<krilla::color::rgb::Color>> = vec![]; let mut stops: Vec<krilla::paint::Stop<krilla::color::rgb::Color>> = vec![];
@ -216,18 +216,14 @@ fn convert_gradient(
let (color, opacity) = convert_color(color); let (color, opacity) = convert_color(color);
let opacity = NormalizedF32::new((opacity as f32) / 255.0).unwrap(); let opacity = NormalizedF32::new((opacity as f32) / 255.0).unwrap();
let offset = NormalizedF32::new(offset.get() as f32).unwrap(); let offset = NormalizedF32::new(offset.get() as f32).unwrap();
let stop = krilla::paint::Stop { let stop = krilla::paint::Stop { offset, color, opacity };
offset,
color,
opacity,
};
stops.push(stop); stops.push(stop);
}; };
match &gradient { match &gradient {
Gradient::Linear(linear) => { Gradient::Linear(linear) => {
let actual_transform = transforms.transform.invert().unwrap() let actual_transform =
.pre_concat(transform); transforms.transform.invert().unwrap().pre_concat(transform);
if let Some((c, t)) = linear.stops.first() { if let Some((c, t)) = linear.stops.first() {
add_single(c, *t); add_single(c, *t);
@ -244,7 +240,8 @@ fn convert_gradient(
if gradient.space().hue_index().is_some() { if gradient.space().hue_index().is_some() {
for i in 0..=32 { for i in 0..=32 {
let t = i as f64 / 32.0; let t = i as f64 / 32.0;
let real_t = Ratio::new(first.1.get() * (1.0 - t) + second.1.get() * t); let real_t =
Ratio::new(first.1.get() * (1.0 - t) + second.1.get() * t);
let c = gradient.sample(RatioOrAngle::Ratio(real_t)); let c = gradient.sample(RatioOrAngle::Ratio(real_t));
add_single(&c, real_t); add_single(&c, real_t);
@ -274,7 +271,9 @@ fn convert_gradient(
y1, y1,
x2, x2,
y2, y2,
transform: actual_transform.as_krilla().pre_concat(krilla::geom::Transform::from_scale(size.x.to_f32(), size.y.to_f32())), transform: actual_transform.as_krilla().pre_concat(
krilla::geom::Transform::from_scale(size.x.to_f32(), size.y.to_f32()),
),
spread_method: SpreadMethod::Pad, spread_method: SpreadMethod::Pad,
stops: stops.into(), stops: stops.into(),
anti_alias: gradient.anti_alias(), anti_alias: gradient.anti_alias(),
@ -282,16 +281,74 @@ fn convert_gradient(
(linear.into(), 255) (linear.into(), 255)
} }
Gradient::Radial(_) => { Gradient::Radial(radial) => {
(krilla::color::rgb::Color::black().into(), 255) let actual_transform =
transforms.transform.invert().unwrap().pre_concat(transform);
if let Some((c, t)) = radial.stops.first() {
add_single(c, *t);
}
// Create the individual gradient functions for each pair of stops.
for window in radial.stops.windows(2) {
let (first, second) = (window[0], window[1]);
// If we have a hue index or are using Oklab, we will create several
// stops in-between to make the gradient smoother without interpolation
// issues with native color spaces.
let mut last_c = first.0;
if gradient.space().hue_index().is_some() {
for i in 0..=32 {
let t = i as f64 / 32.0;
let real_t =
Ratio::new(first.1.get() * (1.0 - t) + second.1.get() * t);
let c = gradient.sample(RatioOrAngle::Ratio(real_t));
add_single(&c, real_t);
last_c = c;
}
}
add_single(&second.0, second.1);
}
let radial = krilla::paint::RadialGradient {
fx: radial.focal_center.x.get() as f32,
fy: radial.focal_center.y.get() as f32,
fr: radial.focal_radius.get() as f32,
cx: radial.center.x.get() as f32,
cy: radial.center.y.get() as f32,
cr: radial.radius.get() as f32,
transform: actual_transform.as_krilla().pre_concat(
krilla::geom::Transform::from_scale(size.x.to_f32(), size.y.to_f32()),
),
spread_method: SpreadMethod::Pad,
stops: stops.into(),
anti_alias: gradient.anti_alias(),
};
(radial.into(), 255)
} }
Gradient::Conic(conic) => { Gradient::Conic(conic) => {
// Correct the gradient's angle // Correct the gradient's angle
let cx = size.x.to_f32() * conic.center.x.get() as f32; let cx = size.x.to_f32() * conic.center.x.get() as f32;
let cy = size.y.to_f32() * conic.center.y.get() as f32; let cy = size.y.to_f32() * conic.center.y.get() as f32;
let actual_transform = transforms.transform.invert().unwrap().pre_concat(transform) let actual_transform = transforms
.pre_concat(Transform::scale_at(-Ratio::one(), Ratio::one(), Abs::pt(cx as f64), Abs::pt(cy as f64))) .transform
.pre_concat(Transform::rotate_at(-angle, Abs::pt(cx as f64), Abs::pt(cy as f64))); .invert()
.unwrap()
.pre_concat(transform)
.pre_concat(Transform::rotate_at(
angle,
Abs::pt(cx as f64),
Abs::pt(cy as f64),
))
.pre_concat(Transform::scale_at(
-Ratio::one(),
Ratio::one(),
Abs::pt(cx as f64),
Abs::pt(cy as f64),
));
if let Some((c, t)) = conic.stops.first() { if let Some((c, t)) = conic.stops.first() {
add_single(c, *t); add_single(c, *t);