Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extra display list data #482

Closed
wants to merge 11 commits into from

Refactor display lists to use less memory.

  • Loading branch information
pcwalton committed May 25, 2013
commit e94fcaf541b8a6e7b6e913748fe6227747571b6b
@@ -2,129 +2,152 @@
* 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 geom::{Point2D, Rect, Size2D};
use std::arc::ARC;
use servo_net::image::base::Image;
use servo_util::range::Range;
use std::arc::ARC;

struct DisplayItemData {
bounds : Rect<Au>, // TODO: whose coordinate system should this use?
/// A list of rendering operations to be performed.
pub struct DisplayList {
priv list: ~[DisplayItem]
}

pub impl DisplayItemData {
fn new(bounds: &Rect<Au>) -> DisplayItemData {
DisplayItemData { bounds: copy *bounds }
impl DisplayList {
/// Creates a new display list.
pub fn new() -> DisplayList {
DisplayList {
list: ~[]
}
}
}

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)
}
/// 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)
}

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
/// 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(render_context)
}
debug!("Ending display list.")
}

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));
() });
}
/// One drawing command in the list.
pub enum DisplayItem {
SolidColorDisplayItemClass(~SolidColorDisplayItem),
TextDisplayItemClass(~TextDisplayItem),
ImageDisplayItemClass(~ImageDisplayItem),
BorderDisplayItemClass(~BorderDisplayItem),
}

fn new_SolidColor(bounds: &Rect<Au>, color: Color) -> DisplayItem {
SolidColor(DisplayItemData::new(bounds), color)
}
/// 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>,
}

fn new_Border(bounds: &Rect<Au>, width: Au, color: Color) -> DisplayItem {
Border(DisplayItemData::new(bounds), width, color)
}
/// Renders a solid color.
pub struct SolidColorDisplayItem {
base: BaseDisplayItem,
color: Color,
}

fn new_Text(bounds: &Rect<Au>,
run: ~SendableTextRun,
range: Range,
color: Color) -> DisplayItem {
Text(DisplayItemData::new(bounds), run, range, color)
}
/// Renders text.
pub struct TextDisplayItem {
base: BaseDisplayItem,
text_run: ~SendableTextRun,
range: Range,
color: 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)
}
/// Renders an image.
pub struct ImageDisplayItem {
base: BaseDisplayItem,
image: ARC<~Image>,
}

// Dual-mode/freezable.
pub struct DisplayList {
list: ~[~DisplayItem]
/// Renders a border.
pub struct BorderDisplayItem {
base: BaseDisplayItem,
/// The width of the border.
width: Au,
/// The color of the border.
color: Color,
}

pub impl DisplayList {
fn new() -> DisplayList {
DisplayList { list: ~[] }
}
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)
}

fn append_item(&mut self, item: ~DisplayItem) {
// FIXME(Issue #150): crashes
//debug!("Adding display item %u: %?", self.len(), item);
self.list.push(item);
}
TextDisplayItemClass(ref text) => {
debug!("Drawing text at %?.", text.base.bounds);

fn draw_into_context(&self, ctx: &RenderContext) {
debug!("beginning display list");
for self.list.each |item| {
// FIXME(Issue #150): crashes
//debug!("drawing %?", *item);
item.draw_into_context(ctx);
// 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)

// Draw the underline if applicable.
if new_run.underline {
// TODO: Use the font metrics to properly position the underline bar.
let width = text.base.bounds.size.width;
let underline_bounds = Rect(Point2D(baseline_origin.x, baseline_origin.y),
Size2D(width, font.metrics.underline_size));
render_context.draw_solid_color(&underline_bounds, 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)
}
}
debug!("ending display list");
}
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.