mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +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
|
||||
* 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 render_context::RenderContext;
|
||||
use text::SendableTextRun;
|
||||
|
||||
use clone_arc = std::arc::clone;
|
||||
use geom::Rect;
|
||||
use geom::{Point2D, Size2D};
|
||||
use std::arc::ARC;
|
||||
use geom::{Point2D, Rect, Size2D};
|
||||
use servo_net::image::base::Image;
|
||||
use servo_util::range::Range;
|
||||
use std::arc::ARC;
|
||||
use std::arc;
|
||||
|
||||
struct DisplayItemData {
|
||||
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.
|
||||
/// A list of rendering operations to be performed.
|
||||
pub struct DisplayList {
|
||||
list: ~[~DisplayItem]
|
||||
priv list: ~[DisplayItem]
|
||||
}
|
||||
|
||||
pub impl DisplayList {
|
||||
fn new() -> DisplayList {
|
||||
DisplayList { list: ~[] }
|
||||
impl DisplayList {
|
||||
/// Creates a new display 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
|
||||
//debug!("Adding display item %u: %?", self.len(), item);
|
||||
self.list.push(item);
|
||||
self.list.push(item)
|
||||
}
|
||||
|
||||
fn draw_into_context(&self, ctx: &RenderContext) {
|
||||
debug!("beginning display list");
|
||||
/// Draws the display list into the given render context.
|
||||
pub fn draw_into_context(&self, render_context: &RenderContext) {
|
||||
debug!("Beginning display list.");
|
||||
for self.list.each |item| {
|
||||
// FIXME(Issue #150): crashes
|
||||
//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::managed;
|
||||
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::geometry::Au;
|
||||
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::local_image_cache::LocalImageCache;
|
||||
use servo_util::range::*;
|
||||
use std::arc;
|
||||
use std::net::url::Url;
|
||||
|
||||
/// 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 color = nearest_ancestor_element.style().color().to_gfx_color();
|
||||
|
||||
// FIXME: This should use `with_mut_ref` when that appears.
|
||||
let mut this_list = list.take();
|
||||
this_list.append_item(~DisplayItem::new_Text(&absolute_box_bounds,
|
||||
~text_box.run.serialize(),
|
||||
text_box.range,
|
||||
color));
|
||||
list.put_back(this_list);
|
||||
// Create the text box.
|
||||
do list.with_mut_ref |list| {
|
||||
let text_display_item = ~TextDisplayItem {
|
||||
base: BaseDisplayItem {
|
||||
bounds: absolute_box_bounds,
|
||||
},
|
||||
// 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.
|
||||
//
|
||||
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
||||
// should have a real `SERVO_DEBUG` system.
|
||||
debug!("%?", {
|
||||
// Compute the text box bounds.
|
||||
//
|
||||
// FIXME: This should use `with_mut_ref` when that appears.
|
||||
let mut this_list = list.take();
|
||||
this_list.append_item(~DisplayItem::new_Border(&absolute_box_bounds,
|
||||
Au::from_px(1),
|
||||
rgb(0, 0, 200).to_gfx_color()));
|
||||
debug!("%?", {
|
||||
// Compute the text box bounds and draw a border surrounding them.
|
||||
do list.with_mut_ref |list| {
|
||||
let border_display_item = ~BorderDisplayItem {
|
||||
base: BaseDisplayItem {
|
||||
bounds: absolute_box_bounds,
|
||||
},
|
||||
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.
|
||||
//
|
||||
|
@ -596,10 +609,17 @@ pub impl RenderBox {
|
|||
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
|
||||
Size2D(absolute_box_bounds.size.width, Au(0)));
|
||||
|
||||
this_list.append_item(~DisplayItem::new_Border(&baseline,
|
||||
Au::from_px(1),
|
||||
rgb(0, 200, 0).to_gfx_color()));
|
||||
list.put_back(this_list);
|
||||
do list.with_mut_ref |list| {
|
||||
let border_display_item = ~BorderDisplayItem {
|
||||
base: BaseDisplayItem {
|
||||
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) => {
|
||||
debug!("(building display list) building image box");
|
||||
|
||||
// FIXME: This should use `with_mut_ref` when that appears.
|
||||
let mut this_list = list.take();
|
||||
this_list.append_item(~DisplayItem::new_Image(&absolute_box_bounds,
|
||||
arc::clone(&image)));
|
||||
list.put_back(this_list);
|
||||
// Place the image into the display list.
|
||||
do list.with_mut_ref |list| {
|
||||
let image_display_item = ~ImageDisplayItem {
|
||||
base: BaseDisplayItem {
|
||||
bounds: absolute_box_bounds,
|
||||
},
|
||||
image: image.clone(),
|
||||
};
|
||||
list.append_item(ImageDisplayItemClass(image_display_item))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// No image data at all? Do nothing.
|
||||
|
@ -644,11 +669,18 @@ pub impl RenderBox {
|
|||
// doesn't have a render box".
|
||||
let nearest_ancestor_element = self.nearest_ancestor_element();
|
||||
|
||||
let bgcolor = nearest_ancestor_element.style().background_color();
|
||||
if !bgcolor.alpha.approx_eq(&0.0) {
|
||||
let mut l = list.take(); // FIXME: use with_mut_ref when available
|
||||
l.append_item(~DisplayItem::new_SolidColor(absolute_bounds, bgcolor.to_gfx_color()));
|
||||
list.put_back(l);
|
||||
let background_color = nearest_ancestor_element.style().background_color();
|
||||
if !background_color.alpha.approx_eq(&0.0) {
|
||||
do list.with_mut_ref |list| {
|
||||
let solid_color_display_item = ~SolidColorDisplayItem {
|
||||
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 color = top_color.to_gfx_color(); // FIXME
|
||||
|
||||
// FIXME: Use `with_mut_ref` when that works.
|
||||
let mut this_list = list.take();
|
||||
this_list.append_item(~DisplayItem::new_Border(&bounds, border_width, color));
|
||||
list.put_back(this_list);
|
||||
// Append the border to the display list.
|
||||
do list.with_mut_ref |list| {
|
||||
let border_display_item = ~BorderDisplayItem {
|
||||
base: BaseDisplayItem {
|
||||
bounds: bounds,
|
||||
},
|
||||
width: border_width,
|
||||
color: color,
|
||||
};
|
||||
|
||||
list.append_item(BorderDisplayItemClass(border_display_item))
|
||||
}
|
||||
} else {
|
||||
warn!("ignoring unimplemented border widths");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue