/* 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 crate::canvas_data::{ Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, ExtendMode, Filter, GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, Pattern, SourceSurface, StrokeOptions, SurfaceFormat, }; use crate::canvas_paint_thread::AntialiasMode; use azure::azure::{AzFloat, AzGradientStop, AzPoint}; use azure::azure_hl; use azure::azure_hl::SurfacePattern; use azure::azure_hl::{BackendType, ColorPattern, DrawTarget}; use azure::azure_hl::{CapStyle, JoinStyle}; use azure::azure_hl::{LinearGradientPattern, RadialGradientPattern}; use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; pub struct AzureBackend; impl Backend for AzureBackend { fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp { CompositionOp::Azure(opts.as_azure().composition) } fn need_to_draw_shadow(&self, color: &Color) -> bool { color.as_azure().a != 0.0f32 } fn size_from_pattern(&self, rect: &Rect, pattern: &Pattern) -> Option> { match pattern { Pattern::Azure(azure_hl::Pattern::Surface(ref surface)) => { let surface_size = surface.size(); let size = match (surface.repeat_x, surface.repeat_y) { (true, true) => rect.size, (true, false) => Size2D::new(rect.size.width, surface_size.height as f32), (false, true) => Size2D::new(surface_size.width as f32, rect.size.height), (false, false) => { Size2D::new(surface_size.width as f32, surface_size.height as f32) }, }; Some(size) }, Pattern::Azure(_) => None, } } fn set_shadow_color<'a>(&mut self, color: RGBA, state: &mut CanvasPaintState<'a>) { state.shadow_color = Color::Azure(color.to_azure_style()); } fn set_fill_style<'a>( &mut self, style: FillOrStrokeStyle, state: &mut CanvasPaintState<'a>, drawtarget: &dyn GenericDrawTarget, ) { if let Some(pattern) = style.to_azure_pattern(drawtarget) { state.fill_style = Pattern::Azure(pattern) } } fn set_stroke_style<'a>( &mut self, style: FillOrStrokeStyle, state: &mut CanvasPaintState<'a>, drawtarget: &dyn GenericDrawTarget, ) { if let Some(pattern) = style.to_azure_pattern(drawtarget) { state.stroke_style = Pattern::Azure(pattern) } } fn set_global_composition<'a>( &mut self, op: CompositionOrBlending, state: &mut CanvasPaintState<'a>, ) { state .draw_options .as_azure_mut() .set_composition_op(op.to_azure_style()); } fn create_drawtarget(&self, size: Size2D) -> Box { // FIXME(nox): Why is the size made of i32 values? Box::new(DrawTarget::new( BackendType::Skia, size.to_i32(), azure_hl::SurfaceFormat::B8G8R8A8, )) } fn recreate_paint_state<'a>(&self, state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> { CanvasPaintState::new(AntialiasMode::from_azure( state.draw_options.as_azure().antialias, )) } } impl<'a> CanvasPaintState<'a> { pub fn new(antialias: AntialiasMode) -> CanvasPaintState<'a> { CanvasPaintState { draw_options: DrawOptions::Azure(azure_hl::DrawOptions::new( 1.0, azure_hl::CompositionOp::Over, antialias.into_azure(), )), fill_style: Pattern::Azure(azure_hl::Pattern::Color(ColorPattern::new( azure_hl::Color::black(), ))), stroke_style: Pattern::Azure(azure_hl::Pattern::Color(ColorPattern::new( azure_hl::Color::black(), ))), stroke_opts: StrokeOptions::Azure(azure_hl::StrokeOptions::new( 1.0, JoinStyle::MiterOrBevel, CapStyle::Butt, 10.0, &[], )), transform: Transform2D::identity(), shadow_offset_x: 0.0, shadow_offset_y: 0.0, shadow_blur: 0.0, shadow_color: Color::Azure(azure_hl::Color::transparent()), } } } impl GenericPathBuilder for azure_hl::PathBuilder { fn arc( &self, origin: Point2D, radius: f32, start_angle: f32, end_angle: f32, anticlockwise: bool, ) { self.arc( origin as Point2D, radius as AzFloat, start_angle as AzFloat, end_angle as AzFloat, anticlockwise, ); } fn bezier_curve_to( &self, control_point1: &Point2D, control_point2: &Point2D, control_point3: &Point2D, ) { self.bezier_curve_to( control_point1 as &Point2D, control_point2 as &Point2D, control_point3 as &Point2D, ); } fn close(&self) { self.close(); } fn ellipse( &self, origin: Point2D, radius_x: f32, radius_y: f32, rotation_angle: f32, start_angle: f32, end_angle: f32, anticlockwise: bool, ) { self.ellipse( origin as Point2D, radius_x as AzFloat, radius_y as AzFloat, rotation_angle as AzFloat, start_angle as AzFloat, end_angle as AzFloat, anticlockwise, ); } fn get_current_point(&self) -> Point2D { let AzPoint { x, y } = self.get_current_point(); Point2D::new(x as f32, y as f32) } fn line_to(&self, point: Point2D) { self.line_to(point as Point2D); } fn move_to(&self, point: Point2D) { self.move_to(point as Point2D); } fn quadratic_curve_to(&self, control_point: &Point2D, end_point: &Point2D) { self.quadratic_curve_to( control_point as &Point2D, end_point as &Point2D, ); } fn finish(&self) -> Path { Path::Azure(self.finish()) } } impl GenericDrawTarget for azure_hl::DrawTarget { fn clear_rect(&self, rect: &Rect) { self.clear_rect(rect as &Rect); } fn copy_surface(&self, surface: SourceSurface, source: Rect, destination: Point2D) { self.copy_surface(surface.into_azure(), source, destination); } fn create_gradient_stops( &self, gradient_stops: Vec, extend_mode: ExtendMode, ) -> GradientStops { let gradient_stops: Vec = gradient_stops.into_iter().map(|x| x.into_azure()).collect(); GradientStops::Azure(self.create_gradient_stops(&gradient_stops, extend_mode.into_azure())) } fn create_path_builder(&self) -> Box { Box::new(self.create_path_builder()) } fn create_similar_draw_target( &self, size: &Size2D, format: SurfaceFormat, ) -> Box { Box::new(self.create_similar_draw_target(size, format.into_azure())) } fn create_source_surface_from_data( &self, data: &[u8], size: Size2D, stride: i32, ) -> Option { self.create_source_surface_from_data(data, size, stride, azure_hl::SurfaceFormat::B8G8R8A8) .map(|s| SourceSurface::Azure(s)) } fn draw_surface( &self, surface: SourceSurface, dest: Rect, source: Rect, filter: Filter, draw_options: &DrawOptions, ) { let surf_options = azure_hl::DrawSurfaceOptions::new(filter.as_azure(), true); let draw_options = azure_hl::DrawOptions::new( draw_options.as_azure().alpha, draw_options.as_azure().composition, azure_hl::AntialiasMode::None, ); self.draw_surface( surface.into_azure(), dest.to_azure_style(), source.to_azure_style(), surf_options, draw_options, ); } fn draw_surface_with_shadow( &self, surface: SourceSurface, dest: &Point2D, color: &Color, offset: &Vector2D, sigma: f32, operator: CompositionOp, ) { self.draw_surface_with_shadow( surface.into_azure(), dest as &Point2D, color.as_azure(), offset as &Vector2D, sigma as AzFloat, operator.into_azure(), ); } fn fill(&self, path: &Path, pattern: Pattern, draw_options: &DrawOptions) { self.fill( path.as_azure(), pattern.as_azure().to_pattern_ref(), draw_options.as_azure(), ); } fn fill_rect(&self, rect: &Rect, pattern: Pattern, draw_options: Option<&DrawOptions>) { self.fill_rect( rect as &Rect, pattern.as_azure().to_pattern_ref(), draw_options.map(|x| x.as_azure()), ); } fn get_format(&self) -> SurfaceFormat { SurfaceFormat::Azure(self.get_format()) } fn get_size(&self) -> Size2D { let size = self.get_size(); Size2D::new(size.width, size.height) } fn get_transform(&self) -> Transform2D { self.get_transform() as Transform2D } fn pop_clip(&self) { self.pop_clip(); } fn push_clip(&self, path: &Path) { self.push_clip(path.as_azure()); } fn set_transform(&self, matrix: &Transform2D) { self.set_transform(matrix as &Transform2D); } fn snapshot(&self) -> SourceSurface { SourceSurface::Azure(self.snapshot()) } fn stroke( &self, path: &Path, pattern: Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { self.stroke( path.as_azure(), pattern.as_azure().to_pattern_ref(), stroke_options.as_azure(), draw_options.as_azure(), ); } fn stroke_line( &self, start: Point2D, end: Point2D, pattern: Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { let stroke_options = stroke_options.as_azure(); let cap = match stroke_options.line_join { JoinStyle::Round => CapStyle::Round, _ => CapStyle::Butt, }; let stroke_opts = azure_hl::StrokeOptions::new( stroke_options.line_width, stroke_options.line_join, cap, stroke_options.miter_limit, stroke_options.mDashPattern, ); self.stroke_line( start, end, pattern.as_azure().to_pattern_ref(), &stroke_opts, draw_options.as_azure(), ); } fn stroke_rect( &self, rect: &Rect, pattern: Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { self.stroke_rect( rect as &Rect, pattern.as_azure().to_pattern_ref(), stroke_options.as_azure(), draw_options.as_azure(), ); } #[allow(unsafe_code)] fn snapshot_data(&self, f: &dyn Fn(&[u8]) -> Vec) -> Vec { unsafe { f(self.snapshot().get_data_surface().data()) } } #[allow(unsafe_code)] fn snapshot_data_owned(&self) -> Vec { unsafe { self.snapshot().get_data_surface().data().into() } } } impl AntialiasMode { fn into_azure(self) -> azure_hl::AntialiasMode { match self { AntialiasMode::Default => azure_hl::AntialiasMode::Default, AntialiasMode::None => azure_hl::AntialiasMode::None, } } fn from_azure(val: azure_hl::AntialiasMode) -> AntialiasMode { match val { azure_hl::AntialiasMode::Default => AntialiasMode::Default, azure_hl::AntialiasMode::None => AntialiasMode::None, v => unimplemented!("{:?} is unsupported", v), } } } impl ExtendMode { fn into_azure(self) -> azure_hl::ExtendMode { match self { ExtendMode::Azure(m) => m, } } } impl GradientStop { fn into_azure(self) -> AzGradientStop { match self { GradientStop::Azure(s) => s, } } } impl GradientStops { fn into_azure(self) -> azure_hl::GradientStops { match self { GradientStops::Azure(s) => s, } } } impl Color { fn as_azure(&self) -> &azure_hl::Color { match self { Color::Azure(s) => s, } } } impl CompositionOp { fn into_azure(self) -> azure_hl::CompositionOp { match self { CompositionOp::Azure(s) => s, } } } impl SurfaceFormat { fn into_azure(self) -> azure_hl::SurfaceFormat { match self { SurfaceFormat::Azure(s) => s, } } } impl SourceSurface { fn into_azure(self) -> azure_hl::SourceSurface { match self { SourceSurface::Azure(s) => s, } } } impl Path { fn as_azure(&self) -> &azure_hl::Path { match self { Path::Azure(p) => p, } } } impl Pattern { fn as_azure(&self) -> &azure_hl::Pattern { match self { Pattern::Azure(p) => p, } } } impl DrawOptions { fn as_azure(&self) -> &azure_hl::DrawOptions { match self { DrawOptions::Azure(options) => options, } } fn as_azure_mut(&mut self) -> &mut azure_hl::DrawOptions { match self { DrawOptions::Azure(options) => options, } } pub fn set_alpha(&mut self, val: f32) { match self { DrawOptions::Azure(options) => options.alpha = val as AzFloat, } } } impl<'a> StrokeOptions<'a> { pub fn as_azure(&self) -> &azure_hl::StrokeOptions<'a> { match self { StrokeOptions::Azure(options) => options, } } pub fn set_line_width(&mut self, val: f32) { match self { StrokeOptions::Azure(options) => options.line_width = val as AzFloat, } } pub fn set_miter_limit(&mut self, val: f32) { match self { StrokeOptions::Azure(options) => options.miter_limit = val as AzFloat, } } pub fn set_line_join(&mut self, val: LineJoinStyle) { match self { StrokeOptions::Azure(options) => options.line_join = val.to_azure_style(), } } pub fn set_line_cap(&mut self, val: LineCapStyle) { match self { StrokeOptions::Azure(options) => options.line_cap = val.to_azure_style(), } } } pub trait ToAzureStyle { type Target; fn to_azure_style(self) -> Self::Target; } impl ToAzureStyle for Rect { type Target = Rect; fn to_azure_style(self) -> Rect { Rect::new( Point2D::new(self.origin.x as f32, self.origin.y as f32), Size2D::new(self.size.width as f32, self.size.height as f32), ) } } impl ToAzureStyle for LineCapStyle { type Target = CapStyle; fn to_azure_style(self) -> CapStyle { match self { LineCapStyle::Butt => CapStyle::Butt, LineCapStyle::Round => CapStyle::Round, LineCapStyle::Square => CapStyle::Square, } } } impl ToAzureStyle for LineJoinStyle { type Target = JoinStyle; fn to_azure_style(self) -> JoinStyle { match self { LineJoinStyle::Round => JoinStyle::Round, LineJoinStyle::Bevel => JoinStyle::Bevel, LineJoinStyle::Miter => JoinStyle::Miter, } } } impl ToAzureStyle for CompositionStyle { type Target = azure_hl::CompositionOp; fn to_azure_style(self) -> azure_hl::CompositionOp { match self { CompositionStyle::SrcIn => azure_hl::CompositionOp::In, CompositionStyle::SrcOut => azure_hl::CompositionOp::Out, CompositionStyle::SrcOver => azure_hl::CompositionOp::Over, CompositionStyle::SrcAtop => azure_hl::CompositionOp::Atop, CompositionStyle::DestIn => azure_hl::CompositionOp::DestIn, CompositionStyle::DestOut => azure_hl::CompositionOp::DestOut, CompositionStyle::DestOver => azure_hl::CompositionOp::DestOver, CompositionStyle::DestAtop => azure_hl::CompositionOp::DestAtop, CompositionStyle::Copy => azure_hl::CompositionOp::Source, CompositionStyle::Lighter => azure_hl::CompositionOp::Add, CompositionStyle::Xor => azure_hl::CompositionOp::Xor, } } } impl ToAzureStyle for BlendingStyle { type Target = azure_hl::CompositionOp; fn to_azure_style(self) -> azure_hl::CompositionOp { match self { BlendingStyle::Multiply => azure_hl::CompositionOp::Multiply, BlendingStyle::Screen => azure_hl::CompositionOp::Screen, BlendingStyle::Overlay => azure_hl::CompositionOp::Overlay, BlendingStyle::Darken => azure_hl::CompositionOp::Darken, BlendingStyle::Lighten => azure_hl::CompositionOp::Lighten, BlendingStyle::ColorDodge => azure_hl::CompositionOp::ColorDodge, BlendingStyle::ColorBurn => azure_hl::CompositionOp::ColorBurn, BlendingStyle::HardLight => azure_hl::CompositionOp::HardLight, BlendingStyle::SoftLight => azure_hl::CompositionOp::SoftLight, BlendingStyle::Difference => azure_hl::CompositionOp::Difference, BlendingStyle::Exclusion => azure_hl::CompositionOp::Exclusion, BlendingStyle::Hue => azure_hl::CompositionOp::Hue, BlendingStyle::Saturation => azure_hl::CompositionOp::Saturation, BlendingStyle::Color => azure_hl::CompositionOp::Color, BlendingStyle::Luminosity => azure_hl::CompositionOp::Luminosity, } } } impl ToAzureStyle for CompositionOrBlending { type Target = azure_hl::CompositionOp; fn to_azure_style(self) -> azure_hl::CompositionOp { match self { CompositionOrBlending::Composition(op) => op.to_azure_style(), CompositionOrBlending::Blending(op) => op.to_azure_style(), } } } pub trait ToAzurePattern { fn to_azure_pattern(&self, drawtarget: &dyn GenericDrawTarget) -> Option; } impl ToAzurePattern for FillOrStrokeStyle { fn to_azure_pattern(&self, drawtarget: &dyn GenericDrawTarget) -> Option { Some(match *self { FillOrStrokeStyle::Color(ref color) => { azure_hl::Pattern::Color(ColorPattern::new(color.to_azure_style())) }, FillOrStrokeStyle::LinearGradient(ref linear_gradient_style) => { let gradient_stops: Vec = linear_gradient_style .stops .iter() .map(|s| { GradientStop::Azure(azure_hl::GradientStop { offset: s.offset as f32, color: s.color.to_azure_style(), }) }) .collect(); azure_hl::Pattern::LinearGradient(LinearGradientPattern::new( &Point2D::new( linear_gradient_style.x0 as f32, linear_gradient_style.y0 as f32, ), &Point2D::new( linear_gradient_style.x1 as f32, linear_gradient_style.y1 as f32, ), drawtarget .create_gradient_stops( gradient_stops, ExtendMode::Azure(azure_hl::ExtendMode::Clamp), ) .into_azure(), &Transform2D::identity(), )) }, FillOrStrokeStyle::RadialGradient(ref radial_gradient_style) => { let gradient_stops: Vec = radial_gradient_style .stops .iter() .map(|s| { GradientStop::Azure(azure_hl::GradientStop { offset: s.offset as f32, color: s.color.to_azure_style(), }) }) .collect(); azure_hl::Pattern::RadialGradient(RadialGradientPattern::new( &Point2D::new( radial_gradient_style.x0 as f32, radial_gradient_style.y0 as f32, ), &Point2D::new( radial_gradient_style.x1 as f32, radial_gradient_style.y1 as f32, ), radial_gradient_style.r0 as f32, radial_gradient_style.r1 as f32, drawtarget .create_gradient_stops( gradient_stops, ExtendMode::Azure(azure_hl::ExtendMode::Clamp), ) .into_azure(), &Transform2D::identity(), )) }, FillOrStrokeStyle::Surface(ref surface_style) => { let source_surface = drawtarget .create_source_surface_from_data( &surface_style.surface_data, // FIXME(nox): Why are those i32 values? surface_style.surface_size.to_i32(), surface_style.surface_size.width as i32 * 4, )? .into_azure(); azure_hl::Pattern::Surface(SurfacePattern::new( source_surface.azure_source_surface, surface_style.repeat_x, surface_style.repeat_y, &Transform2D::identity(), )) }, }) } } impl ToAzureStyle for RGBA { type Target = azure_hl::Color; fn to_azure_style(self) -> azure_hl::Color { azure_hl::Color::rgba( self.red_f32() as f32, self.green_f32() as f32, self.blue_f32() as f32, self.alpha_f32() as f32, ) } } impl Pattern { pub fn is_zero_size_gradient(&self) -> bool { match *self { Pattern::Azure(azure_hl::Pattern::LinearGradient(ref gradient)) => { gradient.is_zero_size() }, _ => false, } } } impl Filter { fn as_azure(&self) -> azure_hl::Filter { match *self { Filter::Linear => azure_hl::Filter::Linear, Filter::Point => azure_hl::Filter::Point, } } } impl Path { pub fn transformed_copy_to_builder( &self, transform: &Transform2D, ) -> Box { Box::new(self.as_azure().transformed_copy_to_builder(transform)) } pub fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool { self.as_azure().contains_point(x, y, path_transform) } pub fn copy_to_builder(&self) -> Box { Box::new(self.as_azure().copy_to_builder()) } }