mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Refactor compositor; implement compositor iframe support
This commit is contained in:
parent
0e737741b3
commit
974ed79144
7 changed files with 482 additions and 303 deletions
|
@ -32,7 +32,7 @@ pub struct RenderLayer {
|
|||
|
||||
pub enum Msg {
|
||||
RenderMsg(RenderLayer),
|
||||
ReRenderMsg(~[BufferRequest], f32),
|
||||
ReRenderMsg(~[BufferRequest], f32, PipelineId),
|
||||
PaintPermissionGranted,
|
||||
PaintPermissionRevoked,
|
||||
ExitMsg(Chan<()>),
|
||||
|
@ -135,18 +135,18 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
|||
match self.port.recv() {
|
||||
RenderMsg(render_layer) => {
|
||||
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);
|
||||
}
|
||||
ReRenderMsg(tiles, scale) => {
|
||||
self.render(tiles, scale);
|
||||
ReRenderMsg(tiles, scale, id) => {
|
||||
self.render(tiles, scale, id);
|
||||
}
|
||||
PaintPermissionGranted => {
|
||||
self.paint_permission = true;
|
||||
match self.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 => {}
|
||||
}
|
||||
|
@ -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;
|
||||
match self.render_layer {
|
||||
Some(ref r_layer) => {
|
||||
|
@ -202,7 +202,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
|||
font_ctx: self.font_ctx,
|
||||
opts: &self.opts
|
||||
};
|
||||
|
||||
|
||||
// Apply the translation to render the tile we want.
|
||||
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
||||
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");
|
||||
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");
|
||||
self.last_paint_msg = Some((layer_buffer_set, render_layer.size));
|
||||
|
|
|
@ -6,24 +6,55 @@ use geom::point::Point2D;
|
|||
use geom::size::Size2D;
|
||||
use geom::rect::Rect;
|
||||
use geom::matrix::identity;
|
||||
use gfx::render_task::BufferRequest;
|
||||
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::{ContainerLayer, TextureLayerKind, TextureLayer, TextureManager};
|
||||
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 elements.
|
||||
pub struct CompositorLayer {
|
||||
id: uint,
|
||||
/// This layer's pipeline. BufferRequests will be sent through this.
|
||||
pipeline: Pipeline,
|
||||
/// The rect that this layer occupies in page coordinates. The offset
|
||||
/// is blind to any parents this layer may have; it only refers to the
|
||||
/// scroll position of the page.
|
||||
page_rect: Rect<f32>,
|
||||
z_order: int,
|
||||
/// 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: Quadtree<~LayerBuffer>,
|
||||
/// 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,
|
||||
|
||||
// TODO: Eventually, it may be useful to have an ID associated with each layer.
|
||||
}
|
||||
|
||||
pub fn CompositorLayer(id: uint, page_size: Size2D<f32>, init_offset: Point2D<f32>, z_order: int, tile_size: uint, max_mem: Option<uint>) -> CompositorLayer {
|
||||
/// 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,
|
||||
}
|
||||
|
||||
pub fn CompositorLayer(pipeline: Pipeline, page_size: Size2D<f32>, tile_size: uint,
|
||||
max_mem: Option<uint>) -> CompositorLayer {
|
||||
CompositorLayer {
|
||||
id: id,
|
||||
page_rect: Rect(init_offset, page_size),
|
||||
z_order: z_order,
|
||||
pipeline: pipeline,
|
||||
page_rect: Rect(Point2D(0f32, 0f32), page_size),
|
||||
children: ~[],
|
||||
quadtree: Quadtree::new(page_size.width as uint, page_size.height as uint,
|
||||
tile_size,
|
||||
max_mem),
|
||||
|
@ -31,39 +62,172 @@ pub fn CompositorLayer(id: uint, page_size: Size2D<f32>, init_offset: Point2D<f3
|
|||
}
|
||||
}
|
||||
|
||||
impl CompositorLayer {
|
||||
// Given the current window size, determine which tiles need to be redisplayed.
|
||||
// Returns a BufferRequest array as well as a bool that is true if the layer tree should
|
||||
// be rebuilt for this layer.
|
||||
fn get_buffer_request(&mut self, window_size: Size2D<int>, scale: f32) -> (~[BufferRequest], bool) {
|
||||
let rect = Rect(Point2D(-(self.page_rect.origin.x as int),
|
||||
-(self.page_rect.origin.y as int)),
|
||||
window_size);
|
||||
self.quadtree.get_tile_rects(rect, scale)
|
||||
}
|
||||
|
||||
// Move the layer by as relative specified amount. Called during a scroll event.
|
||||
fn translate(&mut self, delta: Point2D<f32>) {
|
||||
impl CompositorLayer {
|
||||
// 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.page_rect.origin;
|
||||
for self.children.mut_iter().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.page_rect.origin;
|
||||
self.page_rect.origin = self.page_rect.origin + delta;
|
||||
}
|
||||
|
||||
// Move the layer to an absolute position.
|
||||
fn set_offset(&mut self, new_offset: Point2D<f32>) {
|
||||
self.page_rect.origin = new_offset;
|
||||
|
||||
// bounds checking
|
||||
let min_x = (window_size.width - self.page_rect.size.width).min(&0.0);
|
||||
self.page_rect.origin.x = self.page_rect.origin.x.clamp(&min_x, &0.0);
|
||||
let min_y = (window_size.height - self.page_rect.size.height).min(&0.0);
|
||||
self.page_rect.origin.y = self.page_rect.origin.y.clamp(&min_y, &0.0);
|
||||
|
||||
// check to see if we scrolled
|
||||
if old_origin - self.page_rect.origin == Point2D(0f32, 0f32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.root_layer.common.set_transform(identity().translate(self.page_rect.origin.x,
|
||||
self.page_rect.origin.y,
|
||||
0.0));
|
||||
true
|
||||
}
|
||||
|
||||
// Called when the layer changes size (NOT as a result of a zoom event).
|
||||
fn resize(&mut self, new_size: Size2D<f32>) {
|
||||
self.page_rect.size = new_size;
|
||||
// TODO: might get buffers back here
|
||||
self.quadtree.resize(new_size.width as uint, new_size.height as uint);
|
||||
// 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.page_rect.origin;
|
||||
// FIXME: maybe we want rev_iter() instead? Depends on draw order
|
||||
for self.children.iter().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));
|
||||
}
|
||||
|
||||
fn get_layer_tree(&mut self, scale: f32) -> @mut ContainerLayer {
|
||||
// Given the current window size, determine which tiles need to be redisplayed
|
||||
// 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.page_rect.origin.x + window_rect.origin.x,
|
||||
-self.page_rect.origin.y + window_rect.origin.y),
|
||||
window_rect.size);
|
||||
let (request, redisplay) = self.quadtree.get_tile_rects_page(rect, scale);
|
||||
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()
|
||||
.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.
|
||||
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_rect.size = new_size;
|
||||
// TODO: might get buffers back here
|
||||
self.quadtree.resize(new_size.width as uint, new_size.height as uint);
|
||||
// Call scroll for bounds checking of the page shrunk.
|
||||
self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ID does not match ours, so recurse on descendents.
|
||||
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
|
||||
// Delete old layer.
|
||||
while current_layer_child.is_some() {
|
||||
let trash = current_layer_child.get();
|
||||
do current_layer_child.get().with_common |common| {
|
||||
|
@ -72,10 +236,25 @@ impl CompositorLayer {
|
|||
self.root_layer.remove_child(trash);
|
||||
}
|
||||
|
||||
// Add child layers.
|
||||
for self.children.mut_iter().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 all_tiles = self.quadtree.get_all_tiles();
|
||||
for all_tiles.iter().advance |buffer| {
|
||||
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.
|
||||
|
@ -100,28 +279,61 @@ impl CompositorLayer {
|
|||
Some(_) => fail!(~"found unexpected layer kind"),
|
||||
};
|
||||
|
||||
let origin = buffer.rect.origin;
|
||||
let origin = Point2D(origin.x as f32, origin.y as f32);
|
||||
|
||||
|
||||
let rect = buffer.rect;
|
||||
// Set the layer's transform.
|
||||
let transform = identity().translate(origin.x * scale + self.page_rect.origin.x, origin.y * scale + self.page_rect.origin.y, 0.0);
|
||||
let transform = transform.scale(width as f32 * scale / buffer.resolution, height as f32 * scale / buffer.resolution, 1.0);
|
||||
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);
|
||||
}
|
||||
|
||||
self.root_layer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add LayerBuffers to this layer.
|
||||
// TODO: This may return old buffers, which should be sent back to the renderer.
|
||||
fn add_buffers(&mut self, new_buffers: &mut LayerBufferSet) {
|
||||
for new_buffers.buffers.iter().advance |buffer| {
|
||||
self.quadtree.add_tile(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
|
||||
buffer.resolution, ~buffer.clone());
|
||||
// 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 {
|
||||
for new_buffers.buffers.iter().advance |buffer| {
|
||||
// TODO: This may return old buffers, which should be sent back to the renderer.
|
||||
self.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.
|
||||
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers))
|
||||
}
|
||||
|
||||
// TODO: send buffers back to renderer when later is deleted
|
||||
// Deletes a specified sublayer. 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: 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(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/. */
|
||||
|
||||
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};
|
||||
|
||||
pub use windowing;
|
||||
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
|
||||
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
|
||||
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
|
||||
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::constellation_msg::PipelineId;
|
||||
use servo_msg::constellation_msg;
|
||||
use gfx::render_task::ReRenderMsg;
|
||||
use gfx::opts::Opts;
|
||||
|
||||
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
|
||||
|
@ -35,7 +35,6 @@ use geom::size::Size2D;
|
|||
use geom::rect::Rect;
|
||||
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
|
||||
use layers::layers::{ImageData, WithDataFn};
|
||||
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
|
||||
use layers::rendergl;
|
||||
use layers::scene::Scene;
|
||||
use opengles::gl2;
|
||||
|
@ -44,17 +43,17 @@ use servo_util::{time, url};
|
|||
use servo_util::time::profile;
|
||||
use servo_util::time::ProfilerChan;
|
||||
|
||||
use extra::arc;
|
||||
pub use windowing;
|
||||
|
||||
use extra::time::precise_time_s;
|
||||
use compositing::quadtree::Quadtree;
|
||||
use extra::arc;
|
||||
|
||||
use constellation::SendableFrameTree;
|
||||
use pipeline::Pipeline;
|
||||
use compositing::compositor_layer::CompositorLayer;
|
||||
|
||||
mod quadtree;
|
||||
mod compositor_layer;
|
||||
|
||||
|
||||
/// The implementation of the layers-based compositor.
|
||||
#[deriving(Clone)]
|
||||
pub struct CompositorChan {
|
||||
|
@ -81,18 +80,18 @@ impl RenderListener for CompositorChan {
|
|||
port.recv()
|
||||
}
|
||||
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>) {
|
||||
self.chan.send(Paint(id, layer_buffer_set, new_size))
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>) {
|
||||
self.chan.send(Paint(id, layer_buffer_set))
|
||||
}
|
||||
|
||||
fn new_layer(&self, page_size: Size2D<uint>, tile_size: uint) {
|
||||
self.chan.send(NewLayer(page_size, tile_size))
|
||||
fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
|
||||
self.chan.send(NewLayer(id, page_size))
|
||||
}
|
||||
fn resize_layer(&self, page_size: Size2D<uint>) {
|
||||
self.chan.send(ResizeLayer(page_size))
|
||||
fn resize_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
|
||||
self.chan.send(ResizeLayer(id, page_size))
|
||||
}
|
||||
fn delete_layer(&self) {
|
||||
self.chan.send(DeleteLayer)
|
||||
fn delete_layer(&self, id: PipelineId) {
|
||||
self.chan.send(DeleteLayer(id))
|
||||
}
|
||||
|
||||
fn set_render_state(&self, render_state: RenderState) {
|
||||
|
@ -128,16 +127,16 @@ pub enum Msg {
|
|||
/// Requests the compositors GL context.
|
||||
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.
|
||||
NewLayer(Size2D<uint>, uint),
|
||||
/// Alerts the compositor that the current layer has changed size.
|
||||
ResizeLayer(Size2D<uint>),
|
||||
/// Alerts the compositor that the current layer has been deleted.
|
||||
DeleteLayer,
|
||||
NewLayer(PipelineId, Size2D<uint>),
|
||||
/// Alerts the compositor that the specified layer has changed size.
|
||||
ResizeLayer(PipelineId, Size2D<uint>),
|
||||
/// Alerts the compositor that the specified layer has been deleted.
|
||||
DeleteLayer(PipelineId),
|
||||
|
||||
/// 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.
|
||||
ChangeReadyState(ReadyState),
|
||||
/// Alerts the compositor to the current status of rendering.
|
||||
|
@ -222,119 +221,30 @@ impl CompositorTask {
|
|||
let root_layer = @mut ContainerLayer();
|
||||
let window_size = window.size();
|
||||
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 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
|
||||
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_time = 0f;
|
||||
|
||||
// Extract tiles from the given quadtree and build and display the render tree.
|
||||
let build_layer_tree: &fn(&Quadtree<~LayerBuffer>) = |quad: &Quadtree<~LayerBuffer>| {
|
||||
// Iterate over the children of the container layer.
|
||||
let mut current_layer_child = 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;
|
||||
}
|
||||
root_layer.remove_child(trash);
|
||||
}
|
||||
// Channel to the outermost frame's pipeline.
|
||||
// FIXME: 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;
|
||||
|
||||
let all_tiles = quad.get_all_tiles();
|
||||
for all_tiles.iter().advance |buffer| {
|
||||
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;
|
||||
};
|
||||
// The root CompositorLayer
|
||||
let mut compositor_layer: Option<CompositorLayer> = None;
|
||||
|
||||
// Get BufferRequests from each layer.
|
||||
let ask_for_tiles = || {
|
||||
match quadtree {
|
||||
Some(ref mut quad) => {
|
||||
let (tile_request, redisplay) = quad.get_tile_rects(Rect(Point2D(world_offset.x as int,
|
||||
world_offset.y as int),
|
||||
window_size), world_zoom);
|
||||
|
||||
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");
|
||||
}
|
||||
let window_size_page = Size2D(window_size.width as f32 / world_zoom,
|
||||
window_size.height as f32 / world_zoom);
|
||||
for compositor_layer.mut_iter().advance |layer| {
|
||||
recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
|
||||
world_zoom) || recomposite;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -359,46 +269,66 @@ impl CompositorTask {
|
|||
|
||||
GetGLContext(chan) => chan.send(current_gl_context()),
|
||||
|
||||
NewLayer(new_size, tile_size) => {
|
||||
page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||
quadtree = Some(Quadtree::new(new_size.width.max(&(window_size.width as uint)),
|
||||
new_size.height.max(&(window_size.height as uint)),
|
||||
tile_size, Some(10000000u)));
|
||||
NewLayer(_id, new_size) => {
|
||||
// FIXME: This should create an additional layer instead of replacing the current one.
|
||||
// Once ResizeLayer messages are set up, we can switch to the new functionality.
|
||||
|
||||
let p = match pipeline {
|
||||
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(p.clone(), page_size,
|
||||
self.opts.tile_size, Some(10000000u));
|
||||
|
||||
let current_child = root_layer.first_child;
|
||||
// This assumes there is at most one child, which should be the case.
|
||||
match current_child {
|
||||
Some(old_layer) => root_layer.remove_child(old_layer),
|
||||
None => {}
|
||||
}
|
||||
root_layer.add_child(ContainerLayerKind(new_layer.root_layer));
|
||||
compositor_layer = Some(new_layer);
|
||||
|
||||
ask_for_tiles();
|
||||
|
||||
}
|
||||
ResizeLayer(new_size) => {
|
||||
page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||
// TODO: update quadtree, ask for tiles
|
||||
}
|
||||
DeleteLayer => {
|
||||
// TODO: create secondary layer tree, keep displaying until new tiles come in
|
||||
}
|
||||
|
||||
Paint(id, new_layer_buffer_set, new_size) => {
|
||||
match pipeline {
|
||||
Some(ref pipeline) => if id != pipeline.id { loop; },
|
||||
None => { loop; },
|
||||
ResizeLayer(id, new_size) => {
|
||||
match compositor_layer {
|
||||
Some(ref mut layer) => {
|
||||
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");
|
||||
|
||||
let quad;
|
||||
match quadtree {
|
||||
Some(ref mut q) => quad = q,
|
||||
None => fail!("Compositor: given paint command with no quadtree initialized"),
|
||||
match compositor_layer {
|
||||
Some(ref mut layer) => {
|
||||
assert!(layer.add_buffers(id, new_layer_buffer_set.get()));
|
||||
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
|
||||
// it wishes.
|
||||
}
|
||||
|
@ -433,56 +363,27 @@ impl CompositorTask {
|
|||
}
|
||||
|
||||
MouseWindowEventClass(mouse_window_event) => {
|
||||
let event: Event_;
|
||||
let world_mouse_point = |layer_mouse_point: Point2D<f32>| {
|
||||
layer_mouse_point + world_offset
|
||||
let point = match mouse_window_event {
|
||||
MouseWindowClickEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
||||
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 {
|
||||
MouseWindowClickEvent(button, layer_mouse_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"),
|
||||
for compositor_layer.iter().advance |layer| {
|
||||
layer.send_mouse_event(mouse_window_event, point);
|
||||
}
|
||||
}
|
||||
|
||||
ScrollWindowEvent(delta) => {
|
||||
// FIXME (Rust #2528): Can't use `-=`.
|
||||
let world_offset_copy = world_offset;
|
||||
world_offset = world_offset_copy - delta;
|
||||
|
||||
// Clamp the world offset to the screen size.
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
ScrollWindowEvent(delta, cursor) => {
|
||||
// TODO: modify delta to snap scroll to pixels.
|
||||
let page_delta = Point2D(delta.x as f32 / world_zoom, delta.y as f32 / world_zoom);
|
||||
let page_cursor: Point2D<f32> = Point2D(cursor.x as f32 / world_zoom,
|
||||
cursor.y as f32 / world_zoom);
|
||||
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| {
|
||||
recomposite = layer.scroll(page_delta, page_cursor, page_window) || recomposite;
|
||||
}
|
||||
ask_for_tiles();
|
||||
|
||||
recomposite = true;
|
||||
}
|
||||
|
||||
ZoomWindowEvent(magnification) => {
|
||||
|
@ -492,34 +393,19 @@ impl CompositorTask {
|
|||
|
||||
// Determine zoom amount
|
||||
world_zoom = (world_zoom * magnification).max(&1.0);
|
||||
local_zoom = local_zoom * world_zoom/old_world_zoom;
|
||||
|
||||
// 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);
|
||||
root_layer.common.set_transform(identity().scale(world_zoom, world_zoom, 1f32));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,13 +85,19 @@ impl<T: Tile> Quadtree<T> {
|
|||
self.max_tile_size
|
||||
}
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// 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(&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);
|
||||
match self.max_mem {
|
||||
Some(max) => {
|
||||
|
@ -106,32 +112,73 @@ impl<T: Tile> Quadtree<T> {
|
|||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a tile associtated with a given page position.
|
||||
/// 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(&mut self, x: uint, y: uint, scale: f32) -> BufferRequest {
|
||||
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
|
||||
pub fn get_all_tiles<'r>(&'r self) -> ~[&'r T] {
|
||||
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
|
||||
/// 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);
|
||||
match r {
|
||||
(Some(tile), _, _) => tile,
|
||||
_ => 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
|
||||
/// 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
|
||||
/// 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.
|
||||
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(
|
||||
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)),
|
||||
|
@ -139,6 +186,17 @@ impl<T: Tile> Quadtree<T> {
|
|||
scale, self.max_tile_size as f32 / scale);
|
||||
(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
|
||||
|
|
|
@ -89,15 +89,29 @@ impl WindowMethods<Application> for Window {
|
|||
}
|
||||
do window.glfw_window.set_mouse_button_callback |win, button, action, _mods| {
|
||||
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 {
|
||||
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 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
|
||||
|
@ -148,6 +162,12 @@ impl WindowMethods<Application> for Window {
|
|||
self.render_state = render_state;
|
||||
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 {
|
||||
|
|
|
@ -32,8 +32,8 @@ pub enum WindowEvent {
|
|||
LoadUrlWindowEvent(~str),
|
||||
/// Sent when a mouse hit test is to be performed.
|
||||
MouseWindowEventClass(MouseWindowEvent),
|
||||
/// Sent when the user scrolls.
|
||||
ScrollWindowEvent(Point2D<f32>),
|
||||
/// Sent when the user scrolls. Includes the current cursor position.
|
||||
ScrollWindowEvent(Point2D<f32>, Point2D<i32>),
|
||||
/// Sent when the user zooms.
|
||||
ZoomWindowEvent(f32),
|
||||
/// 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);
|
||||
/// Sets the render state of the current page.
|
||||
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.
|
||||
pub trait RenderListener {
|
||||
fn get_gl_context(&self) -> AzGLContext;
|
||||
fn new_layer(&self, Size2D<uint>, uint);
|
||||
fn resize_layer(&self, Size2D<uint>);
|
||||
fn delete_layer(&self);
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>);
|
||||
fn new_layer(&self, PipelineId, Size2D<uint>);
|
||||
fn resize_layer(&self, PipelineId, Size2D<uint>);
|
||||
fn delete_layer(&self, PipelineId);
|
||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>);
|
||||
fn set_render_state(&self, render_state: RenderState);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue