mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
auto merge of #476 : pcwalton/servo/display-list-refactor, r=pcwalton
r? @metajack
This commit is contained in:
commit
273b6cfe29
2 changed files with 201 additions and 139 deletions
|
@ -2,129 +2,151 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use color::{Color, rgb};
|
//! Servo heavily uses display lists, which are retained-mode lists of rendering commands to
|
||||||
|
/// perform. Using a list instead of rendering elements in immediate mode allows transforms, hit
|
||||||
|
/// testing, and invalidation to be performed using the same primitives as painting. It also allows
|
||||||
|
/// Servo to aggressively cull invisible and out-of-bounds rendering elements, to reduce overdraw.
|
||||||
|
/// Finally, display lists allow tiles to be farmed out onto multiple CPUs and rendered in
|
||||||
|
/// parallel (although this benefit does not apply to GPU-based rendering).
|
||||||
|
///
|
||||||
|
/// Display items describe relatively high-level drawing operations (for example, entire borders
|
||||||
|
/// and shadows instead of lines and blur operations), to reduce the amount of allocation required.
|
||||||
|
/// They are therefore not exactly analogous to constructs like Skia pictures, which consist of
|
||||||
|
/// low-level drawing primitives.
|
||||||
|
|
||||||
|
use color::Color;
|
||||||
use geometry::Au;
|
use geometry::Au;
|
||||||
use render_context::RenderContext;
|
use render_context::RenderContext;
|
||||||
use text::SendableTextRun;
|
use text::SendableTextRun;
|
||||||
|
|
||||||
use clone_arc = std::arc::clone;
|
use geom::{Point2D, Rect, Size2D};
|
||||||
use geom::Rect;
|
|
||||||
use geom::{Point2D, Size2D};
|
|
||||||
use std::arc::ARC;
|
|
||||||
use servo_net::image::base::Image;
|
use servo_net::image::base::Image;
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
|
use std::arc::ARC;
|
||||||
|
use std::arc;
|
||||||
|
|
||||||
struct DisplayItemData {
|
/// A list of rendering operations to be performed.
|
||||||
bounds : Rect<Au>, // TODO: whose coordinate system should this use?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub impl DisplayItemData {
|
|
||||||
fn new(bounds: &Rect<Au>) -> DisplayItemData {
|
|
||||||
DisplayItemData { bounds: copy *bounds }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum DisplayItem {
|
|
||||||
SolidColor(DisplayItemData, Color),
|
|
||||||
// TODO: need to provide spacing data for text run.
|
|
||||||
// (i.e, to support rendering of CSS 'word-spacing' and 'letter-spacing')
|
|
||||||
// TODO: don't copy text runs, ever.
|
|
||||||
Text(DisplayItemData, ~SendableTextRun, Range, Color),
|
|
||||||
Image(DisplayItemData, ARC<~Image>),
|
|
||||||
Border(DisplayItemData, Au, Color)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub impl<'self> DisplayItem {
|
|
||||||
fn d(&'self self) -> &'self DisplayItemData {
|
|
||||||
match *self {
|
|
||||||
SolidColor(ref d, _) => d,
|
|
||||||
Text(ref d, _, _, _) => d,
|
|
||||||
Image(ref d, _) => d,
|
|
||||||
Border(ref d, _, _) => d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_into_context(&self, ctx: &RenderContext) {
|
|
||||||
match self {
|
|
||||||
&SolidColor(_, color) => {
|
|
||||||
ctx.draw_solid_color(&self.d().bounds, color)
|
|
||||||
}
|
|
||||||
&Text(_, ref run, ref range, color) => {
|
|
||||||
debug!("drawing text at %?", self.d().bounds);
|
|
||||||
let new_run = @run.deserialize(ctx.font_ctx);
|
|
||||||
let font = new_run.font;
|
|
||||||
let origin = self.d().bounds.origin;
|
|
||||||
let baseline_origin = Point2D(origin.x, origin.y + font.metrics.ascent);
|
|
||||||
font.draw_text_into_context(ctx, new_run, range, baseline_origin, color);
|
|
||||||
if(new_run.underline){
|
|
||||||
//TODO: Use the font metrics to properly position the underline bar
|
|
||||||
let width = self.d().bounds.size.width;
|
|
||||||
let u_size = font.metrics.underline_size;
|
|
||||||
let u_bounds = Rect(
|
|
||||||
Point2D(baseline_origin.x, baseline_origin.y),
|
|
||||||
Size2D(width, u_size)
|
|
||||||
);
|
|
||||||
ctx.draw_solid_color(&u_bounds, color);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
&Image(_, ref img) => {
|
|
||||||
debug!("drawing image at %?", self.d().bounds);
|
|
||||||
ctx.draw_image(self.d().bounds, clone_arc(img));
|
|
||||||
}
|
|
||||||
&Border(_, width, color) => {
|
|
||||||
ctx.draw_border(&self.d().bounds, width, color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("%?", {
|
|
||||||
ctx.draw_border(&self.d().bounds, Au::from_px(1), rgb(150, 150, 150));
|
|
||||||
() });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_SolidColor(bounds: &Rect<Au>, color: Color) -> DisplayItem {
|
|
||||||
SolidColor(DisplayItemData::new(bounds), color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_Border(bounds: &Rect<Au>, width: Au, color: Color) -> DisplayItem {
|
|
||||||
Border(DisplayItemData::new(bounds), width, color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_Text(bounds: &Rect<Au>,
|
|
||||||
run: ~SendableTextRun,
|
|
||||||
range: Range,
|
|
||||||
color: Color) -> DisplayItem {
|
|
||||||
Text(DisplayItemData::new(bounds), run, range, color)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ARC should be cloned into ImageData, but Images are not sendable
|
|
||||||
fn new_Image(bounds: &Rect<Au>, image: ARC<~Image>) -> DisplayItem {
|
|
||||||
Image(DisplayItemData::new(bounds), image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dual-mode/freezable.
|
|
||||||
pub struct DisplayList {
|
pub struct DisplayList {
|
||||||
list: ~[~DisplayItem]
|
priv list: ~[DisplayItem]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl DisplayList {
|
impl DisplayList {
|
||||||
fn new() -> DisplayList {
|
/// Creates a new display list.
|
||||||
DisplayList { list: ~[] }
|
pub fn new() -> DisplayList {
|
||||||
|
DisplayList {
|
||||||
|
list: ~[]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_item(&mut self, item: ~DisplayItem) {
|
/// Appends the given item to the display list.
|
||||||
|
pub fn append_item(&mut self, item: DisplayItem) {
|
||||||
// FIXME(Issue #150): crashes
|
// FIXME(Issue #150): crashes
|
||||||
//debug!("Adding display item %u: %?", self.len(), item);
|
//debug!("Adding display item %u: %?", self.len(), item);
|
||||||
self.list.push(item);
|
self.list.push(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_into_context(&self, ctx: &RenderContext) {
|
/// Draws the display list into the given render context.
|
||||||
debug!("beginning display list");
|
pub fn draw_into_context(&self, render_context: &RenderContext) {
|
||||||
|
debug!("Beginning display list.");
|
||||||
for self.list.each |item| {
|
for self.list.each |item| {
|
||||||
// FIXME(Issue #150): crashes
|
// FIXME(Issue #150): crashes
|
||||||
//debug!("drawing %?", *item);
|
//debug!("drawing %?", *item);
|
||||||
item.draw_into_context(ctx);
|
item.draw_into_context(render_context)
|
||||||
}
|
}
|
||||||
debug!("ending display list");
|
debug!("Ending display list.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One drawing command in the list.
|
||||||
|
pub enum DisplayItem {
|
||||||
|
SolidColorDisplayItemClass(~SolidColorDisplayItem),
|
||||||
|
TextDisplayItemClass(~TextDisplayItem),
|
||||||
|
ImageDisplayItemClass(~ImageDisplayItem),
|
||||||
|
BorderDisplayItemClass(~BorderDisplayItem),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information common to all display items.
|
||||||
|
pub struct BaseDisplayItem {
|
||||||
|
/// The boundaries of the display item.
|
||||||
|
///
|
||||||
|
/// TODO: Which coordinate system should this use?
|
||||||
|
bounds: Rect<Au>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders a solid color.
|
||||||
|
pub struct SolidColorDisplayItem {
|
||||||
|
base: BaseDisplayItem,
|
||||||
|
color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders text.
|
||||||
|
pub struct TextDisplayItem {
|
||||||
|
base: BaseDisplayItem,
|
||||||
|
text_run: ~SendableTextRun,
|
||||||
|
range: Range,
|
||||||
|
color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders an image.
|
||||||
|
pub struct ImageDisplayItem {
|
||||||
|
base: BaseDisplayItem,
|
||||||
|
image: ARC<~Image>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders a border.
|
||||||
|
pub struct BorderDisplayItem {
|
||||||
|
base: BaseDisplayItem,
|
||||||
|
/// The width of the border.
|
||||||
|
width: Au,
|
||||||
|
/// The color of the border.
|
||||||
|
color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayItem {
|
||||||
|
/// Renders this display item into the given render context.
|
||||||
|
fn draw_into_context(&self, render_context: &RenderContext) {
|
||||||
|
match *self {
|
||||||
|
SolidColorDisplayItemClass(ref solid_color) => {
|
||||||
|
render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextDisplayItemClass(ref text) => {
|
||||||
|
debug!("Drawing text at %?.", text.base.bounds);
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Allocating? Why?
|
||||||
|
let new_run = @text.text_run.deserialize(render_context.font_ctx);
|
||||||
|
|
||||||
|
let font = new_run.font;
|
||||||
|
let origin = text.base.bounds.origin;
|
||||||
|
let baseline_origin = Point2D(origin.x, origin.y + font.metrics.ascent);
|
||||||
|
|
||||||
|
font.draw_text_into_context(render_context,
|
||||||
|
new_run,
|
||||||
|
&text.range,
|
||||||
|
baseline_origin,
|
||||||
|
text.color);
|
||||||
|
|
||||||
|
if new_run.underline {
|
||||||
|
// TODO(eatkinson): Use the font metrics to properly position the underline
|
||||||
|
// bar.
|
||||||
|
let width = text.base.bounds.size.width;
|
||||||
|
let underline_size = font.metrics.underline_size;
|
||||||
|
let underline_bounds = Rect(Point2D(baseline_origin.x, baseline_origin.y),
|
||||||
|
Size2D(width, underline_size));
|
||||||
|
render_context.draw_solid_color(&underline_bounds, text.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageDisplayItemClass(ref image_item) => {
|
||||||
|
debug!("Drawing image at %?.", image_item.base.bounds);
|
||||||
|
|
||||||
|
render_context.draw_image(image_item.base.bounds, image_item.image.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
BorderDisplayItemClass(ref border) => {
|
||||||
|
render_context.draw_border(&border.base.bounds, border.width, border.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@ use core::cell::Cell;
|
||||||
use core::cmp::ApproxEq;
|
use core::cmp::ApproxEq;
|
||||||
use core::managed;
|
use core::managed;
|
||||||
use geom::{Point2D, Rect, Size2D};
|
use geom::{Point2D, Rect, Size2D};
|
||||||
use gfx::display_list::{DisplayItem, DisplayList};
|
use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass};
|
||||||
|
use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass};
|
||||||
|
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem};
|
||||||
|
use gfx::display_list::{TextDisplayItemClass};
|
||||||
use gfx::font::{FontStyle, FontWeight300};
|
use gfx::font::{FontStyle, FontWeight300};
|
||||||
use gfx::geometry::Au;
|
use gfx::geometry::Au;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
|
@ -31,7 +34,6 @@ use script::dom::node::{AbstractNode, LayoutView};
|
||||||
use servo_net::image::holder::ImageHolder;
|
use servo_net::image::holder::ImageHolder;
|
||||||
use servo_net::local_image_cache::LocalImageCache;
|
use servo_net::local_image_cache::LocalImageCache;
|
||||||
use servo_util::range::*;
|
use servo_util::range::*;
|
||||||
use std::arc;
|
|
||||||
use std::net::url::Url;
|
use std::net::url::Url;
|
||||||
|
|
||||||
/// Render boxes (`struct RenderBox`) are the leaves of the layout tree. They cannot position
|
/// Render boxes (`struct RenderBox`) are the leaves of the layout tree. They cannot position
|
||||||
|
@ -567,26 +569,37 @@ pub impl RenderBox {
|
||||||
let nearest_ancestor_element = self.nearest_ancestor_element();
|
let nearest_ancestor_element = self.nearest_ancestor_element();
|
||||||
let color = nearest_ancestor_element.style().color().to_gfx_color();
|
let color = nearest_ancestor_element.style().color().to_gfx_color();
|
||||||
|
|
||||||
// FIXME: This should use `with_mut_ref` when that appears.
|
// Create the text box.
|
||||||
let mut this_list = list.take();
|
do list.with_mut_ref |list| {
|
||||||
this_list.append_item(~DisplayItem::new_Text(&absolute_box_bounds,
|
let text_display_item = ~TextDisplayItem {
|
||||||
~text_box.run.serialize(),
|
base: BaseDisplayItem {
|
||||||
text_box.range,
|
bounds: absolute_box_bounds,
|
||||||
color));
|
},
|
||||||
list.put_back(this_list);
|
// FIXME(pcwalton): Allocation? Why?!
|
||||||
|
text_run: ~text_box.run.serialize(),
|
||||||
|
range: text_box.range,
|
||||||
|
color: color,
|
||||||
|
};
|
||||||
|
|
||||||
|
list.append_item(TextDisplayItemClass(text_display_item))
|
||||||
|
}
|
||||||
|
|
||||||
// Draw debug frames for text bounds.
|
// Draw debug frames for text bounds.
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
||||||
// should have a real `SERVO_DEBUG` system.
|
// should have a real `SERVO_DEBUG` system.
|
||||||
debug!("%?", {
|
debug!("%?", {
|
||||||
// Compute the text box bounds.
|
// Compute the text box bounds and draw a border surrounding them.
|
||||||
//
|
do list.with_mut_ref |list| {
|
||||||
// FIXME: This should use `with_mut_ref` when that appears.
|
let border_display_item = ~BorderDisplayItem {
|
||||||
let mut this_list = list.take();
|
base: BaseDisplayItem {
|
||||||
this_list.append_item(~DisplayItem::new_Border(&absolute_box_bounds,
|
bounds: absolute_box_bounds,
|
||||||
Au::from_px(1),
|
},
|
||||||
rgb(0, 0, 200).to_gfx_color()));
|
width: Au::from_px(1),
|
||||||
|
color: rgb(0, 0, 200).to_gfx_color(),
|
||||||
|
};
|
||||||
|
list.append_item(BorderDisplayItemClass(border_display_item))
|
||||||
|
}
|
||||||
|
|
||||||
// Draw a rectangle representing the baselines.
|
// Draw a rectangle representing the baselines.
|
||||||
//
|
//
|
||||||
|
@ -596,10 +609,17 @@ pub impl RenderBox {
|
||||||
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
|
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
|
||||||
Size2D(absolute_box_bounds.size.width, Au(0)));
|
Size2D(absolute_box_bounds.size.width, Au(0)));
|
||||||
|
|
||||||
this_list.append_item(~DisplayItem::new_Border(&baseline,
|
do list.with_mut_ref |list| {
|
||||||
Au::from_px(1),
|
let border_display_item = ~BorderDisplayItem {
|
||||||
rgb(0, 200, 0).to_gfx_color()));
|
base: BaseDisplayItem {
|
||||||
list.put_back(this_list);
|
bounds: baseline,
|
||||||
|
},
|
||||||
|
width: Au::from_px(1),
|
||||||
|
color: rgb(0, 200, 0).to_gfx_color(),
|
||||||
|
};
|
||||||
|
list.append_item(BorderDisplayItemClass(border_display_item))
|
||||||
|
}
|
||||||
|
|
||||||
()
|
()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -611,11 +631,16 @@ pub impl RenderBox {
|
||||||
Some(image) => {
|
Some(image) => {
|
||||||
debug!("(building display list) building image box");
|
debug!("(building display list) building image box");
|
||||||
|
|
||||||
// FIXME: This should use `with_mut_ref` when that appears.
|
// Place the image into the display list.
|
||||||
let mut this_list = list.take();
|
do list.with_mut_ref |list| {
|
||||||
this_list.append_item(~DisplayItem::new_Image(&absolute_box_bounds,
|
let image_display_item = ~ImageDisplayItem {
|
||||||
arc::clone(&image)));
|
base: BaseDisplayItem {
|
||||||
list.put_back(this_list);
|
bounds: absolute_box_bounds,
|
||||||
|
},
|
||||||
|
image: image.clone(),
|
||||||
|
};
|
||||||
|
list.append_item(ImageDisplayItemClass(image_display_item))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// No image data at all? Do nothing.
|
// No image data at all? Do nothing.
|
||||||
|
@ -644,11 +669,18 @@ pub impl RenderBox {
|
||||||
// doesn't have a render box".
|
// doesn't have a render box".
|
||||||
let nearest_ancestor_element = self.nearest_ancestor_element();
|
let nearest_ancestor_element = self.nearest_ancestor_element();
|
||||||
|
|
||||||
let bgcolor = nearest_ancestor_element.style().background_color();
|
let background_color = nearest_ancestor_element.style().background_color();
|
||||||
if !bgcolor.alpha.approx_eq(&0.0) {
|
if !background_color.alpha.approx_eq(&0.0) {
|
||||||
let mut l = list.take(); // FIXME: use with_mut_ref when available
|
do list.with_mut_ref |list| {
|
||||||
l.append_item(~DisplayItem::new_SolidColor(absolute_bounds, bgcolor.to_gfx_color()));
|
let solid_color_display_item = ~SolidColorDisplayItem {
|
||||||
list.put_back(l);
|
base: BaseDisplayItem {
|
||||||
|
bounds: *absolute_bounds,
|
||||||
|
},
|
||||||
|
color: background_color.to_gfx_color(),
|
||||||
|
};
|
||||||
|
|
||||||
|
list.append_item(SolidColorDisplayItemClass(solid_color_display_item))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,10 +721,18 @@ pub impl RenderBox {
|
||||||
let top_color = self.style().border_top_color();
|
let top_color = self.style().border_top_color();
|
||||||
let color = top_color.to_gfx_color(); // FIXME
|
let color = top_color.to_gfx_color(); // FIXME
|
||||||
|
|
||||||
// FIXME: Use `with_mut_ref` when that works.
|
// Append the border to the display list.
|
||||||
let mut this_list = list.take();
|
do list.with_mut_ref |list| {
|
||||||
this_list.append_item(~DisplayItem::new_Border(&bounds, border_width, color));
|
let border_display_item = ~BorderDisplayItem {
|
||||||
list.put_back(this_list);
|
base: BaseDisplayItem {
|
||||||
|
bounds: bounds,
|
||||||
|
},
|
||||||
|
width: border_width,
|
||||||
|
color: color,
|
||||||
|
};
|
||||||
|
|
||||||
|
list.append_item(BorderDisplayItemClass(border_display_item))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!("ignoring unimplemented border widths");
|
warn!("ignoring unimplemented border widths");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue