First attempt at conic gradients

This commit is contained in:
Laurenz Stampfl 2024-12-15 00:42:27 +01:00
parent 79173b1c55
commit 16bcad9d5a
2 changed files with 73 additions and 68 deletions

View File

@ -162,7 +162,7 @@ pub fn pdf(typst_document: &PagedDocument) -> Vec<u8> {
let mut page = document.start_page_with(settings); let mut page = document.start_page_with(settings);
let mut surface = page.surface(); let mut surface = page.surface();
let mut fc = FrameContext::new(typst_page.frame.size()); let mut fc = FrameContext::new(typst_page.frame.size());
// println!("{:?}", &typst_page.frame); println!("{:?}", &typst_page.frame);
process_frame( process_frame(
&mut fc, &mut fc,
&typst_page.frame, &typst_page.frame,
@ -381,7 +381,7 @@ pub fn handle_shape(
shape.fill_rule, shape.fill_rule,
false, false,
surface, surface,
fc.state().transforms(Size::zero()), fc.state().transforms(shape.geometry.bbox_size()),
); );
surface.fill_path(&path, fill); surface.fill_path(&path, fill);
} }
@ -400,7 +400,7 @@ pub fn handle_shape(
stroke, stroke,
false, false,
surface, surface,
fc.state().transforms(Size::zero()), fc.state().transforms(shape.geometry.bbox_size()),
); );
surface.stroke_path(&path, stroke); surface.stroke_path(&path, stroke);
} }

View File

