aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/webrender_helpers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/webrender_helpers.rs')
-rw-r--r--components/layout/webrender_helpers.rs481
1 files changed, 481 insertions, 0 deletions
diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs
new file mode 100644
index 00000000000..a1ab8fac3fe
--- /dev/null
+++ b/components/layout/webrender_helpers.rs
@@ -0,0 +1,481 @@
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+// TODO(gw): This contains helper traits and implementations for converting Servo display lists
+// into WebRender display lists. In the future, this step should be completely removed.
+// This might be achieved by sharing types between WR and Servo display lists, or
+// completely converting layout to directly generate WebRender display lists, for example.
+
+use app_units::Au;
+use azure::azure_hl::Color;
+use euclid::num::Zero;
+use euclid::{Point2D, Rect, Size2D};
+use gfx::display_list::{BorderRadii, BoxShadowClipMode, ClippingRegion};
+use gfx::display_list::{DisplayItem, DisplayList};
+use gfx::display_list::{GradientStop, StackingContext};
+use gfx_traits::ScrollPolicy;
+use msg::constellation_msg::ConvertPipelineIdToWebRender;
+use style::computed_values::filter::{self, Filter};
+use style::computed_values::{image_rendering, mix_blend_mode};
+use style::values::computed::BorderStyle;
+use webrender_traits;
+
+pub trait WebRenderStackingContextConverter {
+ fn convert_to_webrender(&self,
+ api: &webrender_traits::RenderApi,
+ pipeline_id: webrender_traits::PipelineId,
+ epoch: webrender_traits::Epoch,
+ scroll_layer_id: Option<webrender_traits::ScrollLayerId>)
+ -> webrender_traits::StackingContextId;
+}
+
+trait WebRenderDisplayListConverter {
+ fn convert_to_webrender(&self,
+ api: &webrender_traits::RenderApi,
+ pipeline_id: webrender_traits::PipelineId,
+ epoch: webrender_traits::Epoch) -> webrender_traits::DisplayListBuilder;
+}
+
+trait WebRenderDisplayItemConverter {
+ fn convert_to_webrender(&self,
+ api: &webrender_traits::RenderApi,
+ pipeline_id: webrender_traits::PipelineId,
+ epoch: webrender_traits::Epoch,
+ level: webrender_traits::StackingLevel,
+ builder: &mut webrender_traits::DisplayListBuilder);
+}
+
+trait ToBorderStyle {
+ fn to_border_style(&self) -> webrender_traits::BorderStyle;
+}
+
+impl ToBorderStyle for BorderStyle {
+ fn to_border_style(&self) -> webrender_traits::BorderStyle {
+ match *self {
+ BorderStyle::none => webrender_traits::BorderStyle::None,
+ BorderStyle::solid => webrender_traits::BorderStyle::Solid,
+ BorderStyle::double => webrender_traits::BorderStyle::Double,
+ BorderStyle::dotted => webrender_traits::BorderStyle::Dotted,
+ BorderStyle::dashed => webrender_traits::BorderStyle::Dashed,
+ BorderStyle::hidden => webrender_traits::BorderStyle::Hidden,
+ BorderStyle::groove => webrender_traits::BorderStyle::Groove,
+ BorderStyle::ridge => webrender_traits::BorderStyle::Ridge,
+ BorderStyle::inset => webrender_traits::BorderStyle::Inset,
+ BorderStyle::outset => webrender_traits::BorderStyle::Outset,
+ }
+ }
+}
+
+trait ToBoxShadowClipMode {
+ fn to_clip_mode(&self) -> webrender_traits::BoxShadowClipMode;
+}
+
+impl ToBoxShadowClipMode for BoxShadowClipMode {
+ fn to_clip_mode(&self) -> webrender_traits::BoxShadowClipMode {
+ match *self {
+ BoxShadowClipMode::None => webrender_traits::BoxShadowClipMode::None,
+ BoxShadowClipMode::Inset => webrender_traits::BoxShadowClipMode::Inset,
+ BoxShadowClipMode::Outset => webrender_traits::BoxShadowClipMode::Outset,
+ }
+ }
+}
+
+trait ToSizeF {
+ fn to_sizef(&self) -> Size2D<f32>;
+}
+
+trait ToPointF {
+ fn to_pointf(&self) -> Point2D<f32>;
+}
+
+impl ToPointF for Point2D<Au> {
+ fn to_pointf(&self) -> Point2D<f32> {
+ Point2D::new(self.x.to_f32_px(), self.y.to_f32_px())
+ }
+}
+
+impl ToSizeF for Size2D<Au> {
+ fn to_sizef(&self) -> Size2D<f32> {
+ Size2D::new(self.width.to_f32_px(), self.height.to_f32_px())
+ }
+}
+
+trait ToRectF {
+ fn to_rectf(&self) -> Rect<f32>;
+}
+
+impl ToRectF for Rect<Au> {
+ fn to_rectf(&self) -> Rect<f32> {
+ let x = self.origin.x.to_f32_px();
+ let y = self.origin.y.to_f32_px();
+ let w = self.size.width.to_f32_px();
+ let h = self.size.height.to_f32_px();
+ Rect::new(Point2D::new(x, y), Size2D::new(w, h))
+ }
+}
+
+trait ToColorF {
+ fn to_colorf(&self) -> webrender_traits::ColorF;
+}
+
+impl ToColorF for Color {
+ fn to_colorf(&self) -> webrender_traits::ColorF {
+ webrender_traits::ColorF::new(self.r, self.g, self.b, self.a)
+ }
+}
+
+trait ToGradientStop {
+ fn to_gradient_stop(&self) -> webrender_traits::GradientStop;
+}
+
+impl ToGradientStop for GradientStop {
+ fn to_gradient_stop(&self) -> webrender_traits::GradientStop {
+ webrender_traits::GradientStop {
+ offset: self.offset,
+ color: self.color.to_colorf(),
+ }
+ }
+}
+
+trait ToClipRegion {
+ fn to_clip_region(&self) -> webrender_traits::ClipRegion;
+}
+
+impl ToClipRegion for ClippingRegion {
+ fn to_clip_region(&self) -> webrender_traits::ClipRegion {
+ webrender_traits::ClipRegion::new(self.main.to_rectf(),
+ self.complex.iter().map(|complex_clipping_region| {
+ webrender_traits::ComplexClipRegion::new(
+ complex_clipping_region.rect.to_rectf(),
+ complex_clipping_region.radii.to_border_radius(),
+ )
+ }).collect())
+ }
+}
+
+trait ToBorderRadius {
+ fn to_border_radius(&self) -> webrender_traits::BorderRadius;
+}
+
+impl ToBorderRadius for BorderRadii<Au> {
+ fn to_border_radius(&self) -> webrender_traits::BorderRadius {
+ webrender_traits::BorderRadius {
+ top_left: self.top_left.to_sizef(),
+ top_right: self.top_right.to_sizef(),
+ bottom_left: self.bottom_left.to_sizef(),
+ bottom_right: self.bottom_right.to_sizef(),
+ }
+ }
+}
+
+trait ToBlendMode {
+ fn to_blend_mode(&self) -> webrender_traits::MixBlendMode;
+}
+
+impl ToBlendMode for mix_blend_mode::T {
+ fn to_blend_mode(&self) -> webrender_traits::MixBlendMode {
+ match *self {
+ mix_blend_mode::T::normal => webrender_traits::MixBlendMode::Normal,
+ mix_blend_mode::T::multiply => webrender_traits::MixBlendMode::Multiply,
+ mix_blend_mode::T::screen => webrender_traits::MixBlendMode::Screen,
+ mix_blend_mode::T::overlay => webrender_traits::MixBlendMode::Overlay,
+ mix_blend_mode::T::darken => webrender_traits::MixBlendMode::Darken,
+ mix_blend_mode::T::lighten => webrender_traits::MixBlendMode::Lighten,
+ mix_blend_mode::T::color_dodge => webrender_traits::MixBlendMode::ColorDodge,
+ mix_blend_mode::T::color_burn => webrender_traits::MixBlendMode::ColorBurn,
+ mix_blend_mode::T::hard_light => webrender_traits::MixBlendMode::HardLight,
+ mix_blend_mode::T::soft_light => webrender_traits::MixBlendMode::SoftLight,
+ mix_blend_mode::T::difference => webrender_traits::MixBlendMode::Difference,
+ mix_blend_mode::T::exclusion => webrender_traits::MixBlendMode::Exclusion,
+ mix_blend_mode::T::hue => webrender_traits::MixBlendMode::Hue,
+ mix_blend_mode::T::saturation => webrender_traits::MixBlendMode::Saturation,
+ mix_blend_mode::T::color => webrender_traits::MixBlendMode::Color,
+ mix_blend_mode::T::luminosity => webrender_traits::MixBlendMode::Luminosity,
+ }
+ }
+}
+
+trait ToImageRendering {
+ fn to_image_rendering(&self) -> webrender_traits::ImageRendering;
+}
+
+impl ToImageRendering for image_rendering::T {
+ fn to_image_rendering(&self) -> webrender_traits::ImageRendering {
+ match *self {
+ image_rendering::T::CrispEdges => webrender_traits::ImageRendering::CrispEdges,
+ image_rendering::T::Auto => webrender_traits::ImageRendering::Auto,
+ image_rendering::T::Pixelated => webrender_traits::ImageRendering::Pixelated,
+ }
+ }
+}
+
+trait ToFilterOps {
+ fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp>;
+}
+
+impl ToFilterOps for filter::T {
+ fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp> {
+ let mut result = Vec::with_capacity(self.filters.len());
+ for filter in self.filters.iter() {
+ match *filter {
+ Filter::Blur(radius) => result.push(webrender_traits::FilterOp::Blur(radius)),
+ Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)),
+ Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)),
+ Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)),
+ Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.0)),
+ Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)),
+ Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount)),
+ Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)),
+ Filter::Sepia(amount) => result.push(webrender_traits::FilterOp::Sepia(amount)),
+ }
+ }
+ result
+ }
+}
+
+impl WebRenderStackingContextConverter for StackingContext {
+ fn convert_to_webrender(&self,
+ api: &webrender_traits::RenderApi,
+ pipeline_id: webrender_traits::PipelineId,
+ epoch: webrender_traits::Epoch,
+ scroll_layer_id: Option<webrender_traits::ScrollLayerId>)
+ -> webrender_traits::StackingContextId {
+ let scroll_policy = self.layer_info
+ .map_or(webrender_traits::ScrollPolicy::Scrollable, |info| {
+ match info.scroll_policy {
+ ScrollPolicy::Scrollable => webrender_traits::ScrollPolicy::Scrollable,
+ ScrollPolicy::FixedPosition => webrender_traits::ScrollPolicy::Fixed,
+ }
+ });
+
+ let mut sc = webrender_traits::StackingContext::new(scroll_layer_id,
+ scroll_policy,
+ self.bounds.to_rectf(),
+ self.overflow.to_rectf(),
+ self.z_index,
+ &self.transform,
+ &self.perspective,
+ self.establishes_3d_context,
+ self.blend_mode.to_blend_mode(),
+ self.filters.to_filter_ops());
+
+ let dl_builder = self.display_list.convert_to_webrender(api,
+ pipeline_id,
+ epoch);
+ api.add_display_list(dl_builder, &mut sc, pipeline_id, epoch);
+
+ api.add_stacking_context(sc, pipeline_id, epoch)
+ }
+}
+
+impl WebRenderDisplayListConverter for Box<DisplayList> {
+ fn convert_to_webrender(&self,
+ api: &webrender_traits::RenderApi,
+ pipeline_id: webrender_traits::PipelineId,
+ epoch: webrender_traits::Epoch) -> webrender_traits::DisplayListBuilder {
+ let mut builder = webrender_traits::DisplayListBuilder::new();
+
+ for item in &self.background_and_borders {
+ item.convert_to_webrender(api,
+ pipeline_id,
+ epoch,
+ webrender_traits::StackingLevel::BackgroundAndBorders,
+ &mut builder);
+ }
+
+ for item in &self.block_backgrounds_and_borders {
+ item.convert_to_webrender(api,
+ pipeline_id,
+ epoch,
+ webrender_traits::StackingLevel::BlockBackgroundAndBorders,
+ &mut builder);
+ }
+
+ for item in &self.floats {
+ item.convert_to_webrender(api,
+ pipeline_id,
+ epoch,
+ webrender_traits::StackingLevel::Floats,
+ &mut builder);
+ }
+
+ for item in &self.content {
+ item.convert_to_webrender(api,
+ pipeline_id,
+ epoch,
+ webrender_traits::StackingLevel::Content,
+ &mut builder);
+ }
+
+ for item in &self.positioned_content {
+ item.convert_to_webrender(api,
+ pipeline_id,
+ epoch,
+ webrender_traits::StackingLevel::PositionedContent,
+ &mut builder);
+ }
+
+ for item in &self.outlines {
+ item.convert_to_webrender(api,
+ pipeline_id,
+ epoch,
+ webrender_traits::StackingLevel::Outlines,
+ &mut builder);
+ }
+
+ builder
+ }
+}
+
+impl WebRenderDisplayItemConverter for DisplayItem {
+ fn convert_to_webrender(&self,
+ api: &webrender_traits::RenderApi,
+ pipeline_id: webrender_traits::PipelineId,
+ epoch: webrender_traits::Epoch,
+ level: webrender_traits::StackingLevel,
+ builder: &mut webrender_traits::DisplayListBuilder) {
+ match *self {
+ DisplayItem::SolidColorClass(ref item) => {
+ let color = item.color.to_colorf();
+ if color.a > 0.0 {
+ builder.push_rect(level,
+ item.base.bounds.to_rectf(),
+ item.base.clip.to_clip_region(),
+ color);
+ }
+ }
+ DisplayItem::TextClass(ref item) => {
+ let mut origin = item.baseline_origin.clone();
+ let mut glyphs = vec!();
+
+ for slice in item.text_run.natural_word_slices_in_visual_order(&item.range) {
+ for glyph in slice.glyphs.iter_glyphs_for_char_range(&slice.range) {
+ let glyph_advance = glyph.advance();
+ let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
+ let glyph = webrender_traits::GlyphInstance {
+ index: glyph.id(),
+ x: (origin.x + glyph_offset.x).to_f32_px(),
+ y: (origin.y + glyph_offset.y).to_f32_px(),
+ };
+ origin = Point2D::new(origin.x + glyph_advance, origin.y);
+ glyphs.push(glyph);
+ };
+ }
+
+ if glyphs.len() > 0 {
+ builder.push_text(level,
+ item.base.bounds.to_rectf(),
+ item.base.clip.to_clip_region(),
+ glyphs,
+ item.text_run.font_key.expect("Font not added to webrender!"),
+ item.text_color.to_colorf(),
+ item.text_run.actual_pt_size,
+ item.blur_radius);
+ }
+ }
+ DisplayItem::ImageClass(ref item) => {
+ if let Some(id) = item.image.id {
+ if item.stretch_size.width > Au(0) &&
+ item.stretch_size.height > Au(0) {
+ builder.push_image(level,
+ item.base.bounds.to_rectf(),
+ item.base.clip.to_clip_region(),
+ item.stretch_size.to_sizef(),
+ item.image_rendering.to_image_rendering(),
+ id);
+ }
+ }
+ }
+ DisplayItem::WebGLClass(ref item) => {
+ builder.push_webgl_canvas(level,
+ item.base.bounds.to_rectf(),
+ item.base.clip.to_clip_region(),
+ item.context_id);
+ }
+ DisplayItem::BorderClass(ref item) => {
+ let rect = item.base.bounds.to_rectf();
+ let left = webrender_traits::BorderSide {
+ width: item.border_widths.left.to_f32_px(),
+ color: item.color.left.to_colorf(),
+ style: item.style.left.to_border_style(),
+ };
+ let top = webrender_traits::BorderSide {
+ width: item.border_widths.top.to_f32_px(),
+ color: item.color.top.to_colorf(),
+ style: item.style.top.to_border_style(),
+ };
+ let right = webrender_traits::BorderSide {
+ width: item.border_widths.right.to_f32_px(),
+ color: item.color.right.to_colorf(),
+ style: item.style.right.to_border_style(),
+ };
+ let bottom = webrender_traits::BorderSide {
+ width: item.border_widths.bottom.to_f32_px(),
+ color: item.color.bottom.to_colorf(),
+ style: item.style.bottom.to_border_style(),
+ };
+ let radius = item.radius.to_border_radius();
+ builder.push_border(level,
+ rect,
+ item.base.clip.to_clip_region(),
+ left,
+ top,
+ right,
+ bottom,
+ radius);
+ }
+ DisplayItem::GradientClass(ref item) => {
+ let rect = item.base.bounds.to_rectf();
+ let start_point = item.start_point.to_pointf();
+ let end_point = item.end_point.to_pointf();
+ let mut stops = Vec::new();
+ for stop in &item.stops {
+ stops.push(stop.to_gradient_stop());
+ }
+ builder.push_gradient(level,
+ rect,
+ item.base.clip.to_clip_region(),
+ start_point,
+ end_point,
+ stops);
+ }
+ DisplayItem::LineClass(..) => {
+ println!("TODO DisplayItem::LineClass");
+ }
+ DisplayItem::LayeredItemClass(..) |
+ DisplayItem::NoopClass(..) => {
+ panic!("Unexpected in webrender!");
+ }
+ DisplayItem::BoxShadowClass(ref item) => {
+ let rect = item.base.bounds.to_rectf();
+ let box_bounds = item.box_bounds.to_rectf();
+ builder.push_box_shadow(level,
+ rect,
+ item.base.clip.to_clip_region(),
+ box_bounds,
+ item.offset.to_pointf(),
+ item.color.to_colorf(),
+ item.blur_radius.to_f32_px(),
+ item.spread_radius.to_f32_px(),
+ item.border_radius.to_f32_px(),
+ item.clip_mode.to_clip_mode());
+ }
+ DisplayItem::IframeClass(ref item) => {
+ let rect = item.base.bounds.to_rectf();
+ let pipeline_id = item.iframe.to_webrender();
+ builder.push_iframe(level,
+ rect,
+ item.base.clip.to_clip_region(),
+ pipeline_id);
+ }
+ DisplayItem::StackingContextClass(ref item) => {
+ let stacking_context_id = item.convert_to_webrender(api,
+ pipeline_id,
+ epoch,
+ None);
+ builder.push_stacking_context(level, stacking_context_id);
+ }
+ }
+ }
+}