/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use canvas_traits::canvas::*; use cssparser::color::clamp_unit_f32; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::Angle; use font_kit::font::Font; use log::warn; use lyon_geom::Arc; use raqote::PathOp; use style::color::AbsoluteColor; use crate::canvas_data; use crate::canvas_data::{ Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, Filter, GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, SourceSurface, StrokeOptions, }; use crate::canvas_paint_thread::AntialiasMode; pub struct RaqoteBackend; impl Backend for RaqoteBackend { fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp { CompositionOp::Raqote(opts.as_raqote().blend_mode) } fn need_to_draw_shadow(&self, color: &Color) -> bool { color.as_raqote().a != 0 } fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_>) { state.shadow_color = Color::Raqote(color.to_raqote_style()); } fn set_fill_style( &mut self, style: FillOrStrokeStyle, state: &mut CanvasPaintState<'_>, _drawtarget: &dyn GenericDrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { state.fill_style = canvas_data::Pattern::Raqote(pattern); } } fn set_stroke_style( &mut self, style: FillOrStrokeStyle, state: &mut CanvasPaintState<'_>, _drawtarget: &dyn GenericDrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { state.stroke_style = canvas_data::Pattern::Raqote(pattern); } } fn set_global_composition( &mut self, op: CompositionOrBlending, state: &mut CanvasPaintState<'_>, ) { state.draw_options.as_raqote_mut().blend_mode = op.to_raqote_style(); } fn create_drawtarget(&self, size: Size2D) -> Box { Box::new(raqote::DrawTarget::new( size.width as i32, size.height as i32, )) } fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> { CanvasPaintState::new(AntialiasMode::Default) } } impl<'a> CanvasPaintState<'a> { pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> { let pattern = Pattern::Color(255, 0, 0, 0); CanvasPaintState { draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()), fill_style: canvas_data::Pattern::Raqote(pattern.clone()), stroke_style: canvas_data::Pattern::Raqote(pattern), stroke_opts: StrokeOptions::Raqote(Default::default()), transform: Transform2D::identity(), shadow_offset_x: 0.0, shadow_offset_y: 0.0, shadow_blur: 0.0, shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), font_style: None, text_align: TextAlign::default(), text_baseline: TextBaseline::default(), } } } #[derive(Clone)] pub enum Pattern<'a> { // argb Color(u8, u8, u8, u8), LinearGradient(LinearGradientPattern), RadialGradient(RadialGradientPattern), Surface(SurfacePattern<'a>), } impl<'a> Pattern<'a> { fn set_transform(&mut self, transform: Transform2D) { match self { Pattern::Surface(pattern) => pattern.set_transform(transform), Pattern::LinearGradient(..) | Pattern::RadialGradient(..) | Pattern::Color(..) => { warn!("transform not supported") }, } } } #[derive(Clone)] pub struct LinearGradientPattern { gradient: raqote::Gradient, start: Point2D, end: Point2D, } impl LinearGradientPattern { fn new(start: Point2D, end: Point2D, stops: Vec) -> Self { LinearGradientPattern { gradient: raqote::Gradient { stops }, start, end, } } } #[derive(Clone)] pub struct RadialGradientPattern { gradient: raqote::Gradient, center1: Point2D, radius1: f32, center2: Point2D, radius2: f32, } impl RadialGradientPattern { fn new( center1: Point2D, radius1: f32, center2: Point2D, radius2: f32, stops: Vec, ) -> Self { RadialGradientPattern { gradient: raqote::Gradient { stops }, center1, radius1, center2, radius2, } } } #[derive(Clone)] pub struct SurfacePattern<'a> { image: raqote::Image<'a>, filter: raqote::FilterMode, extend: raqote::ExtendMode, repeat: Repetition, transform: Transform2D, } impl<'a> SurfacePattern<'a> { fn new(image: raqote::Image<'a>, filter: raqote::FilterMode, repeat: Repetition) -> Self { let extend = match repeat { Repetition::NoRepeat => raqote::ExtendMode::Pad, Repetition::RepeatX | Repetition::RepeatY | Repetition::Repeat => { raqote::ExtendMode::Repeat }, }; SurfacePattern { image, filter, extend, repeat, transform: Transform2D::identity(), } } fn set_transform(&mut self, transform: Transform2D) { self.transform = transform; } pub fn size(&self) -> Size2D { Size2D::new(self.image.width as f32, self.image.height as f32) } pub fn repetition(&self) -> &Repetition { &self.repeat } } #[derive(Clone)] pub enum Repetition { Repeat, RepeatX, RepeatY, NoRepeat, } impl Repetition { fn from_xy(repeat_x: bool, repeat_y: bool) -> Self { if repeat_x && repeat_y { Repetition::Repeat } else if repeat_x { Repetition::RepeatX } else if repeat_y { Repetition::RepeatY } else { Repetition::NoRepeat } } } impl canvas_data::Pattern<'_> { pub fn source(&self) -> raqote::Source { match self { canvas_data::Pattern::Raqote(pattern) => match pattern { Pattern::Color(a, r, g, b) => raqote::Source::Solid( raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), ), Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( pattern.gradient.clone(), pattern.start, pattern.end, raqote::Spread::Pad, ), Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( pattern.gradient.clone(), pattern.center1, pattern.radius1, pattern.center2, pattern.radius2, raqote::Spread::Pad, ), Pattern::Surface(pattern) => raqote::Source::Image( pattern.image, pattern.extend, pattern.filter, pattern.transform, ), }, } } pub fn is_zero_size_gradient(&self) -> bool { match self { canvas_data::Pattern::Raqote(pattern) => match pattern { Pattern::RadialGradient(pattern) => { let centers_equal = pattern.center1 == pattern.center2; let radii_equal = pattern.radius1 == pattern.radius2; (centers_equal && radii_equal) || pattern.gradient.stops.is_empty() }, Pattern::LinearGradient(pattern) => { (pattern.start == pattern.end) || pattern.gradient.stops.is_empty() }, Pattern::Color(..) | Pattern::Surface(..) => false, }, } } } impl StrokeOptions { pub fn set_line_width(&mut self, _val: f32) { match self { StrokeOptions::Raqote(options) => options.width = _val, } } pub fn set_miter_limit(&mut self, _val: f32) { match self { StrokeOptions::Raqote(options) => options.miter_limit = _val, } } pub fn set_line_join(&mut self, val: LineJoinStyle) { match self { StrokeOptions::Raqote(options) => options.join = val.to_raqote_style(), } } pub fn set_line_cap(&mut self, val: LineCapStyle) { match self { StrokeOptions::Raqote(options) => options.cap = val.to_raqote_style(), } } pub fn as_raqote(&self) -> &raqote::StrokeStyle { match self { StrokeOptions::Raqote(options) => options, } } } impl DrawOptions { pub fn set_alpha(&mut self, val: f32) { match self { DrawOptions::Raqote(draw_options) => draw_options.alpha = val, } } pub fn as_raqote(&self) -> &raqote::DrawOptions { match self { DrawOptions::Raqote(options) => options, } } fn as_raqote_mut(&mut self) -> &mut raqote::DrawOptions { match self { DrawOptions::Raqote(options) => options, } } } impl Path { pub fn transformed_copy_to_builder( &self, transform: &Transform2D, ) -> Box { Box::new(PathBuilder(Some(raqote::PathBuilder::from( self.as_raqote().clone().transform(transform), )))) } pub fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool { self.as_raqote() .clone() .transform(path_transform) .contains_point(0.1, x as f32, y as f32) } pub fn copy_to_builder(&self) -> Box { Box::new(PathBuilder(Some(raqote::PathBuilder::from( self.as_raqote().clone(), )))) } pub fn as_raqote(&self) -> &raqote::Path { match self { Path::Raqote(p) => p, } } } fn create_gradient_stops(gradient_stops: Vec) -> Vec { let mut stops = gradient_stops .into_iter() .map(|item| item.to_raqote()) .collect::>(); // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); stops } impl GenericDrawTarget for raqote::DrawTarget { fn clear_rect(&mut self, rect: &Rect) { let mut pb = raqote::PathBuilder::new(); pb.rect( rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, ); let mut options = raqote::DrawOptions::new(); options.blend_mode = raqote::BlendMode::Clear; let pattern = Pattern::Color(0, 0, 0, 0); GenericDrawTarget::fill( self, &Path::Raqote(pb.finish()), canvas_data::Pattern::Raqote(pattern), &DrawOptions::Raqote(options), ); } #[allow(unsafe_code)] fn copy_surface( &mut self, surface: SourceSurface, source: Rect, destination: Point2D, ) { let mut dt = raqote::DrawTarget::new(source.size.width, source.size.height); let data = surface.as_raqote(); let s = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) }; dt.get_data_mut().copy_from_slice(s); raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination); } // TODO(pylbrecht) // Somehow a duplicate of `create_gradient_stops()` with different types. // It feels cumbersome to convert GradientStop back and forth just to use // `create_gradient_stops()`, so I'll leave this here for now. fn create_gradient_stops(&self, gradient_stops: Vec) -> GradientStops { let mut stops = gradient_stops .into_iter() .map(|item| *item.as_raqote()) .collect::>(); // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); GradientStops::Raqote(stops) } fn create_path_builder(&self) -> Box { Box::new(PathBuilder::new()) } fn create_similar_draw_target(&self, size: &Size2D) -> Box { Box::new(raqote::DrawTarget::new(size.width, size.height)) } fn create_source_surface_from_data(&self, data: &[u8]) -> Option { Some(SourceSurface::Raqote(data.to_vec())) } #[allow(unsafe_code)] fn draw_surface( &mut self, surface: SourceSurface, dest: Rect, source: Rect, filter: Filter, draw_options: &DrawOptions, ) { let surface_data = surface.as_raqote(); let image = raqote::Image { width: source.size.width as i32, height: source.size.height as i32, data: unsafe { std::slice::from_raw_parts( surface_data.as_ptr() as *const u32, surface_data.len() / std::mem::size_of::(), ) }, }; let mut pattern = Pattern::Surface(SurfacePattern::new( image, filter.to_raqote(), Repetition::NoRepeat, )); let transform = raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32) .then_scale( image.width as f32 / dest.size.width as f32, image.height as f32 / dest.size.height as f32, ); pattern.set_transform(transform); let mut pb = raqote::PathBuilder::new(); pb.rect( dest.origin.x as f32, dest.origin.y as f32, dest.size.width as f32, dest.size.height as f32, ); GenericDrawTarget::fill( self, &Path::Raqote(pb.finish()), canvas_data::Pattern::Raqote(pattern), draw_options, ); } fn draw_surface_with_shadow( &self, _surface: SourceSurface, _dest: &Point2D, _color: &Color, _offset: &Vector2D, _sigma: f32, _operator: CompositionOp, ) { warn!("no support for drawing shadows"); } fn fill(&mut self, path: &Path, pattern: canvas_data::Pattern, draw_options: &DrawOptions) { match draw_options.as_raqote().blend_mode { raqote::BlendMode::Src => { self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); self.fill( path.as_raqote(), &pattern.source(), draw_options.as_raqote(), ); }, raqote::BlendMode::Clear | raqote::BlendMode::SrcAtop | raqote::BlendMode::DstOut | raqote::BlendMode::Add | raqote::BlendMode::Xor | raqote::BlendMode::DstOver | raqote::BlendMode::SrcOver => { self.fill( path.as_raqote(), &pattern.source(), draw_options.as_raqote(), ); }, raqote::BlendMode::SrcIn | raqote::BlendMode::SrcOut | raqote::BlendMode::DstIn | raqote::BlendMode::DstAtop => { let mut options = *draw_options.as_raqote(); self.push_layer_with_blend(1., options.blend_mode); options.blend_mode = raqote::BlendMode::SrcOver; self.fill(path.as_raqote(), &pattern.source(), &options); self.pop_layer(); }, _ => warn!( "unrecognized blend mode: {:?}", draw_options.as_raqote().blend_mode ), } } fn fill_text( &mut self, font: &Font, point_size: f32, text: &str, start: Point2D, pattern: &canvas_data::Pattern, options: &DrawOptions, ) { let mut start = pathfinder_geometry::vector::vec2f(start.x, start.y); let mut ids = Vec::new(); let mut positions = Vec::new(); for c in text.chars() { let id = match font.glyph_for_char(c) { Some(id) => id, None => { warn!("Skipping non-existent glyph {}", c); continue; }, }; ids.push(id); positions.push(Point2D::new(start.x(), start.y())); let advance = match font.advance(id) { Ok(advance) => advance, Err(e) => { warn!("Skipping glyph {} with missing advance: {:?}", c, e); continue; }, }; start += advance * point_size / 24. / 96.; } self.draw_glyphs( font, point_size, &ids, &positions, &pattern.source(), options.as_raqote(), ); } fn fill_rect( &mut self, rect: &Rect, pattern: canvas_data::Pattern, draw_options: Option<&DrawOptions>, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, ); let draw_options = if let Some(options) = draw_options { *options.as_raqote() } else { raqote::DrawOptions::new() }; GenericDrawTarget::fill( self, &Path::Raqote(pb.finish()), pattern, &DrawOptions::Raqote(draw_options), ); } fn get_size(&self) -> Size2D { Size2D::new(self.width(), self.height()) } fn get_transform(&self) -> Transform2D { *self.get_transform() } fn pop_clip(&mut self) { self.pop_clip(); } fn push_clip(&mut self, path: &Path) { self.push_clip(path.as_raqote()); } fn set_transform(&mut self, matrix: &Transform2D) { self.set_transform(matrix); } fn snapshot(&self) -> SourceSurface { SourceSurface::Raqote(self.snapshot_data_owned()) } fn stroke( &mut self, path: &Path, pattern: canvas_data::Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { self.stroke( path.as_raqote(), &pattern.source(), stroke_options.as_raqote(), draw_options.as_raqote(), ); } fn stroke_line( &mut self, start: Point2D, end: Point2D, pattern: canvas_data::Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.move_to(start.x, start.y); pb.line_to(end.x, end.y); let mut stroke_options = stroke_options.as_raqote().clone(); let cap = match stroke_options.join { raqote::LineJoin::Round => raqote::LineCap::Round, _ => raqote::LineCap::Butt, }; stroke_options.cap = cap; self.stroke( &pb.finish(), &pattern.source(), &stroke_options, draw_options.as_raqote(), ); } fn stroke_rect( &mut self, rect: &Rect, pattern: canvas_data::Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, ); self.stroke( &pb.finish(), &pattern.source(), stroke_options.as_raqote(), draw_options.as_raqote(), ); } #[allow(unsafe_code)] fn snapshot_data(&self, f: &dyn Fn(&[u8]) -> Vec) -> Vec { let v = self.get_data(); f( unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) }, ) } #[allow(unsafe_code)] fn snapshot_data_owned(&self) -> Vec { let v = self.get_data(); unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)).into() } } } impl Filter { fn to_raqote(self) -> raqote::FilterMode { match self { Filter::Bilinear => raqote::FilterMode::Bilinear, Filter::Nearest => raqote::FilterMode::Nearest, } } } struct PathBuilder(Option); impl PathBuilder { fn new() -> PathBuilder { PathBuilder(Some(raqote::PathBuilder::new())) } } impl GenericPathBuilder for PathBuilder { fn arc( &mut self, origin: Point2D, radius: f32, start_angle: f32, end_angle: f32, anticlockwise: bool, ) { self.ellipse( origin, radius, radius, 0., start_angle, end_angle, anticlockwise, ); } fn bezier_curve_to( &mut self, control_point1: &Point2D, control_point2: &Point2D, control_point3: &Point2D, ) { self.0.as_mut().unwrap().cubic_to( control_point1.x, control_point1.y, control_point2.x, control_point2.y, control_point3.x, control_point3.y, ); } fn close(&mut self) { self.0.as_mut().unwrap().close(); } fn ellipse( &mut self, origin: Point2D, radius_x: f32, radius_y: f32, rotation_angle: f32, start_angle: f32, end_angle: f32, anticlockwise: bool, ) { let mut start = Angle::radians(start_angle); let mut end = Angle::radians(end_angle); // Wrap angles mod 2 * PI if necessary if !anticlockwise && start > end + Angle::two_pi() || anticlockwise && end > start + Angle::two_pi() { start = start.positive(); end = end.positive(); } // Calculate the total arc we're going to sweep. let sweep = match anticlockwise { true => { if end - start == Angle::two_pi() { -Angle::two_pi() } else if end > start { -(Angle::two_pi() - (end - start)) } else { -(start - end) } }, false => { if start - end == Angle::two_pi() { Angle::two_pi() } else if start > end { Angle::two_pi() - (start - end) } else { end - start } }, }; let arc: Arc = Arc { center: origin, radii: Vector2D::new(radius_x, radius_y), start_angle: start, sweep_angle: sweep, x_rotation: Angle::radians(rotation_angle), }; self.line_to(arc.from()); arc.for_each_quadratic_bezier(&mut |q| { self.quadratic_curve_to(&q.ctrl, &q.to); }); } fn get_current_point(&mut self) -> Option> { let path = self.finish(); self.0 = Some(path.as_raqote().clone().into()); path.as_raqote().ops.iter().last().and_then(|op| match op { PathOp::MoveTo(point) | PathOp::LineTo(point) => Some(Point2D::new(point.x, point.y)), PathOp::CubicTo(_, _, point) => Some(Point2D::new(point.x, point.y)), PathOp::QuadTo(_, point) => Some(Point2D::new(point.x, point.y)), PathOp::Close => None, }) } fn line_to(&mut self, point: Point2D) { self.0.as_mut().unwrap().line_to(point.x, point.y); } fn move_to(&mut self, point: Point2D) { self.0.as_mut().unwrap().move_to(point.x, point.y); } fn quadratic_curve_to(&mut self, control_point: &Point2D, end_point: &Point2D) { self.0.as_mut().unwrap().quad_to( control_point.x, control_point.y, end_point.x, end_point.y, ); } fn finish(&mut self) -> Path { Path::Raqote(self.0.take().unwrap().finish()) } } pub trait ToRaqoteStyle { type Target; fn to_raqote_style(self) -> Self::Target; } impl ToRaqoteStyle for LineJoinStyle { type Target = raqote::LineJoin; fn to_raqote_style(self) -> raqote::LineJoin { match self { LineJoinStyle::Round => raqote::LineJoin::Round, LineJoinStyle::Bevel => raqote::LineJoin::Bevel, LineJoinStyle::Miter => raqote::LineJoin::Miter, } } } impl ToRaqoteStyle for LineCapStyle { type Target = raqote::LineCap; fn to_raqote_style(self) -> raqote::LineCap { match self { LineCapStyle::Butt => raqote::LineCap::Butt, LineCapStyle::Round => raqote::LineCap::Round, LineCapStyle::Square => raqote::LineCap::Square, } } } pub trait ToRaqotePattern<'a> { fn to_raqote_pattern(self) -> Option>; } pub trait ToRaqoteGradientStop { fn to_raqote(self) -> raqote::GradientStop; } impl ToRaqoteGradientStop for CanvasGradientStop { fn to_raqote(self) -> raqote::GradientStop { let srgb = self.color.into_srgb_legacy(); let color = raqote::Color::new( clamp_unit_f32(srgb.alpha), clamp_unit_f32(srgb.components.0), clamp_unit_f32(srgb.components.1), clamp_unit_f32(srgb.components.2), ); let position = self.offset as f32; raqote::GradientStop { position, color } } } impl ToRaqotePattern<'_> for FillOrStrokeStyle { #[allow(unsafe_code)] fn to_raqote_pattern(self) -> Option> { use canvas_traits::canvas::FillOrStrokeStyle::*; match self { Color(color) => { let srgb = color.into_srgb_legacy(); Some(Pattern::Color( clamp_unit_f32(srgb.alpha), clamp_unit_f32(srgb.components.0), clamp_unit_f32(srgb.components.1), clamp_unit_f32(srgb.components.2), )) }, LinearGradient(style) => { let start = Point2D::new(style.x0 as f32, style.y0 as f32); let end = Point2D::new(style.x1 as f32, style.y1 as f32); let stops = create_gradient_stops(style.stops); Some(Pattern::LinearGradient(LinearGradientPattern::new( start, end, stops, ))) }, RadialGradient(style) => { let center1 = Point2D::new(style.x0 as f32, style.y0 as f32); let center2 = Point2D::new(style.x1 as f32, style.y1 as f32); let stops = create_gradient_stops(style.stops); Some(Pattern::RadialGradient(RadialGradientPattern::new( center1, style.r0 as f32, center2, style.r1 as f32, stops, ))) }, Surface(ref style) => { let repeat = Repetition::from_xy(style.repeat_x, style.repeat_y); let data = &style.surface_data[..]; let image = raqote::Image { width: style.surface_size.width as i32, height: style.surface_size.height as i32, data: unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) }, }; Some(Pattern::Surface(SurfacePattern::new( image, raqote::FilterMode::Nearest, repeat, ))) }, } } } impl Color { fn as_raqote(&self) -> &raqote::SolidSource { match self { Color::Raqote(s) => s, } } } impl ToRaqoteStyle for AbsoluteColor { type Target = raqote::SolidSource; fn to_raqote_style(self) -> Self::Target { let srgb = self.into_srgb_legacy(); raqote::SolidSource::from_unpremultiplied_argb( clamp_unit_f32(srgb.alpha), clamp_unit_f32(srgb.components.0), clamp_unit_f32(srgb.components.1), clamp_unit_f32(srgb.components.2), ) } } impl ToRaqoteStyle for CompositionOrBlending { type Target = raqote::BlendMode; fn to_raqote_style(self) -> Self::Target { match self { CompositionOrBlending::Composition(op) => op.to_raqote_style(), CompositionOrBlending::Blending(op) => op.to_raqote_style(), } } } impl ToRaqoteStyle for BlendingStyle { type Target = raqote::BlendMode; fn to_raqote_style(self) -> Self::Target { match self { BlendingStyle::Multiply => raqote::BlendMode::Multiply, BlendingStyle::Screen => raqote::BlendMode::Screen, BlendingStyle::Overlay => raqote::BlendMode::Overlay, BlendingStyle::Darken => raqote::BlendMode::Darken, BlendingStyle::Lighten => raqote::BlendMode::Lighten, BlendingStyle::ColorDodge => raqote::BlendMode::ColorDodge, BlendingStyle::HardLight => raqote::BlendMode::HardLight, BlendingStyle::SoftLight => raqote::BlendMode::SoftLight, BlendingStyle::Difference => raqote::BlendMode::Difference, BlendingStyle::Exclusion => raqote::BlendMode::Exclusion, BlendingStyle::Hue => raqote::BlendMode::Hue, BlendingStyle::Saturation => raqote::BlendMode::Saturation, BlendingStyle::Color => raqote::BlendMode::Color, BlendingStyle::Luminosity => raqote::BlendMode::Luminosity, BlendingStyle::ColorBurn => raqote::BlendMode::ColorBurn, } } } impl ToRaqoteStyle for CompositionStyle { type Target = raqote::BlendMode; fn to_raqote_style(self) -> Self::Target { match self { CompositionStyle::SrcIn => raqote::BlendMode::SrcIn, CompositionStyle::SrcOut => raqote::BlendMode::SrcOut, CompositionStyle::SrcOver => raqote::BlendMode::SrcOver, CompositionStyle::SrcAtop => raqote::BlendMode::SrcAtop, CompositionStyle::DestIn => raqote::BlendMode::DstIn, CompositionStyle::DestOut => raqote::BlendMode::DstOut, CompositionStyle::DestOver => raqote::BlendMode::DstOver, CompositionStyle::DestAtop => raqote::BlendMode::DstAtop, CompositionStyle::Copy => raqote::BlendMode::Src, CompositionStyle::Lighter => raqote::BlendMode::Add, CompositionStyle::Xor => raqote::BlendMode::Xor, CompositionStyle::Clear => raqote::BlendMode::Clear, } } } impl SourceSurface { fn as_raqote(&self) -> &Vec { match self { SourceSurface::Raqote(s) => s, } } } impl GradientStop { fn as_raqote(&self) -> &raqote::GradientStop { match self { GradientStop::Raqote(s) => s, } } }