@ -8,10 +8,11 @@ use krilla::geom::NormalizedF32;
use krilla::page::{NumberingStyle, PageLabel}; use krilla::page::{NumberingStyle, PageLabel};
use krilla::surface::Surface; use krilla::surface::Surface;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use typst_library::layout::{Abs, Angle, Ratio, Transform}; use typst_library::layout::{Abs, Angle, AngleUnit, Ratio, Transform};
use typst_library::model::Numbering; use typst_library::model::Numbering;
use typst_library::visualize::{ColorSpace, DashPattern, FillRule, FixedStroke, Gradient, Paint, Pattern, RelativeTo}; use typst_library::visualize::{Color, ColorSpace, DashPattern, FillRule, FixedStroke, Gradient, Paint, Pattern, RelativeTo, WeightedColor};
use typst_utils::Numeric; use typst_utils::Numeric;
use crate::color_old::ColorSpaceExt;
use crate::gradient_old::PdfGradient; use crate::gradient_old::PdfGradient;
pub(crate) fn fill( pub(crate) fn fill(
@ -57,6 +58,19 @@ fn dash(dash: &DashPattern<Abs, Abs>) -> krilla::path::StrokeDash {
} }
} }
fn convert_color(color: &Color) -> (krilla::color::rgb::Color, u8) {
let components = color.to_space(ColorSpace::Srgb).to_vec4_u8();
(
krilla::color::rgb::Color::new(
components[0],
components[1],
components[2],
)
.into(),
components[3],
)
}
fn paint( fn paint(
gc: &mut GlobalContext, gc: &mut GlobalContext,
paint: &Paint, paint: &Paint,
@ -66,18 +80,10 @@ fn paint(
) -> (krilla::paint::Paint, u8) { ) -> (krilla::paint::Paint, u8) {
match paint { match paint {
Paint::Solid(c) => { Paint::Solid(c) => {
let components = c.to_space(ColorSpace::Srgb).to_vec4_u8(); let (c, alpha) = convert_color(c);
( (c.into(), alpha)
krilla::color::rgb::Color::new( },
components[0], Paint::Gradient(g) => convert_gradient(g, on_text, transforms),
components[1],
components[2],
)
.into(),
components[3],
)
}
Paint::Gradient(_) => (krilla::color::rgb::Color::black().into(), 255),
Paint::Pattern(p) => convert_pattern(gc, p, on_text, surface, transforms), Paint::Pattern(p) => convert_pattern(gc, p, on_text, surface, transforms),
} }
} }
@ -196,15 +202,6 @@ fn convert_gradient(
RelativeTo::Self_ => transforms.size, RelativeTo::Self_ => transforms.size,
RelativeTo::Parent => transforms.container_size, RelativeTo::Parent => transforms.container_size,
}; };
let (offset_x, offset_y) = match gradient {
Gradient::Conic(conic) => (
-size.x * (1.0 - conic.center.x.get() / 2.0) / 2.0,
-size.y * (1.0 - conic.center.y.get() / 2.0) / 2.0,
),
_ => (Abs::zero(), Abs::zero()),
};
let rotation = gradient.angle().unwrap_or_else(Angle::zero); let rotation = gradient.angle().unwrap_or_else(Angle::zero);
let transform = match gradient.unwrap_relative(on_text) { let transform = match gradient.unwrap_relative(on_text) {
@ -212,26 +209,17 @@ fn convert_gradient(
RelativeTo::Parent => transforms.container_transform, RelativeTo::Parent => transforms.container_transform,
}; };
let scale_offset = match gradient {
Gradient::Conic(_) => 4.0_f64,
_ => 1.0,
};
let pdf_gradient = PdfGradient { let pdf_gradient = PdfGradient {
aspect_ratio: size.aspect_ratio(), aspect_ratio: size.aspect_ratio(),
transform: transform transform,
.pre_concat(Transform::translate(
offset_x * scale_offset,
offset_y * scale_offset,
))
.pre_concat(Transform::scale(
Ratio::new(size.x.to_pt() * scale_offset),
Ratio::new(size.y.to_pt() * scale_offset),
)),
gradient: gradient.clone(), gradient: gradient.clone(),
angle: Gradient::correct_aspect_ratio(rotation, size.aspect_ratio()), angle: Gradient::correct_aspect_ratio(rotation, size.aspect_ratio()),
}; };
let actual_transform = transforms.transform.invert().unwrap().pre_concat(transform)
.pre_concat(Transform::scale(-Ratio::one(), Ratio::one()))
.pre_concat(Transform::rotate(-pdf_gradient.angle));
match &gradient { match &gradient {
Gradient::Linear(_) => { Gradient::Linear(_) => {
(krilla::color::rgb::Color::black().into(), 255) (krilla::color::rgb::Color::black().into(), 255)
@ -242,6 +230,29 @@ fn convert_gradient(
Gradient::Conic(conic) => { Gradient::Conic(conic) => {
// Correct the gradient's angle // Correct the gradient's angle
let angle = Gradient::correct_aspect_ratio(conic.angle, pdf_gradient.aspect_ratio); let angle = Gradient::correct_aspect_ratio(conic.angle, pdf_gradient.aspect_ratio);
let mut stops: Vec<krilla::paint::Stop<krilla::color::rgb::Color>> = vec![];
let mut add_single = |color: &Color, offset: Ratio| {
let (color, opacity) = convert_color(color);
let opacity = NormalizedF32::new((opacity as f32) / 255.0).unwrap();
let offset = NormalizedF32::new(offset.get() as f32).unwrap();
let stop = krilla::paint::Stop {
offset,
color,
opacity,
};
stops.push(stop);
};
let encode_space = conic
.space
.hue_index()
.map(|_| ColorSpace::Oklab)
.unwrap_or(conic.space);
if let Some((c, t)) = conic.stops.first() {
add_single(c, *t);
}
for window in conic.stops.windows(2) { for window in conic.stops.windows(2) {
let ((c0, t0), (c1, t1)) = (window[0], window[1]); let ((c0, t0), (c1, t1)) = (window[0], window[1]);
@ -257,24 +268,13 @@ fn convert_gradient(
} else { } else {
0.05 0.05
}; };
let encode_space = conic
.space
.hue_index()
.map(|_| ColorSpace::Oklab)
.unwrap_or(conic.space);
let mut t_x = t0.get(); let mut t_x = t0.get();
let dt = (t1.get() - t0.get()).min(max_dt); let dt = (t1.get() - t0.get()).min(max_dt);
// Special casing for sharp gradients. // Special casing for sharp gradients.
if t0 == t1 { if t0 == t1 {
write_patch( add_single(&c1, t1);
&mut vertices,
t0.get() as f32,
t1.get() as f32,
&encode_space.convert(c0),
&encode_space.convert(c1),
angle,
);
continue; continue;
} }
@ -283,33 +283,38 @@ fn convert_gradient(
// The current progress in the current window. // The current progress in the current window.
let t = |t| (t - t0.get()) / (t1.get() - t0.get()); let t = |t| (t - t0.get()) / (t1.get() - t0.get());
let c = Color::mix_iter(
[WeightedColor::new(c0, 1.0 - t(t_x)), WeightedColor::new(c1, t(t_x))],
conic.space,
)
.unwrap();
let c_next = Color::mix_iter( let c_next = Color::mix_iter(
[ [
WeightedColor::new(c0, 1.0 - t(t_next)), WeightedColor::new(c0, 1.0 - t(t_next)),
WeightedColor::new(c1, t(t_next)), WeightedColor::new(c1, t(t_next)),
], ],
conic.space, encode_space,
) )
.unwrap(); .unwrap();
write_patch( add_single(&c_next, Ratio::new(t_next));
&mut vertices,
t_x as f32,
t_next as f32,
&encode_space.convert(c),
&encode_space.convert(c_next),
angle,
);
t_x = t_next; t_x = t_next;
} }
add_single(&c1, t1);
} }
println!("{:?}, {:?}", size.x.to_f32(), size.y.to_f32());
println!("{:?}, {:?}", conic.center.x.get(), conic.center.y.get());
let sweep = krilla::paint::SweepGradient {
cx: size.x.to_f32() * conic.center.x.get() as f32,
cy: size.y.to_f32() * conic.center.y.get() as f32,
start_angle: 0.0,
end_angle: 360.0,
transform: actual_transform.as_krilla(),
spread_method: Default::default(),
stops: stops.into(),
anti_alias: gradient.anti_alias(),
};
(sweep.into(), 255)
} }
} }
} }