mirror of
https://github.com/servo/servo.git
synced 2025-06-24 00:54:32 +01:00
auto merge of #675 : eschweic/servo/multi-layer, r=metajack
This refactors the compositor and adds initial support for multiple compositor layers being displayed at once. A new module, `compositor_layer.rs`, does most of the heavy lifting that was once in `mod.rs`. --- clipped so bors is happy ---
This commit is contained in:
commit
0d46164b43
10 changed files with 741 additions and 290 deletions
|
@ -32,7 +32,7 @@ pub struct RenderLayer {
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
RenderMsg(RenderLayer),
|
RenderMsg(RenderLayer),
|
||||||
ReRenderMsg(~[BufferRequest], f32),
|
ReRenderMsg(~[BufferRequest], f32, PipelineId),
|
||||||
PaintPermissionGranted,
|
PaintPermissionGranted,
|
||||||
PaintPermissionRevoked,
|
PaintPermissionRevoked,
|
||||||
ExitMsg(Chan<()>),
|
ExitMsg(Chan<()>),
|
||||||
|
@ -135,18 +135,18 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
match self.port.recv() {
|
match self.port.recv() {
|
||||||
RenderMsg(render_layer) => {
|
RenderMsg(render_layer) => {
|
||||||
if self.paint_permission {
|
if self.paint_permission {
|
||||||
self.compositor.new_layer(render_layer.size, self.opts.tile_size);
|
self.compositor.new_layer(self.id, render_layer.size);
|
||||||
}
|
}
|
||||||
self.render_layer = Some(render_layer);
|
self.render_layer = Some(render_layer);
|
||||||
}
|
}
|
||||||
ReRenderMsg(tiles, scale) => {
|
ReRenderMsg(tiles, scale, id) => {
|
||||||
self.render(tiles, scale);
|
self.render(tiles, scale, id);
|
||||||
}
|
}
|
||||||
PaintPermissionGranted => {
|
PaintPermissionGranted => {
|
||||||
self.paint_permission = true;
|
self.paint_permission = true;
|
||||||
match self.render_layer {
|
match self.render_layer {
|
||||||
Some(ref render_layer) => {
|
Some(ref render_layer) => {
|
||||||
self.compositor.new_layer(render_layer.size, self.opts.tile_size);
|
self.compositor.new_layer(self.id, render_layer.size);
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
|
fn render(&mut self, tiles: ~[BufferRequest], scale: f32, id: PipelineId) {
|
||||||
let render_layer;
|
let render_layer;
|
||||||
match self.render_layer {
|
match self.render_layer {
|
||||||
Some(ref r_layer) => {
|
Some(ref r_layer) => {
|
||||||
|
@ -202,7 +202,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
font_ctx: self.font_ctx,
|
font_ctx: self.font_ctx,
|
||||||
opts: &self.opts
|
opts: &self.opts
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply the translation to render the tile we want.
|
// Apply the translation to render the tile we want.
|
||||||
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
||||||
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
|
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
|
||||||
|
@ -234,7 +234,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
|
|
||||||
debug!("render_task: returning surface");
|
debug!("render_task: returning surface");
|
||||||
if self.paint_permission {
|
if self.paint_permission {
|
||||||
self.compositor.paint(self.id, layer_buffer_set.clone(), render_layer.size);
|
self.compositor.paint(id, layer_buffer_set.clone());
|
||||||
}
|
}
|
||||||
debug!("caching paint msg");
|
debug!("caching paint msg");
|
||||||
self.last_paint_msg = Some((layer_buffer_set, render_layer.size));
|
self.last_paint_msg = Some((layer_buffer_set, render_layer.size));
|
||||||
|
|
386
src/components/main/compositing/compositor_layer.rs
Normal file
386
src/components/main/compositing/compositor_layer.rs
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
use geom::point::Point2D;
|
||||||
|
use geom::size::Size2D;
|
||||||
|
use geom::rect::Rect;
|
||||||
|
use geom::matrix::identity;
|
||||||
|
use gfx::render_task::ReRenderMsg;
|
||||||
|
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet};
|
||||||
|
use servo_msg::constellation_msg::PipelineId;
|
||||||
|
use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent};
|
||||||
|
use script::script_task::SendEventMsg;
|
||||||
|
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
||||||
|
use compositing::quadtree::Quadtree;
|
||||||
|
use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager};
|
||||||
|
use pipeline::Pipeline;
|
||||||
|
|
||||||
|
/// The CompositorLayer represents an element on a page that has a unique scroll
|
||||||
|
/// or animation behavior. This can include absolute positioned elements, iframes, etc.
|
||||||
|
/// Each layer can also have child layers.
|
||||||
|
pub struct CompositorLayer {
|
||||||
|
/// This layer's pipeline. BufferRequests and mouse events will be sent through this.
|
||||||
|
pipeline: Pipeline,
|
||||||
|
/// The size of the underlying page in page coordinates. This is an option
|
||||||
|
/// because we may not know the size of the page until layout is finished completely.
|
||||||
|
/// if we have no size yet, the layer is hidden until a size message is recieved.
|
||||||
|
page_size: Option<Size2D<f32>>,
|
||||||
|
/// The offset of the page due to scrolling. (0,0) is when the window sees the
|
||||||
|
/// top left corner of the page.
|
||||||
|
scroll_offset: Point2D<f32>,
|
||||||
|
/// This layer's children. These could be iframes or any element which
|
||||||
|
/// differs in scroll behavior from its parent. Each is associated with a
|
||||||
|
/// ContainerLayer which determines its position relative to its parent and
|
||||||
|
/// clipping rect. Children are stored in the order in which they are drawn.
|
||||||
|
children: ~[CompositorLayerChild],
|
||||||
|
/// This layer's quadtree. This is where all buffers are stored for this layer.
|
||||||
|
quadtree: MaybeQuadtree,
|
||||||
|
/// The root layer of this CompositorLayer's layer tree. Buffers are collected
|
||||||
|
/// from the quadtree and inserted here when the layer is painted to the screen.
|
||||||
|
root_layer: @mut ContainerLayer,
|
||||||
|
/// When set to true, this layer is ignored by its parents. This is useful for
|
||||||
|
/// soft deletion or when waiting on a page size.
|
||||||
|
hidden: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper struct for keeping CompositorLayer children organized.
|
||||||
|
struct CompositorLayerChild {
|
||||||
|
/// The child itself.
|
||||||
|
child: ~CompositorLayer,
|
||||||
|
/// A ContainerLayer managed by the parent node. This deals with clipping and
|
||||||
|
/// positioning, and is added above the child's layer tree.
|
||||||
|
container: @mut ContainerLayer,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper enum for storing quadtrees. Either contains a quadtree, or contains
|
||||||
|
/// information from which a quadtree can be built.
|
||||||
|
enum MaybeQuadtree {
|
||||||
|
Tree(Quadtree<~LayerBuffer>),
|
||||||
|
NoTree(uint, Option<uint>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompositorLayer {
|
||||||
|
/// Creates a new CompositorLayer without a page size that is initially hidden.
|
||||||
|
pub fn new(pipeline: Pipeline, page_size: Option<Size2D<f32>>, tile_size: uint, max_mem: Option<uint>)
|
||||||
|
-> CompositorLayer {
|
||||||
|
CompositorLayer {
|
||||||
|
pipeline: pipeline,
|
||||||
|
page_size: page_size,
|
||||||
|
scroll_offset: Point2D(0f32, 0f32),
|
||||||
|
children: ~[],
|
||||||
|
quadtree: match page_size {
|
||||||
|
None => NoTree(tile_size, max_mem),
|
||||||
|
Some(page_size) => Tree(Quadtree::new(page_size.width as uint,
|
||||||
|
page_size.height as uint,
|
||||||
|
tile_size,
|
||||||
|
max_mem)),
|
||||||
|
},
|
||||||
|
root_layer: @mut ContainerLayer(),
|
||||||
|
hidden: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the layer by as relative specified amount in page coordinates. Does not change
|
||||||
|
// the position of the layer relative to its parent. This also takes in a cursor position
|
||||||
|
// to see if the mouse is over child layers first. If a layer successfully scrolled, returns
|
||||||
|
// true; otherwise returns false, so a parent layer can scroll instead.
|
||||||
|
pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>) -> bool {
|
||||||
|
let cursor = cursor - self.scroll_offset;
|
||||||
|
for self.children.mut_iter().filter(|x| !x.child.hidden).advance |child| {
|
||||||
|
match child.container.scissor {
|
||||||
|
None => {
|
||||||
|
error!("CompositorLayer: unable to perform cursor hit test for layer");
|
||||||
|
}
|
||||||
|
Some(rect) => {
|
||||||
|
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
|
||||||
|
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height
|
||||||
|
&& child.child.scroll(delta, cursor - rect.origin, rect.size) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_origin = self.scroll_offset;
|
||||||
|
self.scroll_offset = self.scroll_offset + delta;
|
||||||
|
|
||||||
|
// bounds checking
|
||||||
|
let page_size = match self.page_size {
|
||||||
|
Some(size) => size,
|
||||||
|
None => fail!("CompositorLayer: tried to scroll with no page size set"),
|
||||||
|
};
|
||||||
|
let min_x = (window_size.width - page_size.width).min(&0.0);
|
||||||
|
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
|
||||||
|
let min_y = (window_size.height - page_size.height).min(&0.0);
|
||||||
|
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
|
||||||
|
|
||||||
|
// check to see if we scrolled
|
||||||
|
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.root_layer.common.set_transform(identity().translate(self.scroll_offset.x,
|
||||||
|
self.scroll_offset.y,
|
||||||
|
0.0));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes in a MouseWindowEvent, determines if it should be passed to children, and
|
||||||
|
// sends the event off to the appropriate pipeline. NB: the cursor position is in
|
||||||
|
// page coordinates.
|
||||||
|
pub fn send_mouse_event(&self, event: MouseWindowEvent, cursor: Point2D<f32>) {
|
||||||
|
let cursor = cursor - self.scroll_offset;
|
||||||
|
for self.children.iter().filter(|&x| !x.child.hidden).advance |child| {
|
||||||
|
match child.container.scissor {
|
||||||
|
None => {
|
||||||
|
error!("CompositorLayer: unable to perform cursor hit test for layer");
|
||||||
|
}
|
||||||
|
Some(rect) => {
|
||||||
|
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
|
||||||
|
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height {
|
||||||
|
child.child.send_mouse_event(event, cursor - rect.origin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This mouse event is mine!
|
||||||
|
let message = match event {
|
||||||
|
MouseWindowClickEvent(button, _) => ClickEvent(button, cursor),
|
||||||
|
MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor),
|
||||||
|
MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.pipeline.script_chan.send(SendEventMsg(self.pipeline.id.clone(), message));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given the current window size, determine which tiles need to be (re)rendered
|
||||||
|
// and sends them off the the appropriate renderer.
|
||||||
|
// Returns a bool that is true if the scene should be repainted.
|
||||||
|
pub fn get_buffer_request(&mut self, window_rect: Rect<f32>, scale: f32) -> bool {
|
||||||
|
let rect = Rect(Point2D(-self.scroll_offset.x + window_rect.origin.x,
|
||||||
|
-self.scroll_offset.y + window_rect.origin.y),
|
||||||
|
window_rect.size);
|
||||||
|
let mut redisplay: bool;
|
||||||
|
{ // block here to prevent double mutable borrow of self
|
||||||
|
let quadtree = match self.quadtree {
|
||||||
|
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
|
||||||
|
Tree(ref mut quadtree) => quadtree,
|
||||||
|
};
|
||||||
|
let (request, r) = quadtree.get_tile_rects_page(rect, scale);
|
||||||
|
redisplay = r; // workaround to make redisplay visible outside block
|
||||||
|
if !request.is_empty() {
|
||||||
|
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if redisplay {
|
||||||
|
self.build_layer_tree();
|
||||||
|
}
|
||||||
|
let transform = |x: &mut CompositorLayerChild| -> bool {
|
||||||
|
match x.container.scissor {
|
||||||
|
Some(scissor) => {
|
||||||
|
let new_rect = window_rect.intersection(&scissor);
|
||||||
|
match new_rect {
|
||||||
|
Some(new_rect) => {
|
||||||
|
x.child.get_buffer_request(new_rect, scale)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
false //Layer is offscreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
fail!("CompositorLayer: Child layer not clipped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.children.mut_iter().filter(|x| !x.child.hidden)
|
||||||
|
.transform(transform)
|
||||||
|
.fold(false, |a, b| a || b) || redisplay
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Move the sublayer to an absolute position in page coordinates relative to its parent,
|
||||||
|
// and clip the layer to the specified size in page coordinates.
|
||||||
|
// This method returns false if the specified layer is not found.
|
||||||
|
pub fn set_clipping_rect(&mut self, pipeline_id: PipelineId, new_rect: Rect<f32>) -> bool {
|
||||||
|
for self.children.iter().advance |child_node| {
|
||||||
|
if pipeline_id != child_node.child.pipeline.id {
|
||||||
|
loop;
|
||||||
|
}
|
||||||
|
let con = child_node.container;
|
||||||
|
con.common.set_transform(identity().translate(new_rect.origin.x,
|
||||||
|
new_rect.origin.y,
|
||||||
|
0.0));
|
||||||
|
con.scissor = Some(new_rect);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID does not match any of our immediate children, so recurse on descendents (including hidden children)
|
||||||
|
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.set_clipping_rect(pipeline_id, new_rect))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Called when the layer changes size (NOT as a result of a zoom event).
|
||||||
|
// This method returns false if the specified layer is not found.
|
||||||
|
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>) -> bool {
|
||||||
|
if self.pipeline.id == pipeline_id {
|
||||||
|
self.page_size = Some(new_size);
|
||||||
|
// TODO: might get buffers back here
|
||||||
|
match self.quadtree {
|
||||||
|
Tree(ref mut quadtree) => quadtree.resize(new_size.width as uint, new_size.height as uint),
|
||||||
|
NoTree(tile_size, max_mem) => self.quadtree = Tree(Quadtree::new(new_size.width as uint,
|
||||||
|
new_size.height as uint,
|
||||||
|
tile_size,
|
||||||
|
max_mem)),
|
||||||
|
}
|
||||||
|
// Call scroll for bounds checking of the page shrunk. Use (-1, -1) as the cursor position
|
||||||
|
// to make sure the scroll isn't propagated downwards.
|
||||||
|
self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID does not match ours, so recurse on descendents (including hidden children)
|
||||||
|
let transform = |x: &mut CompositorLayerChild| -> bool {
|
||||||
|
match x.container.scissor {
|
||||||
|
Some(scissor) => {
|
||||||
|
x.child.resize(pipeline_id, new_size, scissor.size)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
fail!("CompositorLayer: Child layer not clipped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.children.mut_iter().any(transform)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
|
||||||
|
// are not rebuilt directly from this method.
|
||||||
|
pub fn build_layer_tree(&mut self) {
|
||||||
|
// Iterate over the children of the container layer.
|
||||||
|
let mut current_layer_child = self.root_layer.first_child;
|
||||||
|
|
||||||
|
// Delete old layer.
|
||||||
|
while current_layer_child.is_some() {
|
||||||
|
let trash = current_layer_child.get();
|
||||||
|
do current_layer_child.get().with_common |common| {
|
||||||
|
current_layer_child = common.next_sibling;
|
||||||
|
}
|
||||||
|
self.root_layer.remove_child(trash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add child layers.
|
||||||
|
for self.children.mut_iter().filter(|x| !x.child.hidden).advance |child| {
|
||||||
|
current_layer_child = match current_layer_child {
|
||||||
|
None => {
|
||||||
|
child.container.common.parent = None;
|
||||||
|
child.container.common.prev_sibling = None;
|
||||||
|
child.container.common.next_sibling = None;
|
||||||
|
self.root_layer.add_child(ContainerLayerKind(child.container));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
fail!("CompositorLayer: Layer tree failed to delete");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new tiles.
|
||||||
|
let quadtree = match self.quadtree {
|
||||||
|
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
|
||||||
|
Tree(ref mut quadtree) => quadtree,
|
||||||
|
};
|
||||||
|
|
||||||
|
let all_tiles = quadtree.get_all_tiles();
|
||||||
|
for all_tiles.iter().advance |buffer| {
|
||||||
|
debug!("osmain: compositing buffer rect %?", &buffer.rect);
|
||||||
|
|
||||||
|
// Find or create a texture layer.
|
||||||
|
let texture_layer;
|
||||||
|
current_layer_child = match current_layer_child {
|
||||||
|
None => {
|
||||||
|
debug!("osmain: adding new texture layer");
|
||||||
|
texture_layer = @mut TextureLayer::new(@buffer.draw_target.clone() as @TextureManager,
|
||||||
|
buffer.screen_pos.size);
|
||||||
|
self.root_layer.add_child(TextureLayerKind(texture_layer));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Some(TextureLayerKind(existing_texture_layer)) => {
|
||||||
|
texture_layer = existing_texture_layer;
|
||||||
|
texture_layer.manager = @buffer.draw_target.clone() as @TextureManager;
|
||||||
|
|
||||||
|
// Move on to the next sibling.
|
||||||
|
do current_layer_child.get().with_common |common| {
|
||||||
|
common.next_sibling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(_) => fail!(~"found unexpected layer kind"),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let rect = buffer.rect;
|
||||||
|
// Set the layer's transform.
|
||||||
|
let transform = identity().translate(rect.origin.x, rect.origin.y, 0.0);
|
||||||
|
let transform = transform.scale(rect.size.width, rect.size.height, 1.0);
|
||||||
|
texture_layer.common.set_transform(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add LayerBuffers to the specified layer. Returns false if the layer is not found.
|
||||||
|
pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet) -> bool {
|
||||||
|
if self.pipeline.id == pipeline_id {
|
||||||
|
{ // block here to prevent double mutable borrow of self
|
||||||
|
let quadtree = match self.quadtree {
|
||||||
|
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
|
||||||
|
Tree(ref mut quadtree) => quadtree,
|
||||||
|
};
|
||||||
|
|
||||||
|
for new_buffers.buffers.iter().advance |buffer| {
|
||||||
|
// TODO: This may return old buffers, which should be sent back to the renderer.
|
||||||
|
quadtree.add_tile_pixel(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
|
||||||
|
buffer.resolution, ~buffer.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.build_layer_tree();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// ID does not match ours, so recurse on descendents (including hidden children).
|
||||||
|
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.
|
||||||
|
pub fn delete(&mut self, pipeline_id: PipelineId) -> bool {
|
||||||
|
match self.children.rposition(|x| x.child.pipeline.id == pipeline_id) {
|
||||||
|
Some(index) => {
|
||||||
|
// TODO: send buffers back to renderer when layer is deleted
|
||||||
|
self.children.remove(index);
|
||||||
|
self.build_layer_tree();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.delete(pipeline_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a child.
|
||||||
|
pub fn add_child(&mut self, pipeline: Pipeline, page_size: Option<Size2D<f32>>, tile_size: uint,
|
||||||
|
max_mem: Option<uint>, clipping_rect: Rect<f32>) {
|
||||||
|
let container = @mut ContainerLayer();
|
||||||
|
container.scissor = Some(clipping_rect);
|
||||||
|
container.common.set_transform(identity().translate(clipping_rect.origin.x,
|
||||||
|
clipping_rect.origin.y,
|
||||||
|
0.0));
|
||||||
|
let child = ~CompositorLayer::new(pipeline, page_size, tile_size, max_mem);
|
||||||
|
container.add_child(ContainerLayerKind(child.root_layer));
|
||||||
|
self.children.push(CompositorLayerChild {
|
||||||
|
child: child,
|
||||||
|
container: container,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,19 +3,19 @@
|
||||||
* 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 platform::{Application, Window};
|
use platform::{Application, Window};
|
||||||
use script::dom::event::{Event_, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent};
|
use script::dom::event::ResizeEvent;
|
||||||
use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg};
|
use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg};
|
||||||
|
|
||||||
|
pub use windowing;
|
||||||
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
|
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
|
||||||
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
|
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
|
||||||
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
|
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
|
||||||
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
||||||
|
|
||||||
use servo_msg::compositor_msg::{RenderListener, LayerBuffer, LayerBufferSet, RenderState};
|
use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState};
|
||||||
use servo_msg::compositor_msg::{ReadyState, ScriptListener};
|
use servo_msg::compositor_msg::{ReadyState, ScriptListener};
|
||||||
use servo_msg::constellation_msg::PipelineId;
|
use servo_msg::constellation_msg::PipelineId;
|
||||||
use servo_msg::constellation_msg;
|
use servo_msg::constellation_msg;
|
||||||
use gfx::render_task::ReRenderMsg;
|
|
||||||
use gfx::opts::Opts;
|
use gfx::opts::Opts;
|
||||||
|
|
||||||
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
|
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
|
||||||
|
@ -35,7 +35,6 @@ use geom::size::Size2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
|
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
|
||||||
use layers::layers::{ImageData, WithDataFn};
|
use layers::layers::{ImageData, WithDataFn};
|
||||||
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
|
|
||||||
use layers::rendergl;
|
use layers::rendergl;
|
||||||
use layers::scene::Scene;
|
use layers::scene::Scene;
|
||||||
use opengles::gl2;
|
use opengles::gl2;
|
||||||
|
@ -44,14 +43,16 @@ use servo_util::{time, url};
|
||||||
use servo_util::time::profile;
|
use servo_util::time::profile;
|
||||||
use servo_util::time::ProfilerChan;
|
use servo_util::time::ProfilerChan;
|
||||||
|
|
||||||
use extra::arc;
|
|
||||||
pub use windowing;
|
|
||||||
|
|
||||||
use extra::time::precise_time_s;
|
use extra::time::precise_time_s;
|
||||||
use compositing::quadtree::Quadtree;
|
use extra::arc;
|
||||||
|
|
||||||
use constellation::SendableFrameTree;
|
use constellation::SendableFrameTree;
|
||||||
use pipeline::Pipeline;
|
use pipeline::Pipeline;
|
||||||
|
use compositing::compositor_layer::CompositorLayer;
|
||||||
|
|
||||||
mod quadtree;
|
mod quadtree;
|
||||||
|
mod compositor_layer;
|
||||||
|
|
||||||
|
|
||||||
/// The implementation of the layers-based compositor.
|
/// The implementation of the layers-based compositor.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
|
@ -79,18 +80,18 @@ impl RenderListener for CompositorChan {
|
||||||
port.recv()
|
port.recv()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>) {
|
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>) {
|
||||||
self.chan.send(Paint(id, layer_buffer_set, new_size))
|
self.chan.send(Paint(id, layer_buffer_set))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_layer(&self, page_size: Size2D<uint>, tile_size: uint) {
|
fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
|
||||||
self.chan.send(NewLayer(page_size, tile_size))
|
self.chan.send(NewLayer(id, page_size))
|
||||||
}
|
}
|
||||||
fn resize_layer(&self, page_size: Size2D<uint>) {
|
fn resize_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
|
||||||
self.chan.send(ResizeLayer(page_size))
|
self.chan.send(ResizeLayer(id, page_size))
|
||||||
}
|
}
|
||||||
fn delete_layer(&self) {
|
fn delete_layer(&self, id: PipelineId) {
|
||||||
self.chan.send(DeleteLayer)
|
self.chan.send(DeleteLayer(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_render_state(&self, render_state: RenderState) {
|
fn set_render_state(&self, render_state: RenderState) {
|
||||||
|
@ -126,16 +127,16 @@ pub enum Msg {
|
||||||
/// Requests the compositors GL context.
|
/// Requests the compositors GL context.
|
||||||
GetGLContext(Chan<AzGLContext>),
|
GetGLContext(Chan<AzGLContext>),
|
||||||
|
|
||||||
// TODO: Attach layer ids and epochs to these messages
|
// TODO: Attach epochs to these messages
|
||||||
/// Alerts the compositor that there is a new layer to be rendered.
|
/// Alerts the compositor that there is a new layer to be rendered.
|
||||||
NewLayer(Size2D<uint>, uint),
|
NewLayer(PipelineId, Size2D<uint>),
|
||||||
/// Alerts the compositor that the current layer has changed size.
|
/// Alerts the compositor that the specified layer has changed size.
|
||||||
ResizeLayer(Size2D<uint>),
|
ResizeLayer(PipelineId, Size2D<uint>),
|
||||||
/// Alerts the compositor that the current layer has been deleted.
|
/// Alerts the compositor that the specified layer has been deleted.
|
||||||
DeleteLayer,
|
DeleteLayer(PipelineId),
|
||||||
|
|
||||||
/// Requests that the compositor paint the given layer buffer set for the given page size.
|
/// Requests that the compositor paint the given layer buffer set for the given page size.
|
||||||
Paint(PipelineId, arc::ARC<LayerBufferSet>, Size2D<uint>),
|
Paint(PipelineId, arc::ARC<LayerBufferSet>),
|
||||||
/// Alerts the compositor to the current status of page loading.
|
/// Alerts the compositor to the current status of page loading.
|
||||||
ChangeReadyState(ReadyState),
|
ChangeReadyState(ReadyState),
|
||||||
/// Alerts the compositor to the current status of rendering.
|
/// Alerts the compositor to the current status of rendering.
|
||||||
|
@ -220,119 +221,30 @@ impl CompositorTask {
|
||||||
let root_layer = @mut ContainerLayer();
|
let root_layer = @mut ContainerLayer();
|
||||||
let window_size = window.size();
|
let window_size = window.size();
|
||||||
let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity());
|
let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity());
|
||||||
|
let mut window_size = Size2D(window_size.width as int, window_size.height as int);
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
let mut recomposite = false;
|
let mut recomposite = false;
|
||||||
|
|
||||||
// FIXME: This should not be a separate offset applied after the fact but rather should be
|
|
||||||
// applied to the layers themselves on a per-layer basis. However, this won't work until scroll
|
|
||||||
// positions are sent to content.
|
|
||||||
let mut world_offset = Point2D(0f32, 0f32);
|
|
||||||
let mut page_size = Size2D(0f32, 0f32);
|
|
||||||
let mut window_size = Size2D(window_size.width as int,
|
|
||||||
window_size.height as int);
|
|
||||||
|
|
||||||
// Keeps track of the current zoom factor
|
// Keeps track of the current zoom factor
|
||||||
let mut world_zoom = 1f32;
|
let mut world_zoom = 1f32;
|
||||||
// Keeps track of local zoom factor. Reset to 1 after a rerender event.
|
|
||||||
let mut local_zoom = 1f32;
|
|
||||||
// Channel to the outermost frame's pipeline.
|
|
||||||
// FIXME: Compositor currently only asks for tiles to composite from this pipeline,
|
|
||||||
// Subframes need to be handled, as well. Additionally, events are only forwarded
|
|
||||||
// to this pipeline, but they should be routed to the appropriate pipeline via
|
|
||||||
// the constellation.
|
|
||||||
let mut pipeline: Option<Pipeline> = None;
|
|
||||||
|
|
||||||
// Quadtree for this layer
|
|
||||||
// FIXME: This should be one-per-layer
|
|
||||||
let mut quadtree: Option<Quadtree<~LayerBuffer>> = None;
|
|
||||||
|
|
||||||
// Keeps track of if we have performed a zoom event and how recently.
|
|
||||||
let mut zoom_action = false;
|
let mut zoom_action = false;
|
||||||
let mut zoom_time = 0f;
|
let mut zoom_time = 0f;
|
||||||
|
|
||||||
// Extract tiles from the given quadtree and build and display the render tree.
|
// Channel to the outermost frame's pipeline.
|
||||||
let build_layer_tree: &fn(&Quadtree<~LayerBuffer>) = |quad: &Quadtree<~LayerBuffer>| {
|
// FIXME: Events are only forwarded to this pipeline, but they should be
|
||||||
// Iterate over the children of the container layer.
|
// routed to the appropriate pipeline via the constellation.
|
||||||
let mut current_layer_child = root_layer.first_child;
|
let mut pipeline: Option<Pipeline> = None;
|
||||||
|
|
||||||
// Delete old layer
|
|
||||||
while current_layer_child.is_some() {
|
|
||||||
let trash = current_layer_child.get();
|
|
||||||
do current_layer_child.get().with_common |common| {
|
|
||||||
current_layer_child = common.next_sibling;
|
|
||||||
}
|
|
||||||
root_layer.remove_child(trash);
|
|
||||||
}
|
|
||||||
|
|
||||||
let all_tiles = quad.get_all_tiles();
|
// The root CompositorLayer
|
||||||
for all_tiles.iter().advance |buffer| {
|
let mut compositor_layer: Option<CompositorLayer> = None;
|
||||||
let width = buffer.screen_pos.size.width as uint;
|
|
||||||
let height = buffer.screen_pos.size.height as uint;
|
|
||||||
debug!("osmain: compositing buffer rect %?", &buffer.rect);
|
|
||||||
|
|
||||||
// Find or create a texture layer.
|
|
||||||
let texture_layer;
|
|
||||||
current_layer_child = match current_layer_child {
|
|
||||||
None => {
|
|
||||||
debug!("osmain: adding new texture layer");
|
|
||||||
texture_layer = @mut TextureLayer::new(@buffer.draw_target.clone() as @TextureManager,
|
|
||||||
buffer.screen_pos.size);
|
|
||||||
root_layer.add_child(TextureLayerKind(texture_layer));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Some(TextureLayerKind(existing_texture_layer)) => {
|
|
||||||
texture_layer = existing_texture_layer;
|
|
||||||
texture_layer.manager = @buffer.draw_target.clone() as @TextureManager;
|
|
||||||
|
|
||||||
// Move on to the next sibling.
|
|
||||||
do current_layer_child.get().with_common |common| {
|
|
||||||
common.next_sibling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(_) => fail!(~"found unexpected layer kind"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let origin = buffer.rect.origin;
|
|
||||||
let origin = Point2D(origin.x as f32, origin.y as f32);
|
|
||||||
|
|
||||||
// Set the layer's transform.
|
|
||||||
let transform = identity().translate(origin.x * world_zoom, origin.y * world_zoom, 0.0);
|
|
||||||
let transform = transform.scale(width as f32 * world_zoom / buffer.resolution, height as f32 * world_zoom / buffer.resolution, 1.0);
|
|
||||||
texture_layer.common.set_transform(transform);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset zoom
|
|
||||||
local_zoom = 1f32;
|
|
||||||
root_layer.common.set_transform(identity().translate(-world_offset.x,
|
|
||||||
-world_offset.y,
|
|
||||||
0.0));
|
|
||||||
recomposite = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// Get BufferRequests from each layer.
|
||||||
let ask_for_tiles = || {
|
let ask_for_tiles = || {
|
||||||
match quadtree {
|
let window_size_page = Size2D(window_size.width as f32 / world_zoom,
|
||||||
Some(ref mut quad) => {
|
window_size.height as f32 / world_zoom);
|
||||||
let (tile_request, redisplay) = quad.get_tile_rects(Rect(Point2D(world_offset.x as int,
|
for compositor_layer.mut_iter().advance |layer| {
|
||||||
world_offset.y as int),
|
recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
|
||||||
window_size), world_zoom);
|
world_zoom) || recomposite;
|
||||||
|
|
||||||
if !tile_request.is_empty() {
|
|
||||||
match pipeline {
|
|
||||||
Some(ref pipeline) => {
|
|
||||||
pipeline.render_chan.send(ReRenderMsg(tile_request, world_zoom));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println("Warning: Compositor: Cannot send tile request, no render chan initialized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if redisplay {
|
|
||||||
build_layer_tree(quad);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
fail!("Compositor: Tried to ask for tiles without an initialized quadtree");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -357,46 +269,66 @@ impl CompositorTask {
|
||||||
|
|
||||||
GetGLContext(chan) => chan.send(current_gl_context()),
|
GetGLContext(chan) => chan.send(current_gl_context()),
|
||||||
|
|
||||||
NewLayer(new_size, tile_size) => {
|
NewLayer(_id, new_size) => {
|
||||||
page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
// FIXME: This should create an additional layer instead of replacing the current one.
|
||||||
quadtree = Some(Quadtree::new(new_size.width.max(&(window_size.width as uint)),
|
// Once ResizeLayer messages are set up, we can switch to the new functionality.
|
||||||
new_size.height.max(&(window_size.height as uint)),
|
|
||||||
tile_size, Some(10000000u)));
|
let p = match pipeline {
|
||||||
ask_for_tiles();
|
Some(ref pipeline) => pipeline,
|
||||||
|
None => fail!("Compositor: Received new layer without initialized pipeline"),
|
||||||
|
};
|
||||||
|
let page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||||
|
let new_layer = CompositorLayer::new(p.clone(), Some(page_size),
|
||||||
|
self.opts.tile_size, Some(10000000u));
|
||||||
|
|
||||||
}
|
let current_child = root_layer.first_child;
|
||||||
ResizeLayer(new_size) => {
|
// This assumes there is at most one child, which should be the case.
|
||||||
page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
match current_child {
|
||||||
// TODO: update quadtree, ask for tiles
|
Some(old_layer) => root_layer.remove_child(old_layer),
|
||||||
}
|
None => {}
|
||||||
DeleteLayer => {
|
}
|
||||||
// TODO: create secondary layer tree, keep displaying until new tiles come in
|
root_layer.add_child(ContainerLayerKind(new_layer.root_layer));
|
||||||
|
compositor_layer = Some(new_layer);
|
||||||
|
|
||||||
|
ask_for_tiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint(id, new_layer_buffer_set, new_size) => {
|
ResizeLayer(id, new_size) => {
|
||||||
match pipeline {
|
match compositor_layer {
|
||||||
Some(ref pipeline) => if id != pipeline.id { loop; },
|
Some(ref mut layer) => {
|
||||||
None => { loop; },
|
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||||
|
window_size.height as f32 / world_zoom);
|
||||||
|
assert!(layer.resize(id, Size2D(new_size.width as f32,
|
||||||
|
new_size.height as f32),
|
||||||
|
page_window));
|
||||||
|
ask_for_tiles();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteLayer(id) => {
|
||||||
|
match compositor_layer {
|
||||||
|
Some(ref mut layer) => {
|
||||||
|
assert!(layer.delete(id));
|
||||||
|
ask_for_tiles();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Paint(id, new_layer_buffer_set) => {
|
||||||
debug!("osmain: received new frame");
|
debug!("osmain: received new frame");
|
||||||
|
|
||||||
let quad;
|
match compositor_layer {
|
||||||
match quadtree {
|
Some(ref mut layer) => {
|
||||||
Some(ref mut q) => quad = q,
|
assert!(layer.add_buffers(id, new_layer_buffer_set.get()));
|
||||||
None => fail!("Compositor: given paint command with no quadtree initialized"),
|
recomposite = true;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
fail!("Compositor: given paint command with no CompositorLayer initialized");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_layer_buffer_set = new_layer_buffer_set.get();
|
|
||||||
for new_layer_buffer_set.buffers.iter().advance |buffer| {
|
|
||||||
// FIXME: Don't copy the buffers here
|
|
||||||
quad.add_tile(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
|
|
||||||
buffer.resolution, ~buffer.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
|
||||||
|
|
||||||
build_layer_tree(quad);
|
|
||||||
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
|
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
|
||||||
// it wishes.
|
// it wishes.
|
||||||
}
|
}
|
||||||
|
@ -431,56 +363,27 @@ impl CompositorTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseWindowEventClass(mouse_window_event) => {
|
MouseWindowEventClass(mouse_window_event) => {
|
||||||
let event: Event_;
|
let point = match mouse_window_event {
|
||||||
let world_mouse_point = |layer_mouse_point: Point2D<f32>| {
|
MouseWindowClickEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
||||||
layer_mouse_point + world_offset
|
MouseWindowMouseDownEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
||||||
|
MouseWindowMouseUpEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
||||||
};
|
};
|
||||||
match mouse_window_event {
|
for compositor_layer.iter().advance |layer| {
|
||||||
MouseWindowClickEvent(button, layer_mouse_point) => {
|
layer.send_mouse_event(mouse_window_event, point);
|
||||||
event = ClickEvent(button, world_mouse_point(layer_mouse_point));
|
|
||||||
}
|
|
||||||
MouseWindowMouseDownEvent(button, layer_mouse_point) => {
|
|
||||||
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
|
|
||||||
}
|
|
||||||
MouseWindowMouseUpEvent(button, layer_mouse_point) => {
|
|
||||||
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match pipeline {
|
|
||||||
Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), event)),
|
|
||||||
None => error!("Compositor: Recieved mouse event without initialized layout chan"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollWindowEvent(delta) => {
|
ScrollWindowEvent(delta, cursor) => {
|
||||||
// FIXME (Rust #2528): Can't use `-=`.
|
// TODO: modify delta to snap scroll to pixels.
|
||||||
let world_offset_copy = world_offset;
|
let page_delta = Point2D(delta.x as f32 / world_zoom, delta.y as f32 / world_zoom);
|
||||||
world_offset = world_offset_copy - delta;
|
let page_cursor: Point2D<f32> = Point2D(cursor.x as f32 / world_zoom,
|
||||||
|
cursor.y as f32 / world_zoom);
|
||||||
// Clamp the world offset to the screen size.
|
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||||
let max_x = (page_size.width * world_zoom - window_size.width as f32).max(&0.0);
|
window_size.height as f32 / world_zoom);
|
||||||
world_offset.x = world_offset.x.clamp(&0.0, &max_x).round();
|
for compositor_layer.mut_iter().advance |layer| {
|
||||||
let max_y = (page_size.height * world_zoom - window_size.height as f32).max(&0.0);
|
recomposite = layer.scroll(page_delta, page_cursor, page_window) || recomposite;
|
||||||
world_offset.y = world_offset.y.clamp(&0.0, &max_y).round();
|
}
|
||||||
|
|
||||||
debug!("compositor: scrolled to %?", world_offset);
|
|
||||||
|
|
||||||
|
|
||||||
let mut scroll_transform = identity();
|
|
||||||
|
|
||||||
scroll_transform = scroll_transform.translate(window_size.width as f32 / 2f32 * local_zoom - world_offset.x,
|
|
||||||
window_size.height as f32 / 2f32 * local_zoom - world_offset.y,
|
|
||||||
0.0);
|
|
||||||
scroll_transform = scroll_transform.scale(local_zoom, local_zoom, 1f32);
|
|
||||||
scroll_transform = scroll_transform.translate(window_size.width as f32 / -2f32,
|
|
||||||
window_size.height as f32 / -2f32,
|
|
||||||
0.0);
|
|
||||||
|
|
||||||
root_layer.common.set_transform(scroll_transform);
|
|
||||||
|
|
||||||
ask_for_tiles();
|
ask_for_tiles();
|
||||||
|
|
||||||
recomposite = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoomWindowEvent(magnification) => {
|
ZoomWindowEvent(magnification) => {
|
||||||
|
@ -490,34 +393,19 @@ impl CompositorTask {
|
||||||
|
|
||||||
// Determine zoom amount
|
// Determine zoom amount
|
||||||
world_zoom = (world_zoom * magnification).max(&1.0);
|
world_zoom = (world_zoom * magnification).max(&1.0);
|
||||||
local_zoom = local_zoom * world_zoom/old_world_zoom;
|
root_layer.common.set_transform(identity().scale(world_zoom, world_zoom, 1f32));
|
||||||
|
|
||||||
// Update world offset
|
|
||||||
let corner_to_center_x = world_offset.x + window_size.width as f32 / 2f32;
|
|
||||||
let new_corner_to_center_x = corner_to_center_x * world_zoom / old_world_zoom;
|
|
||||||
world_offset.x = world_offset.x + new_corner_to_center_x - corner_to_center_x;
|
|
||||||
|
|
||||||
let corner_to_center_y = world_offset.y + window_size.height as f32 / 2f32;
|
|
||||||
let new_corner_to_center_y = corner_to_center_y * world_zoom / old_world_zoom;
|
|
||||||
world_offset.y = world_offset.y + new_corner_to_center_y - corner_to_center_y;
|
|
||||||
|
|
||||||
// Clamp to page bounds when zooming out
|
|
||||||
let max_x = (page_size.width * world_zoom - window_size.width as f32).max(&0.0);
|
|
||||||
world_offset.x = world_offset.x.clamp(&0.0, &max_x).round();
|
|
||||||
let max_y = (page_size.height * world_zoom - window_size.height as f32).max(&0.0);
|
|
||||||
world_offset.y = world_offset.y.clamp(&0.0, &max_y).round();
|
|
||||||
|
|
||||||
// Apply transformations
|
|
||||||
let mut zoom_transform = identity();
|
|
||||||
zoom_transform = zoom_transform.translate(window_size.width as f32 / 2f32 * local_zoom - world_offset.x,
|
|
||||||
window_size.height as f32 / 2f32 * local_zoom - world_offset.y,
|
|
||||||
0.0);
|
|
||||||
zoom_transform = zoom_transform.scale(local_zoom, local_zoom, 1f32);
|
|
||||||
zoom_transform = zoom_transform.translate(window_size.width as f32 / -2f32,
|
|
||||||
window_size.height as f32 / -2f32,
|
|
||||||
0.0);
|
|
||||||
root_layer.common.set_transform(zoom_transform);
|
|
||||||
|
|
||||||
|
// Scroll as needed
|
||||||
|
let page_delta = Point2D(window_size.width as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5,
|
||||||
|
window_size.height as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5);
|
||||||
|
// TODO: modify delta to snap scroll to pixels.
|
||||||
|
let page_cursor = Point2D(-1f32, -1f32); // Make sure this hits the base layer
|
||||||
|
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||||
|
window_size.height as f32 / world_zoom);
|
||||||
|
for compositor_layer.mut_iter().advance |layer| {
|
||||||
|
layer.scroll(page_delta, page_cursor, page_window);
|
||||||
|
}
|
||||||
|
|
||||||
recomposite = true;
|
recomposite = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use std::uint::{div_ceil, next_power_of_two};
|
use std::uint::{div_ceil, next_power_of_two, range};
|
||||||
use std::vec::build_sized;
|
use std::vec::build_sized;
|
||||||
use std::util::replace;
|
use std::util::replace;
|
||||||
use gfx::render_task::BufferRequest;
|
use gfx::render_task::BufferRequest;
|
||||||
|
@ -17,8 +17,17 @@ use servo_msg::compositor_msg::Tile;
|
||||||
/// Parent to all quadtree nodes. Stores variables needed at all levels. All method calls
|
/// Parent to all quadtree nodes. Stores variables needed at all levels. All method calls
|
||||||
/// at this level are in pixel coordinates.
|
/// at this level are in pixel coordinates.
|
||||||
pub struct Quadtree<T> {
|
pub struct Quadtree<T> {
|
||||||
root: QuadtreeNode<T>,
|
// The root node of the quadtree
|
||||||
|
root: ~QuadtreeNode<T>,
|
||||||
|
// The size of the layer in pixels. Tiles will be clipped to this size.
|
||||||
|
// Note that the underlying quadtree has a potentailly larger size, since it is rounded
|
||||||
|
// to the next highest power of two.
|
||||||
|
clip_size: Size2D<uint>,
|
||||||
|
// The maximum size of the tiles requested in pixels. Tiles requested will be
|
||||||
|
// of a size anywhere between half this value and this value.
|
||||||
max_tile_size: uint,
|
max_tile_size: uint,
|
||||||
|
// The maximum allowed total memory of tiles in the tree. If this limit is reached, tiles
|
||||||
|
// will be removed from the tree. Set this to None to prevent this behavior.
|
||||||
max_mem: Option<uint>,
|
max_mem: Option<uint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +67,7 @@ impl<T: Tile> Quadtree<T> {
|
||||||
let size = power_of_two * tile_size;
|
let size = power_of_two * tile_size;
|
||||||
|
|
||||||
Quadtree {
|
Quadtree {
|
||||||
root: QuadtreeNode {
|
root: ~QuadtreeNode {
|
||||||
tile: None,
|
tile: None,
|
||||||
origin: Point2D(0f32, 0f32),
|
origin: Point2D(0f32, 0f32),
|
||||||
size: size as f32,
|
size: size as f32,
|
||||||
|
@ -66,6 +75,7 @@ impl<T: Tile> Quadtree<T> {
|
||||||
render_flag: false,
|
render_flag: false,
|
||||||
tile_mem: 0,
|
tile_mem: 0,
|
||||||
},
|
},
|
||||||
|
clip_size: Size2D(width, height),
|
||||||
max_tile_size: tile_size,
|
max_tile_size: tile_size,
|
||||||
max_mem: max_mem,
|
max_mem: max_mem,
|
||||||
}
|
}
|
||||||
|
@ -76,13 +86,19 @@ impl<T: Tile> Quadtree<T> {
|
||||||
self.max_tile_size
|
self.max_tile_size
|
||||||
}
|
}
|
||||||
/// Get a tile at a given pixel position and scale.
|
/// Get a tile at a given pixel position and scale.
|
||||||
pub fn get_tile<'r>(&'r self, x: uint, y: uint, scale: f32) -> &'r Option<T> {
|
pub fn get_tile_pixel<'r>(&'r self, x: uint, y: uint, scale: f32) -> &'r Option<T> {
|
||||||
self.root.get_tile(x as f32 / scale, y as f32 / scale)
|
self.root.get_tile(x as f32 / scale, y as f32 / scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a tile at a given page position.
|
||||||
|
pub fn get_tile_page<'r>(&'r self, x: f32, y: f32) -> &'r Option<T> {
|
||||||
|
self.root.get_tile(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a tile associtated with a given pixel position and scale.
|
/// Add a tile associtated with a given pixel position and scale.
|
||||||
/// If the tile pushes the total memory over its maximum, tiles will be removed
|
/// If the tile pushes the total memory over its maximum, tiles will be removed
|
||||||
/// until total memory is below the maximum again.
|
/// until total memory is below the maximum again.
|
||||||
pub fn add_tile(&mut self, x: uint, y: uint, scale: f32, tile: T) {
|
pub fn add_tile_pixel(&mut self, x: uint, y: uint, scale: f32, tile: T) {
|
||||||
self.root.add_tile(x as f32 / scale, y as f32 / scale, tile, self.max_tile_size as f32 / scale);
|
self.root.add_tile(x as f32 / scale, y as f32 / scale, tile, self.max_tile_size as f32 / scale);
|
||||||
match self.max_mem {
|
match self.max_mem {
|
||||||
Some(max) => {
|
Some(max) => {
|
||||||
|
@ -97,36 +113,138 @@ impl<T: Tile> Quadtree<T> {
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the tile rect in screen and page coordinates for a given pixel position
|
|
||||||
pub fn get_tile_rect(&mut self, x: uint, y: uint, scale: f32) -> BufferRequest {
|
/// Add a tile associtated with a given page position.
|
||||||
self.root.get_tile_rect(x as f32 / scale, y as f32 / scale, scale, self.max_tile_size as f32 / scale)
|
/// If the tile pushes the total memory over its maximum, tiles will be removed
|
||||||
|
/// until total memory is below the maximum again.
|
||||||
|
pub fn add_tile_page(&mut self, x: f32, y: f32, scale: f32, tile: T) {
|
||||||
|
self.root.add_tile(x, y, tile, self.max_tile_size as f32 / scale);
|
||||||
|
match self.max_mem {
|
||||||
|
Some(max) => {
|
||||||
|
while self.root.tile_mem > max {
|
||||||
|
let r = self.root.remove_tile(x, y);
|
||||||
|
match r {
|
||||||
|
(Some(_), _, _) => {}
|
||||||
|
_ => fail!("Quadtree: No valid tiles to remove"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the tile rect in screen and page coordinates for a given pixel position
|
||||||
|
pub fn get_tile_rect_pixel(&mut self, x: uint, y: uint, scale: f32) -> BufferRequest {
|
||||||
|
self.root.get_tile_rect(x as f32 / scale, y as f32 / scale,
|
||||||
|
self.clip_size.width as f32,
|
||||||
|
self.clip_size.height as f32,
|
||||||
|
scale, self.max_tile_size as f32 / scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the tile rect in screen and page coordinates for a given page position
|
||||||
|
pub fn get_tile_rect_page(&mut self, x: f32, y: f32, scale: f32) -> BufferRequest {
|
||||||
|
self.root.get_tile_rect(x, y,
|
||||||
|
self.clip_size.width as f32,
|
||||||
|
self.clip_size.height as f32,
|
||||||
|
scale, self.max_tile_size as f32 / scale)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get all the tiles in the tree
|
/// Get all the tiles in the tree
|
||||||
pub fn get_all_tiles<'r>(&'r self) -> ~[&'r T] {
|
pub fn get_all_tiles<'r>(&'r self) -> ~[&'r T] {
|
||||||
self.root.get_all_tiles()
|
self.root.get_all_tiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ask a tile to be deleted from the quadtree. This tries to delete a tile that is far from the
|
/// Ask a tile to be deleted from the quadtree. This tries to delete a tile that is far from the
|
||||||
/// given point in pixel coordinates.
|
/// given point in pixel coordinates.
|
||||||
pub fn remove_tile(&mut self, x: uint, y: uint, scale: f32) -> T {
|
pub fn remove_tile_pixel(&mut self, x: uint, y: uint, scale: f32) -> T {
|
||||||
let r = self.root.remove_tile(x as f32 / scale, y as f32 / scale);
|
let r = self.root.remove_tile(x as f32 / scale, y as f32 / scale);
|
||||||
match r {
|
match r {
|
||||||
(Some(tile), _, _) => tile,
|
(Some(tile), _, _) => tile,
|
||||||
_ => fail!("Quadtree: No valid tiles to remove"),
|
_ => fail!("Quadtree: No valid tiles to remove"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ask a tile to be deleted from the quadtree. This tries to delete a tile that is far from the
|
||||||
|
/// given point in page coordinates.
|
||||||
|
pub fn remove_tile_page(&mut self, x: f32, y: f32) -> T {
|
||||||
|
let r = self.root.remove_tile(x, y);
|
||||||
|
match r {
|
||||||
|
(Some(tile), _, _) => tile,
|
||||||
|
_ => fail!("Quadtree: No valid tiles to remove"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a window rect in pixel coordinates, this function returns a list of BufferRequests for tiles that
|
/// Given a window rect in pixel coordinates, this function returns a list of BufferRequests for tiles that
|
||||||
/// need to be rendered. It also returns a boolean if the window needs to be redisplayed, i.e. if
|
/// need to be rendered. It also returns a boolean if the window needs to be redisplayed, i.e. if
|
||||||
/// no tiles need to be rendered, but the display tree needs to be rebuilt. This can occur when the
|
/// no tiles need to be rendered, but the display tree needs to be rebuilt. This can occur when the
|
||||||
/// user zooms out and cached tiles need to be displayed on top of higher resolution tiles.
|
/// user zooms out and cached tiles need to be displayed on top of higher resolution tiles.
|
||||||
/// When this happens, higher resolution tiles will be removed from the quadtree.
|
/// When this happens, higher resolution tiles will be removed from the quadtree.
|
||||||
pub fn get_tile_rects(&mut self, window: Rect<int>, scale: f32) -> (~[BufferRequest], bool) {
|
pub fn get_tile_rects_pixel(&mut self, window: Rect<int>, scale: f32) -> (~[BufferRequest], bool) {
|
||||||
let (ret, redisplay, _) = self.root.get_tile_rects(
|
let (ret, redisplay, _) = self.root.get_tile_rects(
|
||||||
Rect(Point2D(window.origin.x as f32 / scale, window.origin.y as f32 / scale),
|
Rect(Point2D(window.origin.x as f32 / scale, window.origin.y as f32 / scale),
|
||||||
Size2D(window.size.width as f32 / scale, window.size.height as f32 / scale)),
|
Size2D(window.size.width as f32 / scale, window.size.height as f32 / scale)),
|
||||||
|
Size2D(self.clip_size.width as f32, self.clip_size.height as f32),
|
||||||
scale, self.max_tile_size as f32 / scale);
|
scale, self.max_tile_size as f32 / scale);
|
||||||
(ret, redisplay)
|
(ret, redisplay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same function as above, using page coordinates for the window
|
||||||
|
pub fn get_tile_rects_page(&mut self, window: Rect<f32>, scale: f32) -> (~[BufferRequest], bool) {
|
||||||
|
let (ret, redisplay, _) = self.root.get_tile_rects(
|
||||||
|
window,
|
||||||
|
Size2D(self.clip_size.width as f32, self.clip_size.height as f32),
|
||||||
|
scale, self.max_tile_size as f32 / scale);
|
||||||
|
(ret, redisplay)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Resize the quadtree. This can add more space, changing the root node, or it can shrink, making
|
||||||
|
/// an internal node the new root.
|
||||||
|
/// TODO: return tiles after shrinking
|
||||||
|
pub fn resize(&mut self, width: uint, height: uint) {
|
||||||
|
self.clip_size = Size2D(width, height);
|
||||||
|
let longer = width.max(&height);
|
||||||
|
let new_num_tiles = div_ceil(longer, self.max_tile_size);
|
||||||
|
let new_size = next_power_of_two(new_num_tiles);
|
||||||
|
// difference here indicates the number of times the underlying size of the quadtree needs
|
||||||
|
// to be doubled or halved. It will recursively add a new root if it is positive, or
|
||||||
|
// recursivly make a child the new root if it is negative.
|
||||||
|
let difference = (new_size as f32 / self.root.size as f32).log2() as int;
|
||||||
|
if difference > 0 { // doubling
|
||||||
|
let difference = difference as uint;
|
||||||
|
for range(0, difference) |i| {
|
||||||
|
let new_root = ~QuadtreeNode {
|
||||||
|
tile: None,
|
||||||
|
origin: Point2D(0f32, 0f32),
|
||||||
|
size: new_size as f32 / ((difference - i - 1) as f32).exp2(),
|
||||||
|
quadrants: [None, None, None, None],
|
||||||
|
render_flag: false,
|
||||||
|
tile_mem: self.root.tile_mem,
|
||||||
|
};
|
||||||
|
self.root.quadrants[TL as int] = Some(replace(&mut self.root, new_root));
|
||||||
|
}
|
||||||
|
} else if difference < 0 { // halving
|
||||||
|
let difference = difference.abs() as uint;
|
||||||
|
for difference.times {
|
||||||
|
let remove = replace(&mut self.root.quadrants[TL as int], None);
|
||||||
|
match remove {
|
||||||
|
Some(child) => self.root = child,
|
||||||
|
None => {
|
||||||
|
self.root = ~QuadtreeNode {
|
||||||
|
tile: None,
|
||||||
|
origin: Point2D(0f32, 0f32),
|
||||||
|
size: new_size as f32,
|
||||||
|
quadrants: [None, None, None, None],
|
||||||
|
render_flag: false,
|
||||||
|
tile_mem: 0,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate html to visualize the tree. For debugging purposes only.
|
/// Generate html to visualize the tree. For debugging purposes only.
|
||||||
pub fn get_html(&self) -> ~str {
|
pub fn get_html(&self) -> ~str {
|
||||||
static HEADER: &'static str = "<!DOCTYPE html><html>";
|
static HEADER: &'static str = "<!DOCTYPE html><html>";
|
||||||
|
@ -248,19 +366,23 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a tile rect in screen and page coords for a given position in page coords
|
/// Get a tile rect in screen and page coords for a given position in page coords
|
||||||
fn get_tile_rect(&mut self, x: f32, y: f32, scale: f32, tile_size: f32) -> BufferRequest {
|
fn get_tile_rect(&mut self, x: f32, y: f32, clip_x: f32, clip_y: f32, scale: f32,
|
||||||
|
tile_size: f32) -> BufferRequest {
|
||||||
if x >= self.origin.x + self.size || x < self.origin.x
|
if x >= self.origin.x + self.size || x < self.origin.x
|
||||||
|| y >= self.origin.y + self.size || y < self.origin.y {
|
|| y >= self.origin.y + self.size || y < self.origin.y {
|
||||||
fail!("Quadtree: Tried to query a tile rect outside of range");
|
fail!("Quadtree: Tried to query a tile rect outside of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.size <= tile_size {
|
if self.size <= tile_size {
|
||||||
let self_x = (self.origin.x * scale).ceil() as uint;
|
let pix_x = (self.origin.x * scale).ceil() as uint;
|
||||||
let self_y = (self.origin.y * scale).ceil() as uint;
|
let pix_y = (self.origin.y * scale).ceil() as uint;
|
||||||
let self_size = (self.size * scale).ceil() as uint;
|
let page_width = (clip_x - self.origin.x).min(&self.size);
|
||||||
|
let page_height = (clip_y - self.origin.y).min(&self.size);
|
||||||
|
let pix_width = (page_width * scale).ceil() as uint;
|
||||||
|
let pix_height = (page_height * scale).ceil() as uint;
|
||||||
self.render_flag = true;
|
self.render_flag = true;
|
||||||
return BufferRequest(Rect(Point2D(self_x, self_y), Size2D(self_size, self_size)),
|
return BufferRequest(Rect(Point2D(pix_x, pix_y), Size2D(pix_width, pix_height)),
|
||||||
Rect(Point2D(self.origin.x, self.origin.y), Size2D(self.size, self.size)));
|
Rect(Point2D(self.origin.x, self.origin.y), Size2D(page_width, page_height)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let quad = self.get_quadrant(x,y);
|
let quad = self.get_quadrant(x,y);
|
||||||
|
@ -276,11 +398,11 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
BL | BR => self.origin.y + new_size,
|
BL | BR => self.origin.y + new_size,
|
||||||
};
|
};
|
||||||
let mut c = ~QuadtreeNode::new_child(new_x, new_y, new_size);
|
let mut c = ~QuadtreeNode::new_child(new_x, new_y, new_size);
|
||||||
let result = c.get_tile_rect(x, y, scale, tile_size);
|
let result = c.get_tile_rect(x, y, clip_x, clip_y, scale, tile_size);
|
||||||
self.quadrants[quad as int] = Some(c);
|
self.quadrants[quad as int] = Some(c);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
Some(ref mut child) => child.get_tile_rect(x, y, scale, tile_size),
|
Some(ref mut child) => child.get_tile_rect(x, y, clip_x, clip_y, scale, tile_size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +479,7 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
/// a redisplay boolean, and the difference in tile memory between the new and old quadtree nodes.
|
/// a redisplay boolean, and the difference in tile memory between the new and old quadtree nodes.
|
||||||
/// NOTE: this method will sometimes modify the tree by deleting tiles.
|
/// NOTE: this method will sometimes modify the tree by deleting tiles.
|
||||||
/// See the QuadTree function description for more details.
|
/// See the QuadTree function description for more details.
|
||||||
fn get_tile_rects(&mut self, window: Rect<f32>, scale: f32, tile_size: f32) ->
|
fn get_tile_rects(&mut self, window: Rect<f32>, clip: Size2D<f32>, scale: f32, tile_size: f32) ->
|
||||||
(~[BufferRequest], bool, int) {
|
(~[BufferRequest], bool, int) {
|
||||||
|
|
||||||
let w_x = window.origin.x;
|
let w_x = window.origin.x;
|
||||||
|
@ -368,13 +490,17 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
let s_y = self.origin.y;
|
let s_y = self.origin.y;
|
||||||
let s_size = self.size;
|
let s_size = self.size;
|
||||||
|
|
||||||
if w_x < s_x || w_x + w_width > s_x + s_size
|
// if window is outside of visible region, nothing to do
|
||||||
|| w_y < s_y || w_y + w_height > s_y + s_size {
|
if w_x + w_width < s_x || w_x > s_x + s_size
|
||||||
println(fmt!("window: %?, %?, %?, %?; self: %?, %?, %?",
|
|| w_y + w_height < s_y || w_y > s_y + s_size
|
||||||
w_x, w_y, w_width, w_height, s_x, s_y, s_size));
|
|| w_x >= clip.width || w_y >= clip.height {
|
||||||
fail!("Quadtree: tried to query an invalid tile rect");
|
return (~[], false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clip window to visible region
|
||||||
|
let w_width = (clip.width - w_x).min(&w_width);
|
||||||
|
let w_height = (clip.height - w_y).min(&w_height);
|
||||||
|
|
||||||
if s_size <= tile_size { // We are the child
|
if s_size <= tile_size { // We are the child
|
||||||
return match self.tile {
|
return match self.tile {
|
||||||
_ if self.render_flag => (~[], false, 0),
|
_ if self.render_flag => (~[], false, 0),
|
||||||
|
@ -397,7 +523,7 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
}
|
}
|
||||||
(~[], redisplay, delta)
|
(~[], redisplay, delta)
|
||||||
}
|
}
|
||||||
_ => (~[self.get_tile_rect(s_x, s_y, scale, tile_size)], false, 0),
|
_ => (~[self.get_tile_rect(s_x, s_y, clip.width, clip.height, scale, tile_size)], false, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +583,7 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (c_ret, c_redisplay, c_delta) = match self.quadrants[*quad as int] {
|
let (c_ret, c_redisplay, c_delta) = match self.quadrants[*quad as int] {
|
||||||
Some(ref mut child) => child.get_tile_rects(new_window, scale, tile_size),
|
Some(ref mut child) => child.get_tile_rects(new_window, clip, scale, tile_size),
|
||||||
None => {
|
None => {
|
||||||
// Create new child
|
// Create new child
|
||||||
let new_size = self.size / 2.0;
|
let new_size = self.size / 2.0;
|
||||||
|
@ -470,9 +596,9 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
BL | BR => self.origin.y + new_size,
|
BL | BR => self.origin.y + new_size,
|
||||||
};
|
};
|
||||||
let mut child = ~QuadtreeNode::new_child(new_x, new_y, new_size);
|
let mut child = ~QuadtreeNode::new_child(new_x, new_y, new_size);
|
||||||
let (a, b, c) = child.get_tile_rects(new_window, scale, tile_size);
|
let ret = child.get_tile_rects(new_window, clip, scale, tile_size);
|
||||||
self.quadrants[*quad as int] = Some(child);
|
self.quadrants[*quad as int] = Some(child);
|
||||||
(a, b, c)
|
ret
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -484,7 +610,6 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
(ret, redisplay, delta)
|
(ret, redisplay, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Generate html to visualize the tree.
|
/// Generate html to visualize the tree.
|
||||||
/// This is really inefficient, but it's for testing only.
|
/// This is really inefficient, but it's for testing only.
|
||||||
fn get_html(&self) -> ~str {
|
fn get_html(&self) -> ~str {
|
||||||
|
@ -525,6 +650,35 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_resize() {
|
||||||
|
struct T {
|
||||||
|
a: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tile for T {
|
||||||
|
fn get_mem(&self) -> uint {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid(&self, _: f32) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut q = Quadtree::new(6, 6, 1, None);
|
||||||
|
q.add_tile_pixel(0, 0, 1f32, T{a: 0});
|
||||||
|
q.add_tile_pixel(5, 5, 1f32, T{a: 1});
|
||||||
|
q.resize(8, 1);
|
||||||
|
assert!(q.root.size == 8.0);
|
||||||
|
q.resize(18, 1);
|
||||||
|
assert!(q.root.size == 32.0);
|
||||||
|
q.resize(8, 1);
|
||||||
|
assert!(q.root.size == 8.0);
|
||||||
|
q.resize(3, 1);
|
||||||
|
assert!(q.root.size == 4.0);
|
||||||
|
assert!(q.get_all_tiles().len() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test() {
|
pub fn test() {
|
||||||
|
@ -543,26 +697,26 @@ pub fn test() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut q = Quadtree::new(8, 8, 2, Some(4));
|
let mut q = Quadtree::new(8, 8, 2, Some(4));
|
||||||
q.add_tile(0, 0, 1f32, T{a: 0});
|
q.add_tile_pixel(0, 0, 1f32, T{a: 0});
|
||||||
q.add_tile(0, 0, 2f32, T{a: 1});
|
q.add_tile_pixel(0, 0, 2f32, T{a: 1});
|
||||||
q.add_tile(0, 0, 2f32, T{a: 2});
|
q.add_tile_pixel(0, 0, 2f32, T{a: 2});
|
||||||
q.add_tile(2, 0, 2f32, T{a: 3});
|
q.add_tile_pixel(2, 0, 2f32, T{a: 3});
|
||||||
assert!(q.root.tile_mem == 3);
|
assert!(q.root.tile_mem == 3);
|
||||||
assert!(q.get_all_tiles().len() == 3);
|
assert!(q.get_all_tiles().len() == 3);
|
||||||
q.add_tile(0, 2, 2f32, T{a: 4});
|
q.add_tile_pixel(0, 2, 2f32, T{a: 4});
|
||||||
q.add_tile(2, 2, 2f32, T{a: 5});
|
q.add_tile_pixel(2, 2, 2f32, T{a: 5});
|
||||||
assert!(q.root.tile_mem == 4);
|
assert!(q.root.tile_mem == 4);
|
||||||
|
|
||||||
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 2f32);
|
let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 2f32);
|
||||||
assert!(request.is_empty());
|
assert!(request.is_empty());
|
||||||
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 1.9);
|
let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 1.9);
|
||||||
assert!(request.is_empty());
|
assert!(request.is_empty());
|
||||||
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 1f32);
|
let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 1f32);
|
||||||
assert!(request.len() == 4);
|
assert!(request.len() == 4);
|
||||||
|
|
||||||
q.add_tile(0, 0, 0.5, T{a: 6});
|
q.add_tile_pixel(0, 0, 0.5, T{a: 6});
|
||||||
q.add_tile(0, 0, 1f32, T{a: 7});
|
q.add_tile_pixel(0, 0, 1f32, T{a: 7});
|
||||||
let (_, redisplay) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5);
|
let (_, redisplay) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5);
|
||||||
assert!(redisplay);
|
assert!(redisplay);
|
||||||
assert!(q.root.tile_mem == 1);
|
assert!(q.root.tile_mem == 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,15 +89,29 @@ impl WindowMethods<Application> for Window {
|
||||||
}
|
}
|
||||||
do window.glfw_window.set_mouse_button_callback |win, button, action, _mods| {
|
do window.glfw_window.set_mouse_button_callback |win, button, action, _mods| {
|
||||||
let (x, y) = win.get_cursor_pos();
|
let (x, y) = win.get_cursor_pos();
|
||||||
|
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
|
||||||
|
let (backing_size, _) = win.get_framebuffer_size();
|
||||||
|
let (window_size, _) = win.get_size();
|
||||||
|
let hidpi = (backing_size as f32) / (window_size as f32);
|
||||||
|
let x = x as f32 * hidpi;
|
||||||
|
let y = y as f32 * hidpi;
|
||||||
if button < 3 {
|
if button < 3 {
|
||||||
window.handle_mouse(button, action, x as i32, y as i32);
|
window.handle_mouse(button, action, x as i32, y as i32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
do window.glfw_window.set_scroll_callback |_win, x_offset, y_offset| {
|
do window.glfw_window.set_scroll_callback |win, x_offset, y_offset| {
|
||||||
let dx = (x_offset as f32) * 30.0;
|
let dx = (x_offset as f32) * 30.0;
|
||||||
let dy = (y_offset as f32) * 30.0;
|
let dy = (y_offset as f32) * 30.0;
|
||||||
|
|
||||||
event_queue.push(ScrollWindowEvent(Point2D(dx, dy)));
|
let (x, y) = win.get_cursor_pos();
|
||||||
|
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
|
||||||
|
let (backing_size, _) = win.get_framebuffer_size();
|
||||||
|
let (window_size, _) = win.get_size();
|
||||||
|
let hidpi = (backing_size as f32) / (window_size as f32);
|
||||||
|
let x = x as f32 * hidpi;
|
||||||
|
let y = y as f32 * hidpi;
|
||||||
|
|
||||||
|
event_queue.push(ScrollWindowEvent(Point2D(dx, dy), Point2D(x as i32, y as i32)));
|
||||||
}
|
}
|
||||||
|
|
||||||
window
|
window
|
||||||
|
@ -148,6 +162,12 @@ impl WindowMethods<Application> for Window {
|
||||||
self.render_state = render_state;
|
self.render_state = render_state;
|
||||||
self.update_window_title()
|
self.update_window_title()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hidpi_factor(@mut self) -> f32 {
|
||||||
|
let (backing_size, _) = self.glfw_window.get_framebuffer_size();
|
||||||
|
let (window_size, _) = self.glfw_window.get_size();
|
||||||
|
(backing_size as f32) / (window_size as f32)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
|
|
@ -32,8 +32,8 @@ pub enum WindowEvent {
|
||||||
LoadUrlWindowEvent(~str),
|
LoadUrlWindowEvent(~str),
|
||||||
/// Sent when a mouse hit test is to be performed.
|
/// Sent when a mouse hit test is to be performed.
|
||||||
MouseWindowEventClass(MouseWindowEvent),
|
MouseWindowEventClass(MouseWindowEvent),
|
||||||
/// Sent when the user scrolls.
|
/// Sent when the user scrolls. Includes the current cursor position.
|
||||||
ScrollWindowEvent(Point2D<f32>),
|
ScrollWindowEvent(Point2D<f32>, Point2D<i32>),
|
||||||
/// Sent when the user zooms.
|
/// Sent when the user zooms.
|
||||||
ZoomWindowEvent(f32),
|
ZoomWindowEvent(f32),
|
||||||
/// Sent when the user uses chrome navigation (i.e. backspace or shift-backspace).
|
/// Sent when the user uses chrome navigation (i.e. backspace or shift-backspace).
|
||||||
|
@ -64,5 +64,8 @@ pub trait WindowMethods<A> {
|
||||||
pub fn set_ready_state(@mut self, ready_state: ReadyState);
|
pub fn set_ready_state(@mut self, ready_state: ReadyState);
|
||||||
/// Sets the render state of the current page.
|
/// Sets the render state of the current page.
|
||||||
pub fn set_render_state(@mut self, render_state: RenderState);
|
pub fn set_render_state(@mut self, render_state: RenderState);
|
||||||
|
|
||||||
|
/// Returns the hidpi factor of the monitor.
|
||||||
|
pub fn hidpi_factor(@mut self) -> f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,10 +58,10 @@ pub enum ReadyState {
|
||||||
/// submit them to be drawn to the display.
|
/// submit them to be drawn to the display.
|
||||||
pub trait RenderListener {
|
pub trait RenderListener {
|
||||||
fn get_gl_context(&self) -> AzGLContext;
|
fn get_gl_context(&self) -> AzGLContext;
|
||||||
fn new_layer(&self, Size2D<uint>, uint);
|
fn new_layer(&self, PipelineId, Size2D<uint>);
|
||||||
fn resize_layer(&self, Size2D<uint>);
|
fn resize_layer(&self, PipelineId, Size2D<uint>);
|
||||||
fn delete_layer(&self);
|
fn delete_layer(&self, PipelineId);
|
||||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>);
|
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>);
|
||||||
fn set_render_state(&self, render_state: RenderState);
|
fn set_render_state(&self, render_state: RenderState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a23cb0bc99a933efa9104c2471ccd14638744ea4
|
Subproject commit 5b9a447946b736802a0791eb1c24687849ad3984
|
|
@ -1 +1 @@
|
||||||
Subproject commit a8843ea084262773c31916e3a52c6dacea135153
|
Subproject commit 177277d4aaeb56913f2eb04670679cae078cf70b
|
|
@ -1 +1 @@
|
||||||
Subproject commit b5cb5593f4938a578eebda56c905bbaff33efe66
|
Subproject commit e83cca0e287db58a27ee77947a8d4dc3617a6ed0
|
Loading…
Add table
Add a link
Reference in a new issue