canvas/
peniko_conversions.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use canvas_traits::canvas::*;
6use pixels::{SnapshotAlphaMode, SnapshotPixelFormat};
7use style::color::AbsoluteColor;
8
9use crate::backend::Convert;
10use crate::canvas_data::Filter;
11
12impl Convert<peniko::Font> for fonts::FontDataAndIndex {
13    fn convert(self) -> peniko::Font {
14        use std::sync::Arc;
15        peniko::Font::new(peniko::Blob::new(Arc::new(self.data)), self.index)
16    }
17}
18
19impl Convert<kurbo::Join> for LineJoinStyle {
20    fn convert(self) -> kurbo::Join {
21        match self {
22            LineJoinStyle::Round => kurbo::Join::Round,
23            LineJoinStyle::Bevel => kurbo::Join::Bevel,
24            LineJoinStyle::Miter => kurbo::Join::Miter,
25        }
26    }
27}
28
29impl Convert<kurbo::Cap> for LineCapStyle {
30    fn convert(self) -> kurbo::Cap {
31        match self {
32            LineCapStyle::Butt => kurbo::Cap::Butt,
33            LineCapStyle::Round => kurbo::Cap::Round,
34            LineCapStyle::Square => kurbo::Cap::Square,
35        }
36    }
37}
38
39impl Convert<peniko::Color> for AbsoluteColor {
40    fn convert(self) -> peniko::Color {
41        let srgb = self.into_srgb_legacy();
42        peniko::Color::new([
43            srgb.components.0,
44            srgb.components.1,
45            srgb.components.2,
46            srgb.alpha,
47        ])
48    }
49}
50
51impl Convert<peniko::BlendMode> for CompositionOrBlending {
52    fn convert(self) -> peniko::BlendMode {
53        match self {
54            CompositionOrBlending::Composition(composition_style) => {
55                composition_style.convert().into()
56            },
57            CompositionOrBlending::Blending(blending_style) => blending_style.convert().into(),
58        }
59    }
60}
61
62impl Convert<peniko::Compose> for CompositionStyle {
63    fn convert(self) -> peniko::Compose {
64        match self {
65            CompositionStyle::SourceIn => peniko::Compose::SrcIn,
66            CompositionStyle::SourceOut => peniko::Compose::SrcOut,
67            CompositionStyle::SourceOver => peniko::Compose::SrcOver,
68            CompositionStyle::SourceAtop => peniko::Compose::SrcAtop,
69            CompositionStyle::DestinationIn => peniko::Compose::DestIn,
70            CompositionStyle::DestinationOut => peniko::Compose::DestOut,
71            CompositionStyle::DestinationOver => peniko::Compose::DestOver,
72            CompositionStyle::DestinationAtop => peniko::Compose::DestAtop,
73            CompositionStyle::Copy => peniko::Compose::Copy,
74            CompositionStyle::Lighter => peniko::Compose::Plus,
75            CompositionStyle::Xor => peniko::Compose::Xor,
76            CompositionStyle::Clear => peniko::Compose::Clear,
77        }
78    }
79}
80
81impl Convert<peniko::Mix> for BlendingStyle {
82    fn convert(self) -> peniko::Mix {
83        match self {
84            BlendingStyle::Multiply => peniko::Mix::Multiply,
85            BlendingStyle::Screen => peniko::Mix::Screen,
86            BlendingStyle::Overlay => peniko::Mix::Overlay,
87            BlendingStyle::Darken => peniko::Mix::Darken,
88            BlendingStyle::Lighten => peniko::Mix::Lighten,
89            BlendingStyle::ColorDodge => peniko::Mix::ColorDodge,
90            BlendingStyle::ColorBurn => peniko::Mix::ColorBurn,
91            BlendingStyle::HardLight => peniko::Mix::HardLight,
92            BlendingStyle::SoftLight => peniko::Mix::SoftLight,
93            BlendingStyle::Difference => peniko::Mix::Difference,
94            BlendingStyle::Exclusion => peniko::Mix::Exclusion,
95            BlendingStyle::Hue => peniko::Mix::Hue,
96            BlendingStyle::Saturation => peniko::Mix::Saturation,
97            BlendingStyle::Color => peniko::Mix::Color,
98            BlendingStyle::Luminosity => peniko::Mix::Luminosity,
99        }
100    }
101}
102
103impl Convert<kurbo::Stroke> for LineOptions {
104    fn convert(self) -> kurbo::Stroke {
105        let LineOptions {
106            width,
107            cap_style,
108            join_style,
109            miter_limit,
110            dash,
111            dash_offset,
112        } = self;
113        kurbo::Stroke {
114            width,
115            join: join_style.convert(),
116            miter_limit,
117            start_cap: cap_style.convert(),
118            end_cap: cap_style.convert(),
119            dash_pattern: dash.iter().map(|x| *x as f64).collect(),
120            dash_offset,
121        }
122    }
123}
124
125impl Convert<peniko::Brush> for FillOrStrokeStyle {
126    fn convert(self) -> peniko::Brush {
127        use canvas_traits::canvas::FillOrStrokeStyle::*;
128        match self {
129            Color(absolute_color) => peniko::Brush::Solid(absolute_color.convert()),
130            LinearGradient(style) => {
131                let start = kurbo::Point::new(style.x0, style.y0);
132                let end = kurbo::Point::new(style.x1, style.y1);
133                let mut gradient = peniko::Gradient::new_linear(start, end);
134                gradient.stops = style.stops.convert();
135                peniko::Brush::Gradient(gradient)
136            },
137            RadialGradient(style) => {
138                let center1 = kurbo::Point::new(style.x0, style.y0);
139                let center2 = kurbo::Point::new(style.x1, style.y1);
140                let mut gradient = peniko::Gradient::new_two_point_radial(
141                    center1,
142                    style.r0 as f32,
143                    center2,
144                    style.r1 as f32,
145                );
146                gradient.stops = style.stops.convert();
147                peniko::Brush::Gradient(gradient)
148            },
149            Surface(surface_style) => {
150                let data = surface_style
151                    .surface_data
152                    .to_owned()
153                    .to_vec(
154                        Some(SnapshotAlphaMode::Transparent {
155                            premultiplied: false,
156                        }),
157                        Some(SnapshotPixelFormat::RGBA),
158                    )
159                    .0;
160                peniko::Brush::Image(peniko::Image {
161                    data: peniko::Blob::from(data),
162                    format: peniko::ImageFormat::Rgba8,
163                    width: surface_style.surface_size.width,
164                    height: surface_style.surface_size.height,
165                    x_extend: if surface_style.repeat_x {
166                        peniko::Extend::Repeat
167                    } else {
168                        peniko::Extend::Pad
169                    },
170                    y_extend: if surface_style.repeat_y {
171                        peniko::Extend::Repeat
172                    } else {
173                        peniko::Extend::Pad
174                    },
175                    quality: peniko::ImageQuality::Low,
176                    alpha: 1.0,
177                })
178            },
179        }
180    }
181}
182
183impl Convert<peniko::color::DynamicColor> for AbsoluteColor {
184    fn convert(self) -> peniko::color::DynamicColor {
185        peniko::color::DynamicColor::from_alpha_color(self.convert())
186    }
187}
188
189impl Convert<peniko::ColorStop> for CanvasGradientStop {
190    fn convert(self) -> peniko::ColorStop {
191        peniko::ColorStop {
192            offset: self.offset as f32,
193            color: self.color.convert(),
194        }
195    }
196}
197
198impl Convert<peniko::ColorStops> for Vec<CanvasGradientStop> {
199    fn convert(self) -> peniko::ColorStops {
200        let mut stops = peniko::ColorStops(self.into_iter().map(|item| item.convert()).collect());
201        // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap
202        stops
203            .0
204            .sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap());
205        stops
206    }
207}
208
209impl Convert<peniko::ImageQuality> for Filter {
210    fn convert(self) -> peniko::ImageQuality {
211        match self {
212            Filter::Bilinear => peniko::ImageQuality::Medium,
213            Filter::Nearest => peniko::ImageQuality::Low,
214        }
215    }
216}
217
218impl Convert<peniko::Fill> for FillRule {
219    fn convert(self) -> peniko::Fill {
220        match self {
221            FillRule::Nonzero => peniko::Fill::NonZero,
222            FillRule::Evenodd => peniko::Fill::EvenOdd,
223        }
224    }
225}