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