Add WebRender integration to Servo.

WebRender is an experimental GPU accelerated rendering backend for Servo.

The WebRender backend can be specified by running Servo with the -w option (otherwise the default rendering backend will be used).

WebRender has many bugs, and missing features - but it is usable to browse most websites - please report any WebRender specific rendering bugs you encounter!
This commit is contained in:
Glenn Watson 2016-02-18 07:57:31 +10:00
parent f7f0eea470
commit c0531c312f
75 changed files with 2869 additions and 888 deletions

View file

@ -56,6 +56,9 @@ path = "../util"
[dependencies.ipc-channel]
git = "https://github.com/servo/ipc-channel"
[dependencies.webrender_traits]
git = "https://github.com/glennw/webrender_traits"
[dependencies]
app_units = {version = "0.2.1", features = ["plugins"]}
bitflags = "0.3"

View file

@ -13,7 +13,7 @@
use app_units::{Au, AU_PER_PX};
use azure::azure_hl::Color;
use block::BlockFlow;
use canvas_traits::{CanvasMsg, FromLayoutMsg};
use canvas_traits::{CanvasMsg, CanvasPixelData, CanvasData, FromLayoutMsg};
use context::LayoutContext;
use euclid::num::Zero;
use euclid::{Matrix4, Point2D, Point3D, Rect, SideOffsets2D, Size2D};
@ -26,7 +26,7 @@ use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayIte
use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection};
use gfx::display_list::{GradientDisplayItem};
use gfx::display_list::{GradientStop, ImageDisplayItem, LayeredItem, LayerInfo};
use gfx::display_list::{GradientStop, IframeDisplayItem, ImageDisplayItem, WebGLDisplayItem, LayeredItem, LayerInfo};
use gfx::display_list::{LineDisplayItem, OpaqueNode, SolidColorDisplayItem};
use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation};
use gfx::paint_thread::THREAD_TINT_COLORS;
@ -50,8 +50,7 @@ use style::properties::style_structs::Border;
use style::properties::{self, ComputedValues};
use style::values::RGBA;
use style::values::computed;
use style::values::computed::LinearGradient;
use style::values::computed::{LengthOrNone, LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::{LengthOrNone, LengthOrPercentage, LengthOrPercentageOrAuto, LinearGradient};
use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection};
use style_traits::cursor::Cursor;
use table_cell::CollapsedBordersForCell;
@ -948,9 +947,12 @@ impl FragmentDisplayListBuilding for Fragment {
stacking_relative_flow_origin,
self);
if !stacking_relative_border_box.intersects(stacking_relative_display_port) {
debug!("Fragment::build_display_list: outside display port");
return
// webrender deals with all culling via aabb
if !opts::get().use_webrender {
if !stacking_relative_border_box.intersects(stacking_relative_display_port) {
debug!("Fragment::build_display_list: outside display port");
return
}
}
// Calculate the clip rect. If there's nothing to render at all, don't even construct
@ -1112,20 +1114,31 @@ impl FragmentDisplayListBuilding for Fragment {
}
SpecificFragmentInfo::Iframe(ref fragment_info) => {
if !stacking_relative_content_box.is_empty() {
let layer_id = self.layer_id();
display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem {
item: DisplayItem::NoopClass(
box BaseDisplayItem::new(&stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
Cursor::DefaultCursor),
clip)),
layer_id: layer_id
}));
if opts::get().use_webrender {
display_list.content.push_back(DisplayItem::IframeClass(box IframeDisplayItem {
base: BaseDisplayItem::new(&stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
Cursor::DefaultCursor),
clip),
iframe: fragment_info.pipeline_id,
}));
} else {
let layer_id = self.layer_id();
display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem {
item: DisplayItem::NoopClass(
box BaseDisplayItem::new(&stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
Cursor::DefaultCursor),
clip)),
layer_id: layer_id
}));
display_list.layer_info.push_back(LayerInfo::new(layer_id,
ScrollPolicy::Scrollable,
Some(fragment_info.pipeline_id)));
display_list.layer_info.push_back(LayerInfo::new(layer_id,
ScrollPolicy::Scrollable,
Some(fragment_info.pipeline_id)));
}
}
}
SpecificFragmentInfo::Image(ref mut image_fragment) => {
@ -1144,7 +1157,6 @@ impl FragmentDisplayListBuilding for Fragment {
}
}
SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
// TODO(ecoal95): make the canvas with a renderer use the custom layer
let width = canvas_fragment_info.replaced_image_fragment_info
.computed_inline_size.map_or(0, |w| w.to_px() as usize);
let height = canvas_fragment_info.replaced_image_fragment_info
@ -1156,7 +1168,7 @@ impl FragmentDisplayListBuilding for Fragment {
let ipc_renderer = ipc_renderer.lock().unwrap();
let (sender, receiver) = ipc::channel().unwrap();
ipc_renderer.send(CanvasMsg::FromLayout(
FromLayoutMsg::SendPixelContents(sender))).unwrap();
FromLayoutMsg::SendData(sender))).unwrap();
let data = receiver.recv().unwrap();
// Propagate the layer and the renderer to the paint thread.
@ -1165,31 +1177,54 @@ impl FragmentDisplayListBuilding for Fragment {
data
},
None => IpcSharedMemory::from_byte(0xFFu8, width * height * 4),
};
let display_item = DisplayItem::ImageClass(box ImageDisplayItem {
base: BaseDisplayItem::new(&stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
Cursor::DefaultCursor),
clip),
image: Arc::new(Image {
width: width as u32,
height: height as u32,
format: PixelFormat::RGBA8,
bytes: canvas_data,
None => CanvasData::Pixels(CanvasPixelData {
image_data: IpcSharedMemory::from_byte(0xFFu8, width * height * 4),
image_key: None,
}),
stretch_size: stacking_relative_content_box.size,
image_rendering: image_rendering::T::Auto,
});
};
display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem {
item: display_item,
layer_id: layer_id
}));
let display_item = match canvas_data {
CanvasData::Pixels(canvas_data) => {
DisplayItem::ImageClass(box ImageDisplayItem {
base: BaseDisplayItem::new(&stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
Cursor::DefaultCursor),
clip),
image: Arc::new(Image {
width: width as u32,
height: height as u32,
format: PixelFormat::RGBA8,
bytes: canvas_data.image_data,
id: canvas_data.image_key,
}),
stretch_size: stacking_relative_content_box.size,
image_rendering: image_rendering::T::Auto,
})
}
CanvasData::WebGL(context_id) => {
DisplayItem::WebGLClass(box WebGLDisplayItem {
base: BaseDisplayItem::new(&stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
Cursor::DefaultCursor),
clip),
context_id: context_id,
})
}
};
display_list.layer_info.push_back(
LayerInfo::new(layer_id, ScrollPolicy::Scrollable, None));
if opts::get().use_webrender {
display_list.content.push_back(display_item);
} else {
display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem {
item: display_item,
layer_id: layer_id
}));
display_list.layer_info.push_back(
LayerInfo::new(layer_id, ScrollPolicy::Scrollable, None));
}
}
}
SpecificFragmentInfo::UnscannedText(_) => {
@ -1993,4 +2028,3 @@ pub enum StackingContextCreationMode {
OuterScrollWrapper,
InnerScrollWrapper,
}

View file

@ -1928,9 +1928,14 @@ impl InlineMetrics {
#[inline]
pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
let leading = line_height - (font_metrics.ascent + font_metrics.descent);
// Calculating the half leading here and then using leading - half_leading
// below ensure that we don't introduce any rounding accuracy issues here.
// The invariant is that the resulting total line height must exactly
// equal the requested line_height.
let half_leading = leading.scale_by(0.5);
InlineMetrics {
block_size_above_baseline: font_metrics.ascent + leading.scale_by(0.5),
depth_below_baseline: font_metrics.descent + leading.scale_by(0.5),
block_size_above_baseline: font_metrics.ascent + half_leading,
depth_below_baseline: font_metrics.descent + leading - half_leading,
ascent: font_metrics.ascent,
}
}

View file

@ -35,7 +35,7 @@ use ipc_channel::router::ROUTER;
use layout_debug;
use layout_traits::LayoutThreadFactory;
use log;
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId};
use msg::constellation_msg::{ConstellationChan, ConvertPipelineIdToWebRender, Failure, PipelineId};
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
use parallel;
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
@ -80,6 +80,8 @@ use util::opts;
use util::thread;
use util::thread_state;
use util::workqueue::WorkQueue;
use webrender_helpers::WebRenderStackingContextConverter;
use webrender_traits;
use wrapper::{LayoutNode, NonOpaqueStyleAndLayoutData, ServoLayoutNode, ThreadSafeLayoutNode};
/// The number of screens of data we're allowed to generate display lists for in each direction.
@ -221,6 +223,8 @@ pub struct LayoutThread {
/// The CSS error reporter for all CSS loaded in this layout thread
error_reporter: CSSErrorReporter,
// Webrender interface, if enabled.
webrender_api: Option<webrender_traits::RenderApi>,
}
impl LayoutThreadFactory for LayoutThread {
@ -240,7 +244,8 @@ impl LayoutThreadFactory for LayoutThread {
time_profiler_chan: time::ProfilerChan,
mem_profiler_chan: mem::ProfilerChan,
shutdown_chan: IpcSender<()>,
content_process_shutdown_chan: IpcSender<()>) {
content_process_shutdown_chan: IpcSender<()>,
webrender_api_sender: Option<webrender_traits::RenderApiSender>) {
let ConstellationChan(con_chan) = constellation_chan.clone();
thread::spawn_named_with_send_on_failure(format!("LayoutThread {:?}", id),
thread_state::LAYOUT,
@ -258,7 +263,8 @@ impl LayoutThreadFactory for LayoutThread {
image_cache_thread,
font_cache_thread,
time_profiler_chan,
mem_profiler_chan.clone());
mem_profiler_chan.clone(),
webrender_api_sender);
let reporter_name = format!("layout-reporter-{}", id);
mem_profiler_chan.run_with_memory_reporting(|| {
@ -367,7 +373,8 @@ impl LayoutThread {
image_cache_thread: ImageCacheThread,
font_cache_thread: FontCacheThread,
time_profiler_chan: time::ProfilerChan,
mem_profiler_chan: mem::ProfilerChan)
mem_profiler_chan: mem::ProfilerChan,
webrender_api_sender: Option<webrender_traits::RenderApiSender>)
-> LayoutThread {
let device = Device::new(
MediaType::Screen,
@ -437,6 +444,7 @@ impl LayoutThread {
expired_animations: Arc::new(RwLock::new(HashMap::new())),
epoch: Epoch(0),
viewport_size: Size2D::new(Au(0), Au(0)),
webrender_api: webrender_api_sender.map(|wr| wr.create_api()),
rw_data: Arc::new(Mutex::new(
LayoutThreadData {
constellation_chan: constellation_chan,
@ -705,7 +713,8 @@ impl LayoutThread {
self.time_profiler_chan.clone(),
self.mem_profiler_chan.clone(),
info.layout_shutdown_chan,
info.content_process_shutdown_chan);
info.content_process_shutdown_chan,
self.webrender_api.as_ref().map(|wr| wr.clone_sender()));
}
/// Enters a quiescent state in which no new messages will be processed until an `ExitNow` is
@ -908,9 +917,40 @@ impl LayoutThread {
debug!("Layout done!");
self.epoch.next();
self.paint_chan
.send(LayoutToPaintMsg::PaintInit(self.epoch, paint_layer))
.unwrap();
if opts::get().use_webrender {
let api = self.webrender_api.as_ref().unwrap();
// TODO: Avoid the temporary conversion and build webrender sc/dl directly!
let Epoch(epoch_number) = self.epoch;
let epoch = webrender_traits::Epoch(epoch_number);
let pipeline_id = self.id.to_webrender();
// TODO(gw) For now only create a root scrolling layer!
let root_scroll_layer_id = webrender_traits::ScrollLayerId::new(pipeline_id, 0);
let sc_id = rw_data.stacking_context.as_ref()
.unwrap()
.convert_to_webrender(&self.webrender_api.as_ref().unwrap(),
pipeline_id,
epoch,
Some(root_scroll_layer_id));
let root_background_color = webrender_traits::ColorF::new(root_background_color.r,
root_background_color.g,
root_background_color.b,
root_background_color.a);
let viewport_size = Size2D::new(self.viewport_size.width.to_f32_px(),
self.viewport_size.height.to_f32_px());
api.set_root_stacking_context(sc_id,
root_background_color,
epoch,
pipeline_id,
viewport_size);
} else {
self.paint_chan
.send(LayoutToPaintMsg::PaintInit(self.epoch, paint_layer))
.unwrap();
}
}
});
}

View file

@ -59,6 +59,7 @@ extern crate unicode_script;
extern crate url;
#[macro_use]
extern crate util;
extern crate webrender_traits;
#[macro_use]
mod layout_debug;
@ -95,4 +96,5 @@ mod table_rowgroup;
mod table_wrapper;
mod text;
mod traversal;
mod webrender_helpers;
mod wrapper;

View file

@ -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);
}
}
}
}