diff options
author | Josh Matthews <josh@joshmatthews.net> | 2019-05-29 14:50:45 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2019-06-03 00:12:01 -0400 |
commit | 3243f1753bca71c5b14d9f43c34aad51e59a01a4 (patch) | |
tree | b78885795bbd59bdd30fefcfc48a71211d83472c /components/canvas/azure_backend.rs | |
parent | 42bf1984be6498517e98b7942633ec0f57adaab1 (diff) | |
download | servo-3243f1753bca71c5b14d9f43c34aad51e59a01a4.tar.gz servo-3243f1753bca71c5b14d9f43c34aad51e59a01a4.zip |
Add cargo feature to control canvas backend.
Diffstat (limited to 'components/canvas/azure_backend.rs')
-rw-r--r-- | components/canvas/azure_backend.rs | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/components/canvas/azure_backend.rs b/components/canvas/azure_backend.rs new file mode 100644 index 00000000000..8727751d5a6 --- /dev/null +++ b/components/canvas/azure_backend.rs @@ -0,0 +1,744 @@ +/* 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 cssparser::RGBA; +use crate::canvas_data::{ + Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, GenericDrawTarget, + GenericPathBuilder, Path, Pattern, StrokeOptions, +}; +use crate::canvas_paint_thread::AntialiasMode; +use azure::azure::{AzFloat, AzGradientStop, AzIntSize, AzPoint}; +use azure::azure_hl; +use azure::azure_hl::SurfacePattern; +use azure::azure_hl::{AsAzurePoint, CapStyle, JoinStyle}; +use azure::azure_hl::{BackendType, ColorPattern, DrawTarget}; +use azure::azure_hl::{LinearGradientPattern, RadialGradientPattern}; +use euclid::{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 { + self.state.shadow_color.as_azure().a != 0.0f32 + } + + fn size_from_pattern(&self, rect: &Rect<f32>, pattern: &Pattern) -> Option<Size2D<f32>> { + 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: &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: &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<u64>) -> Box<GenericDrawTarget> { + // 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( + self.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<f32>, + radius: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ) { + self.arc( + origin as Point2D<AzFloat>, + radius as AzFloat, + start_angle as AzFloat, + end_angle as AzFloat, + anticlockwise, + ); + } + fn bezier_curve_to( + &self, + control_point1: &Point2D<f32>, + control_point2: &Point2D<f32>, + control_point3: &Point2D<f32>, + ) { + self.bezier_curve_to( + control_point1 as &Point2D<AzFloat>, + control_point2 as &Point2D<AzFloat>, + control_point3 as &Point2D<AzFloat>, + ); + } + fn close(&self) { + self.close(); + } + fn ellipse( + &self, + origin: Point2D<f32>, + radius_x: f32, + radius_y: f32, + rotation_angle: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ) { + self.ellipse( + origin as Point2D<AzFloat>, + 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<f32> { + let AzPoint { x, y } = self.get_current_point(); + Point2D::new(x as f32, y as f32) + } + fn line_to(&self, point: Point2D<f32>) { + self.line_to(point as Point2D<AzFloat>); + } + fn move_to(&self, point: Point2D<f32>) { + self.move_to(point as Point2D<AzFloat>); + } + fn quadratic_curve_to(&self, control_point: &Point2D<f32>, end_point: &Point2D<f32>) { + self.quadratic_curve_to( + control_point as &Point2D<AzFloat>, + end_point as &Point2D<AzFloat>, + ); + } + fn finish(&self) -> Path { + Path::Azure(self.finish()) + } +} + +impl GenericDrawTarget for azure_hl::DrawTarget { + fn clear_rect(&self, rect: &Rect<f32>) { + self.clear_rect(rect as &Rect<AzFloat>); + } + + fn copy_surface(&self, surface: SourceSurface, source: Rect<i32>, destination: Point2D<i32>) { + self.copy_surface(surface.into_azure(), source, destination); + } + + fn create_gradient_stops( + &self, + gradient_stops: Vec<GradientStop>, + extend_mode: ExtendMode, + ) -> GradientStops { + let gradient_stops: Vec<AzGradientStop> = + 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<GenericPathBuilder> { + Box::new(self.create_path_builder()) + } + + fn create_similar_draw_target( + &self, + size: &Size2D<i32>, + format: SurfaceFormat, + ) -> Box<GenericDrawTarget> { + Box::new(self.create_similar_draw_target(size, format.into_azure())) + } + fn create_source_surface_from_data( + &self, + data: &[u8], + size: Size2D<i32>, + stride: i32, + ) -> Option<SourceSurface> { + 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<f64>, + source: Rect<f64>, + 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.into_azure(), + azure_hl::AntialiasMode::None + ); + self.draw_surface( + surface.into_azure(), + dest as Rect<AzFloat>, + source as Rect<AzFloat>, + surf_options.into_azure(), + options.into_azure(), + ); + } + fn draw_surface_with_shadow( + &self, + surface: SourceSurface, + dest: &Point2D<f32>, + color: &Color, + offset: &Vector2D<f32>, + sigma: f32, + operator: CompositionOp, + ) { + self.draw_surface_with_shadow( + surface.into_azure(), + dest as &Point2D<AzFloat>, + color.as_azure(), + offset as &Vector2D<AzFloat>, + 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<f32>, pattern: Pattern, draw_options: Option<&DrawOptions>) { + self.fill_rect( + rect as &Rect<AzFloat>, + 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<i32> { + let size = self.get_size(); + Size2D::new(size.width, size.height) + } + fn get_transform(&self) -> Transform2D<f32> { + self.get_transform() as Transform2D<f32> + } + 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<f32>) { + self.set_transform(matrix as &Transform2D<AzFloat>); + } + 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<f32>, + end: Point2D<f32>, + 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_options.as_azure(), + draw_options.as_azure(), + ); + } + fn stroke_rect( + &self, + rect: &Rect<f32>, + pattern: Pattern, + stroke_options: &StrokeOptions, + draw_options: &DrawOptions, + ) { + self.stroke_rect( + rect as &Rect<AzFloat>, + pattern.as_azure().to_pattern_ref(), + stroke_options.as_azure(), + draw_options.as_azure(), + ); + } + + fn snapshot_data(&self) -> &[u8] { + unsafe { + self.snapshot().get_data_surface().data() + } + } +} + +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) { + match val { + azure_hl::AntialiasMode::Default => AntialiasMode::Default, + azure_hl::AntialiasMode::None => AntialiasMode::None, + } + } +} + +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 IntSize { + fn into_azure(self) -> AzIntSize { + match self { + IntSize::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 DrawSurfaceOptions { + fn into_azure(self) -> azure_hl::DrawSurfaceOptions { + match self { + DrawSurfaceOptions::Azure(options) => options, + } + } +} + +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, + } + } + fn into_azure(self) -> 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<f64> { + type Target = Rect<f32>; + + fn to_azure_style(self) -> Rect<f32> { + 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: &GenericDrawTarget) -> Option<azure_hl::Pattern>; +} + +impl ToAzurePattern for FillOrStrokeStyle { + fn to_azure_pattern(&self, drawtarget: &GenericDrawTarget) -> Option<azure_hl::Pattern> { + 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<GradientStop> = 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<GradientStop> = 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 az_pattern)) => { + 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<f32>) -> Box<GenericPathBuilder> { + Box::new(self.as_azure().transformed_copy_to_builder(transform)) + } + + pub fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> bool { + self.as_azure().contains_point(x, y, path_transform) + } + + pub fn copy_to_builder(&self) -> Box<GenericPathBuilder> { + self.as_azure().copy_to_builder() + } +} |