diff --git a/.gitmodules b/.gitmodules index ffce2ddf38e..9bb7b40de34 100644 --- a/.gitmodules +++ b/.gitmodules @@ -85,3 +85,6 @@ [submodule "src/skia"] path = src/skia url = git://github.com/mozilla-servo/skia.git +[submodule "src/servo-gfx"] + path = src/servo-gfx + url = git://github.com/mozilla-servo/servo-gfx.git diff --git a/configure b/configure index 3b7eec53e3c..d5c478f8788 100755 --- a/configure +++ b/configure @@ -344,7 +344,7 @@ step_msg "running submodule autoconf scripts" (cd ${CFG_SRC_DIR}src/mozjs/js/src && "${CFG_AUTOCONF213}") || exit $? -CFG_SUBMODULES="libwapcaplet rust-wapcaplet rust-harfbuzz rust-opengles skia rust-azure rust-cairo rust-stb-image rust-geom rust-glut rust-layers rust-http-client libparserutils libhubbub libcss rust-netsurfcss rust-css rust-hubbub sharegl rust-mozjs mozjs" +CFG_SUBMODULES="libwapcaplet rust-wapcaplet rust-harfbuzz rust-opengles skia rust-azure rust-cairo rust-stb-image rust-geom rust-glut rust-layers rust-http-client libparserutils libhubbub libcss rust-netsurfcss rust-css rust-hubbub sharegl rust-mozjs mozjs servo-gfx" if [ $CFG_OSTYPE = "darwin" ] then diff --git a/mk/sub.mk b/mk/sub.mk index ac1206fed61..a47519a7dfb 100644 --- a/mk/sub.mk +++ b/mk/sub.mk @@ -81,6 +81,16 @@ DEPS_libcss += \ libparserutils \ $(NULL) +DEPS_servo-gfx += \ + rust-azure \ + rust-cairo \ + rust-freetype \ + rust-geom \ + rust-harfbuzz \ + rust-http-client \ + rust-stb-image \ + $(NULL) + # Platform-specific dependencies ifeq ($(CFG_OSTYPE),darwin) DEPS_rust-azure += \ @@ -119,6 +129,12 @@ DEPS_rust-layers += \ rust-core-text \ $(NULL) +DEPS_servo-gfx += \ + rust-core-foundation \ + rust-core-graphics \ + rust-core-text \ + $(NULL) + endif ifeq ($(CFG_OSTYPE),linux) diff --git a/src/servo-gfx b/src/servo-gfx new file mode 160000 index 00000000000..08d11a2db06 --- /dev/null +++ b/src/servo-gfx @@ -0,0 +1 @@ +Subproject commit 08d11a2db06e733ce9c84524819d37486a26c160 diff --git a/src/servo/content/content_task.rs b/src/servo/content/content_task.rs index db2ee847ddd..6e6038a2b99 100644 --- a/src/servo/content/content_task.rs +++ b/src/servo/content/content_task.rs @@ -3,51 +3,36 @@ The content task is the main task that runs JavaScript and spawns layout tasks. */ -export Content, ContentTask; -export ControlMsg, ExecuteMsg, ParseMsg, ExitMsg, Timer; -export PingMsg, PongMsg; -export task_from_context; - -use core::util::replace; -use std::arc::{ARC, clone}; -use comm::{Port, Chan, listen, select2}; -use task::{spawn, spawn_listener}; -use io::{read_whole_file, println}; - +use dom::bindings::utils::rust_box; use dom::document::Document; use dom::node::{Node, NodeScope, define_bindings}; use dom::event::{Event, ResizeEvent, ReflowEvent}; use dom::window::Window; -use geom::size::Size2D; use layout::layout_task; -use layout_task::{LayoutTask, BuildMsg, BuildData, AddStylesheet}; -use resource::image_cache_task::ImageCacheTask; +use layout::layout_task::{AddStylesheet, BuildData, BuildMsg, LayoutTask}; -use newcss::stylesheet::Stylesheet; - -use jsrt = js::rust::rt; -use js::rust::{cx, methods}; -use js::global::{global_class, debug_fns}; - -use either::{Either, Left, Right}; - -use dom::bindings::utils::rust_box; -use js::rust::compartment; - -use resource::resource_task; -use resource_task::{ResourceTask}; - -use std::net::url::Url; -use url_to_str = std::net::url::to_str; -use util::url::make_url; -use task::{task, SingleThreaded}; -use std::cell::Cell; - -use js::glue::bindgen::RUST_JSVAL_TO_OBJECT; +use core::comm::{Port, Chan, listen, select2}; +use core::either; +use core::task::{SingleThreaded, spawn, spawn_listener, task}; +use core::io::{println, read_whole_file}; +use core::ptr::null; +use core::util::replace; +use geom::size::Size2D; +use gfx::resource::image_cache_task::ImageCacheTask; +use gfx::resource::resource_task::ResourceTask; +use gfx::util::url::make_url; use js::JSVAL_NULL; +use js::global::{global_class, debug_fns}; +use js::glue::bindgen::RUST_JSVAL_TO_OBJECT; use js::jsapi::{JSContext, JSVal}; use js::jsapi::bindgen::{JS_CallFunctionValue, JS_GetContextPrivate}; -use ptr::null; +use js::rust::{compartment, cx, methods}; +use jsrt = js::rust::rt; +use newcss::stylesheet::Stylesheet; +use std::arc::{ARC, clone}; +use std::cell::Cell; +use std::net::url::Url; +use url_to_str = std::net::url::to_str; pub enum ControlMsg { ParseMsg(Url), @@ -89,7 +74,7 @@ fn ContentTask(layout_task: LayoutTask, return move control_chan; } -struct Content { +pub struct Content { layout_task: LayoutTask, mut layout_join_port: Option>, @@ -159,7 +144,7 @@ fn Content(layout_task: LayoutTask, content } -fn task_from_context(cx: *JSContext) -> *Content unsafe { +pub fn task_from_context(cx: *JSContext) -> *Content unsafe { cast::reinterpret_cast(&JS_GetContextPrivate(cx)) } diff --git a/src/servo/engine.rs b/src/servo/engine.rs index 76de3a7217b..df9670ac00f 100644 --- a/src/servo/engine.rs +++ b/src/servo/engine.rs @@ -1,18 +1,18 @@ use content::content_task::{ContentTask, ExecuteMsg, ParseMsg, ExitMsg}; use content::content_task; use dom::event::Event; -use gfx::compositor::Compositor; -use gfx::render_task::RenderTask; -use gfx::render_task; use layout::layout_task; use layout_task::LayoutTask; -use opts::Opts; use resource::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use resource::resource_task::ResourceTask; use resource::resource_task; use core::pipes::{Port, Chan}; use core::task::spawn_listener; +use gfx::compositor::Compositor; +use gfx::opts::Opts; +use gfx::render_task::RenderTask; +use gfx::render_task; use std::cell::Cell; use std::net::url::Url; diff --git a/src/servo/gfx.rs b/src/servo/gfx.rs deleted file mode 100644 index 331d12ae507..00000000000 --- a/src/servo/gfx.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* This file exists just to make it easier to import things inside of - ./gfx/ without specifying the file they came out of imports. - -Note that you still must define each of the files as a module in -servo.rc. This is not ideal and may be changed in the future. */ - -// shortcut names -pub use au = geometry; -pub use dl = display_list; - -pub use display_list::DisplayItem; -pub use display_list::DisplayList; -pub use font::Font; -pub use font::FontDescriptor; -pub use font::FontGroup; -pub use font::FontSelector; -pub use font::FontStyle; -pub use font::RunMetrics; -pub use font_context::FontContext; -pub use font_list::FontFamily; -pub use font_list::FontList; -pub use geometry::Au; - -pub use render_context::RenderContext; -pub use render_layers::RenderLayer; \ No newline at end of file diff --git a/src/servo/gfx/JosefinSans-SemiBold.ttf b/src/servo/gfx/JosefinSans-SemiBold.ttf deleted file mode 100644 index f58d378543e..00000000000 Binary files a/src/servo/gfx/JosefinSans-SemiBold.ttf and /dev/null differ diff --git a/src/servo/gfx/OFL.txt b/src/servo/gfx/OFL.txt deleted file mode 100644 index d0a273c523b..00000000000 --- a/src/servo/gfx/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2009, 2010, 2011 Daniel Johnson (). - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/servo/gfx/color.rs b/src/servo/gfx/color.rs deleted file mode 100644 index 8763b9ee7a7..00000000000 --- a/src/servo/gfx/color.rs +++ /dev/null @@ -1,18 +0,0 @@ -use azure::AzFloat; -use AzColor = azure::azure_hl::Color; - -pub type Color = AzColor; - -pub fn rgb(r: u8, g: u8, b: u8) -> AzColor { - rgba(r, g, b, 1.0) -} - -pub fn rgba(r: u8, g: u8, b: u8, a: float) -> AzColor { - AzColor { - r: (r as AzFloat) / (255.0 as AzFloat), - g: (g as AzFloat) / (255.0 as AzFloat), - b: (b as AzFloat) / (255.0 as AzFloat), - a: a as AzFloat - } -} - diff --git a/src/servo/gfx/compositor.rs b/src/servo/gfx/compositor.rs deleted file mode 100644 index 21157d5136b..00000000000 --- a/src/servo/gfx/compositor.rs +++ /dev/null @@ -1,29 +0,0 @@ -use azure::azure_hl::{DrawTarget}; -use dom::event::Event; -use geom::rect::Rect; - -struct LayerBuffer { - draw_target: DrawTarget, - - // The rect in the containing RenderLayer that this represents. - rect: Rect, - - // NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH. - stride: uint -} - -/// A set of layer buffers. This is an atomic unit used to switch between the front and back -/// buffers. -struct LayerBufferSet { - buffers: ~[LayerBuffer] -} - -/** -The interface used to by the renderer to aquire draw targets for -each rendered frame and submit them to be drawn to the display -*/ -trait Compositor { - fn begin_drawing(next_dt: pipes::Chan); - fn draw(next_dt: pipes::Chan, +draw_me: LayerBufferSet); -} - diff --git a/src/servo/gfx/display_list.rs b/src/servo/gfx/display_list.rs deleted file mode 100644 index 372d3f173fc..00000000000 --- a/src/servo/gfx/display_list.rs +++ /dev/null @@ -1,121 +0,0 @@ -use azure::azure_hl::DrawTarget; - -use geom::Rect; -use geom::Point2D; - -use gfx::{au, Au}; -use color::{Color, rgb}; -use image::base::Image; -use render_context::RenderContext; -use text::SendableTextRun; -use util::range::Range; - -use std::arc::ARC; -use clone_arc = std::arc::clone; -use dvec::DVec; - -pub use layout::display_list_builder::DisplayListBuilder; - -struct DisplayItemData { - bounds : Rect, // TODO: whose coordinate system should this use? -} - -impl DisplayItemData { - static pure fn new(bounds: &Rect) -> DisplayItemData { - DisplayItemData { bounds: copy *bounds } - } -} - -pub enum DisplayItem { - SolidColor(DisplayItemData, Color), - // TODO: need to provide spacing data for text run. - // (i.e, to support rendering of CSS 'word-spacing' and 'letter-spacing') - // TODO: don't copy text runs, ever. - Text(DisplayItemData, ~SendableTextRun, Range, Color), - Image(DisplayItemData, ARC<~image::base::Image>), - Border(DisplayItemData, Au, Color) -} - -impl DisplayItem { - pure fn d(&self) -> &self/DisplayItemData { - match *self { - SolidColor(ref d, _) => d, - Text(ref d, _, _, _) => d, - Image(ref d, _) => d, - Border(ref d, _, _) => d - } - } - - fn draw_into_context(&self, ctx: &RenderContext) { - match *self { - SolidColor(_, color) => ctx.draw_solid_color(&self.d().bounds, color), - Text(_, run, range, color) => { - let new_run = @run.deserialize(ctx.font_ctx); - let font = new_run.font; - let origin = self.d().bounds.origin; - let baseline_origin = Point2D(origin.x, origin.y + font.metrics.ascent); - font.draw_text_into_context(ctx, new_run, range, baseline_origin, color); - }, - Image(_, ref img) => ctx.draw_image(self.d().bounds, clone_arc(img)), - Border(_, width, color) => ctx.draw_border(&self.d().bounds, width, color), - } - - debug!("%?", { - ctx.draw_border(&self.d().bounds, au::from_px(1), rgb(150, 150, 150)); - () }); - } - - static pure fn new_SolidColor(bounds: &Rect, color: Color) -> DisplayItem { - SolidColor(DisplayItemData::new(bounds), color) - } - - static pure fn new_Border(bounds: &Rect, width: Au, color: Color) -> DisplayItem { - Border(DisplayItemData::new(bounds), width, color) - } - - static pure fn new_Text(bounds: &Rect, - run: ~SendableTextRun, - range: Range, - color: Color) -> DisplayItem { - Text(DisplayItemData::new(bounds), move run, range, color) - } - - // ARC should be cloned into ImageData, but Images are not sendable - static pure fn new_Image(bounds: &Rect, image: ARC<~image::base::Image>) -> DisplayItem { - Image(DisplayItemData::new(bounds), move image) - } -} - -// Dual-mode/freezable. -pub struct DisplayList { - list: ~[~DisplayItem] -} - -trait DisplayListMethods { - fn append_item(&mut self, item: ~DisplayItem); - fn draw_into_context(ctx: &RenderContext); -} - -impl DisplayList { - static fn new() -> DisplayList { - DisplayList { list: ~[] } - } -} - -impl DisplayList : DisplayListMethods { - fn append_item(&mut self, item: ~DisplayItem) { - // FIXME(Issue #150): crashes - //debug!("Adding display item %u: %?", self.len(), item); - self.list.push(move item); - } - - fn draw_into_context(ctx: &RenderContext) { - debug!("beginning display list"); - for self.list.each |item| { - // FIXME(Issue #150): crashes - //debug!("drawing %?", *item); - item.draw_into_context(ctx); - } - debug!("ending display list"); - } -} diff --git a/src/servo/gfx/font.rs b/src/servo/gfx/font.rs deleted file mode 100644 index b31d9d3c8b9..00000000000 --- a/src/servo/gfx/font.rs +++ /dev/null @@ -1,500 +0,0 @@ -use color::Color; -use gfx::au; -use gfx::{Au, RenderContext}; -use geom::{Point2D, Rect, Size2D}; -use util::range::Range; -use text::glyph::{GlyphStore, GlyphIndex}; -use text::{ - Shaper, - TextRun, -}; - -use azure::{AzFloat, AzScaledFontRef}; -use azure::azure_hl::{BackendType, ColorPattern}; -use core::dvec::DVec; - -// FontHandle encapsulates access to the platform's font API, -// e.g. quartz, FreeType. It provides access to metrics and tables -// needed by the text shaper as well as access to the underlying font -// resources needed by the graphics layer to draw glyphs. - -#[cfg(target_os = "macos")] -pub type FontHandle/& = quartz::font::QuartzFontHandle; - -#[cfg(target_os = "linux")] -pub type FontHandle/& = freetype::font::FreeTypeFontHandle; - -pub trait FontHandleMethods { - pure fn face_name() -> ~str; - pure fn is_italic() -> bool; - pure fn boldness() -> CSSFontWeight; - - fn glyph_index(codepoint: char) -> Option; - fn glyph_h_advance(GlyphIndex) -> Option; - fn get_metrics() -> FontMetrics; -} - -// TODO: `new` should be part of trait FontHandleMethods - -// TODO(Issue #163): this is a workaround for static methods and -// typedefs not working well together. It should be removed. - -impl FontHandle { - #[cfg(target_os = "macos")] - static pub fn new(fctx: &native::FontContextHandle, buf: @~[u8], pt_size: float) -> Result { - quartz::font::QuartzFontHandle::new_from_buffer(fctx, buf, pt_size) - } - - #[cfg(target_os = "linux")] - static pub fn new(fctx: &native::FontContextHandle, buf: @~[u8], pt_size: float) -> Result { - freetype::font::FreeTypeFontHandle::new(fctx, buf, pt_size) - } -} - -// Used to abstract over the shaper's choice of fixed int representation. -type FractionalPixel = float; - -struct FontMetrics { - underline_size: Au, - underline_offset: Au, - leading: Au, - x_height: Au, - em_size: Au, - ascent: Au, - descent: Au, - max_advance: Au -} - -// TODO: use enum from CSS bindings -enum CSSFontWeight { - FontWeight100, - FontWeight200, - FontWeight300, - FontWeight400, - FontWeight500, - FontWeight600, - FontWeight700, - FontWeight800, - FontWeight900, -} -pub impl CSSFontWeight : cmp::Eq; - -pub impl CSSFontWeight { - pub pure fn is_bold() -> bool { - match self { - FontWeight900 | FontWeight800 | FontWeight700 | FontWeight600 => true, - _ => false - } - } -} - -// TODO: eventually this will be split into the specified and used -// font styles. specified contains uninterpreted CSS font property -// values, while 'used' is attached to gfx::Font to descript the -// instance's properties. -// -// For now, the cases are differentiated with a typedef -pub struct FontStyle { - pt_size: float, - weight: CSSFontWeight, - italic: bool, - oblique: bool, - families: ~str, - // TODO: font-stretch, text-decoration, font-variant, size-adjust -} - -// TODO(Issue #181): use deriving for trivial cmp::Eq implementations -pub impl FontStyle : cmp::Eq { - pure fn eq(other: &FontStyle) -> bool { - use std::cmp::FuzzyEq; - - self.pt_size.fuzzy_eq(&other.pt_size) && - self.weight == other.weight && - self.italic == other.italic && - self.oblique == other.oblique && - self.families == other.families - } - pure fn ne(other: &FontStyle) -> bool { !self.eq(other) } -} - -pub type SpecifiedFontStyle = FontStyle; -pub type UsedFontStyle = FontStyle; - -// TODO: move me to layout -struct ResolvedFont { - group: @FontGroup, - style: SpecifiedFontStyle, -} - -// FontDescriptor serializes a specific font and used font style -// options, such as point size. - -// It's used to swizzle/unswizzle gfx::Font instances when -// communicating across tasks, such as the display list between layout -// and render tasks. -pub struct FontDescriptor { - style: UsedFontStyle, - selector: FontSelector, -} - - -// TODO(Issue #181): use deriving for trivial cmp::Eq implementations -pub impl FontDescriptor : cmp::Eq { - pure fn eq(other: &FontDescriptor) -> bool { - self.style == other.style && - self.selector == other.selector - } - pure fn ne(other: &FontDescriptor) -> bool { !self.eq(other) } -} - -pub impl FontDescriptor { - static pure fn new(style: &UsedFontStyle, selector: &FontSelector) -> FontDescriptor { - FontDescriptor { - style: copy *style, - selector: copy *selector, - } - } -} - -// A FontSelector is a platform-specific strategy for serializing face names. -pub enum FontSelector { - SelectorPlatformName(~str), - SelectorStubDummy, // aka, use Josephin Sans -} - -// TODO(Issue #181): use deriving for trivial cmp::Eq implementations -pub impl FontSelector : cmp::Eq { - pure fn eq(other: &FontSelector) -> bool { - match (&self, other) { - (&SelectorStubDummy, &SelectorStubDummy) => true, - (&SelectorPlatformName(a), &SelectorPlatformName(b)) => a == b, - _ => false - } - } - pure fn ne(other: &FontSelector) -> bool { !self.eq(other) } -} - -// This struct is the result of mapping a specified FontStyle into the -// available fonts on the system. It contains an ordered list of font -// instances to be used in case the prior font cannot be used for -// rendering the specified language. - -// The ordering of font instances is mainly decided by the CSS -// 'font-family' property. The last font is a system fallback font. -pub struct FontGroup { - families: @str, - // style of the first western font in group, which is - // used for purposes of calculating text run metrics. - style: UsedFontStyle, - fonts: ~[@Font], -} - -pub impl FontGroup { - static fn new(families: @str, style: &UsedFontStyle, fonts: ~[@Font]) -> FontGroup { - FontGroup { - families: families, - style: copy *style, - fonts: move fonts, - } - } -} - -struct RunMetrics { - // may be negative due to negative width (i.e., kerning of '.' in 'P.T.') - advance_width: Au, - ascent: Au, // nonzero - descent: Au, // nonzero - // this bounding box is relative to the left origin baseline. - // so, bounding_box.position.y = -ascent - bounding_box: Rect -} - -/** -A font instance. Layout can use this to calculate glyph metrics -and the renderer can use it to render text. -*/ -struct Font { - priv fontbuf: @~[u8], - priv handle: FontHandle, - priv mut azure_font: Option, - priv mut shaper: Option<@Shaper>, - style: UsedFontStyle, - metrics: FontMetrics, - backend: BackendType, - - drop { - use azure::bindgen::AzReleaseScaledFont; - do (copy self.azure_font).iter |fontref| { AzReleaseScaledFont(*fontref); } - } -} - -impl Font { - // TODO: who should own fontbuf? - static fn new(fontbuf: @~[u8], - handle: FontHandle, - style: UsedFontStyle, - backend: BackendType) -> Font { - let metrics = handle.get_metrics(); - - Font { - fontbuf : fontbuf, - handle : move handle, - azure_font: None, - shaper: None, - style: move style, - metrics: move metrics, - backend: backend - } - } - - priv fn get_shaper(@self) -> @Shaper { - // fast path: already created a shaper - match self.shaper { - Some(shaper) => { return shaper; }, - None => {} - } - - let shaper = @Shaper::new(self); - self.shaper = Some(shaper); - shaper - } - - priv fn get_azure_font() -> AzScaledFontRef { - // fast path: we've already created the azure font resource - match self.azure_font { - Some(azfont) => return azfont, - None => {} - } - - let ct_font = &self.handle.ctfont; - let size = self.style.pt_size as AzFloat; - let scaled_font = azure::scaled_font::ScaledFont::new(self.backend, ct_font, size); - - let azure_scaled_font; - unsafe { - azure_scaled_font = scaled_font.azure_scaled_font; - cast::forget(move scaled_font); - } - - self.azure_font = Some(azure_scaled_font); - azure_scaled_font - /* - // TODO: these cairo-related things should be in rust-cairo. - // creating a cairo font/face from a native font resource - // should be part of the NativeFont API, not exposed here. - #[cfg(target_os = "linux")] - fn get_cairo_face(font: &Font) -> *cairo_font_face_t { - use cairo::cairo_ft::bindgen::{cairo_ft_font_face_create_for_ft_face}; - - let ftface = font.handle.face; - let cface = cairo_ft_font_face_create_for_ft_face(ftface, 0 as c_int); - // FIXME: error handling - return cface; - } - */ - } -} - -// Public API -pub trait FontMethods { - fn draw_text_into_context(rctx: &RenderContext, - run: &TextRun, - range: Range, - baseline_origin: Point2D, - color: Color); - fn measure_text(&TextRun, Range) -> RunMetrics; - fn shape_text(@self, &str) -> GlyphStore; - fn get_descriptor() -> FontDescriptor; - - fn buf(&self) -> @~[u8]; - // these are used to get glyphs and advances in the case that the - // shaper can't figure it out. - fn glyph_index(char) -> Option; - fn glyph_h_advance(GlyphIndex) -> FractionalPixel; -} - -pub impl Font : FontMethods { - fn draw_text_into_context(rctx: &RenderContext, - run: &TextRun, - range: Range, - baseline_origin: Point2D, - color: Color) { - use libc::types::common::c99::{uint16_t, uint32_t}; - use azure::{AzDrawOptions, - AzGlyph, - AzGlyphBuffer}; - use azure::bindgen::{AzCreateColorPattern, - AzDrawTargetFillGlyphs, - AzReleaseColorPattern}; - - let target = rctx.get_draw_target(); - let azfont = self.get_azure_font(); - let pattern = ColorPattern(color); - let azure_pattern = pattern.azure_color_pattern; - assert azure_pattern.is_not_null(); - - let options: AzDrawOptions = { - mAlpha: 1f as AzFloat, - fields: 0x0200 as uint16_t - }; - - let mut origin = copy baseline_origin; - let azglyphs = DVec(); - azglyphs.reserve(range.length()); - - do run.glyphs.iter_glyphs_for_range(range) |_i, glyph| { - let glyph_advance = glyph.advance(); - let glyph_offset = glyph.offset().get_default(au::zero_point()); - - let azglyph: AzGlyph = { - mIndex: glyph.index() as uint32_t, - mPosition: { - x: au::to_px(origin.x + glyph_offset.x) as AzFloat, - y: au::to_px(origin.y + glyph_offset.y) as AzFloat - } - }; - origin = Point2D(origin.x + glyph_advance, origin.y); - azglyphs.push(move azglyph) - }; - - let azglyph_buf_len = azglyphs.len(); - let azglyph_buf = dvec::unwrap(move azglyphs); - let glyphbuf: AzGlyphBuffer = unsafe {{ - mGlyphs: vec::raw::to_ptr(azglyph_buf), - mNumGlyphs: azglyph_buf_len as uint32_t - }}; - - // TODO: this call needs to move into azure_hl.rs - AzDrawTargetFillGlyphs(target.azure_draw_target, - azfont, - ptr::to_unsafe_ptr(&glyphbuf), - azure_pattern, - ptr::to_unsafe_ptr(&options), - ptr::null()); - } - - fn measure_text(run: &TextRun, range: Range) -> RunMetrics { - assert range.is_valid_for_string(run.text); - - // TODO: alter advance direction for RTL - // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text - let mut advance = Au(0); - for run.glyphs.iter_glyphs_for_range(range) |_i, glyph| { - advance += glyph.advance(); - } - let mut bounds = Rect(Point2D(Au(0), -self.metrics.ascent), - Size2D(advance, self.metrics.ascent + self.metrics.descent)); - - // TODO(Issue #125): support loose and tight bounding boxes; using the - // ascent+descent and advance is sometimes too generous and - // looking at actual glyph extents can yield a tighter box. - - let metrics = RunMetrics { advance_width: advance, - bounding_box: bounds, - ascent: self.metrics.ascent, - descent: self.metrics.descent, - }; - debug!("Measured text range '%s' with metrics:", run.text.substr(range.begin(), range.length())); - debug!("%?", metrics); - - return metrics; - } - - fn shape_text(@self, text: &str) -> GlyphStore { - let store = GlyphStore(text.len()); - let shaper = self.get_shaper(); - shaper.shape_text(text, &store); - return move store; - } - - fn get_descriptor() -> FontDescriptor { - // TODO(Issue #174): implement by-platform-name FontSelectors, - // probably by adding such an API to FontHandle. - FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy) - } - - fn buf(&self) -> @~[u8] { - self.fontbuf - } - - fn glyph_index(codepoint: char) -> Option { - self.handle.glyph_index(codepoint) - } - - fn glyph_h_advance(glyph: GlyphIndex) -> FractionalPixel { - match self.handle.glyph_h_advance(glyph) { - Some(adv) => adv, - None => /* FIXME: Need fallback strategy */ 10f as FractionalPixel - } - } -} - -/*fn should_destruct_on_fail_without_leaking() { - #[test]; - #[should_fail]; - - let fctx = @FontContext(); - let matcher = @FontMatcher(fctx); - let _font = matcher.get_test_font(); - fail; -} - -fn should_get_glyph_indexes() { - #[test]; - - let fctx = @FontContext(); - let matcher = @FontMatcher(fctx); - let font = matcher.get_test_font(); - let glyph_idx = font.glyph_index('w'); - assert glyph_idx == Some(40u as GlyphIndex); -} - -fn should_get_glyph_advance() { - #[test]; - #[ignore]; - - let fctx = @FontContext(); - let matcher = @FontMatcher(fctx); - let font = matcher.get_test_font(); - let x = font.glyph_h_advance(40u as GlyphIndex); - assert x == 15f || x == 16f; -} - -// Testing thread safety -fn should_get_glyph_advance_stress() { - #[test]; - #[ignore]; - - let mut ports = ~[]; - - for iter::repeat(100) { - let (chan, port) = pipes::stream(); - ports += [@move port]; - do task::spawn |move chan| { - let fctx = @FontContext(); - let matcher = @FontMatcher(fctx); - let _font = matcher.get_test_font(); - let x = font.glyph_h_advance(40u as GlyphIndex); - assert x == 15f || x == 16f; - chan.send(()); - } - } - - for ports.each |port| { - port.recv(); - } -} - -fn should_be_able_to_create_instances_in_multiple_threads() { - #[test]; - - for iter::repeat(10u) { - do task::spawn { - let fctx = @FontContext(); - let matcher = @FontMatcher(fctx); - let _font = matcher.get_test_font(); - } - } -} - -*/ diff --git a/src/servo/gfx/font_context.rs b/src/servo/gfx/font_context.rs deleted file mode 100644 index 23c2096165e..00000000000 --- a/src/servo/gfx/font_context.rs +++ /dev/null @@ -1,123 +0,0 @@ -use gfx::{FontDescriptor, FontList, FontSelector, FontStyle}; -use gfx::font::{SelectorPlatformName, SelectorStubDummy, SpecifiedFontStyle}; -use gfx::native::FontHandle; -use util::cache; - -use azure::azure_hl::BackendType; -use core::dvec::DVec; - -// TODO(Issue #164): delete, and get default font from font list -const TEST_FONT: [u8 * 33004] = #include_bin("JosefinSans-SemiBold.ttf"); - -fn test_font_bin() -> ~[u8] { - return vec::from_fn(33004, |i| TEST_FONT[i]); -} - -// TODO(Rust #3934): creating lots of new dummy styles is a workaround -// for not being able to store symbolic enums in top-level constants. -pub fn dummy_style() -> FontStyle { - use gfx::font::FontWeight300; - return FontStyle { - pt_size: 20f, - weight: FontWeight300, - italic: false, - oblique: false, - families: ~"Helvetica, serif", - } -} - -// TODO(Issue #163): this is a workaround for static methods and -// typedefs not working well together. It should be removed. -#[cfg(target_os = "macos")] -type FontContextHandle/& = quartz::font_context::QuartzFontContextHandle; - -#[cfg(target_os = "linux")] -type FontContextHandle/& = freetype::font_context::FreeTypeFontContextHandle; - -pub impl FontContextHandle { - #[cfg(target_os = "macos")] - static pub fn new() -> FontContextHandle { - quartz::font_context::QuartzFontContextHandle::new() - } - - #[cfg(target_os = "linux")] - static pub fn new() -> FontContextHandle { - freetype::font_context::FreeTypeFontContextHandle::new() - } -} - -pub struct FontContext { - instance_cache: cache::MonoCache, - font_list: Option, // only needed by layout - handle: FontContextHandle, - backend: BackendType, -} - -pub impl FontContext { - static fn new(backend: BackendType, needs_font_list: bool) -> FontContext { - let handle = FontContextHandle::new(); - let font_list = if needs_font_list { Some(FontList::new(&handle)) } else { None }; - FontContext { - // TODO(Rust #3902): remove extraneous type parameters once they are inferred correctly. - instance_cache: cache::new::>(10), - font_list: move font_list, - handle: move handle, - backend: backend - } - } - - fn get_resolved_font_for_style(style: &SpecifiedFontStyle) -> @FontGroup { - // TODO(Issue #178, E): implement a cache of FontGroup instances. - self.create_font_group(style) - } - - fn get_font_by_descriptor(desc: &FontDescriptor) -> Result<@Font, ()> { - match self.instance_cache.find(desc) { - Some(f) => Ok(f), - None => { - let result = self.create_font_instance(desc); - match result { - Ok(font) => { - self.instance_cache.insert(desc, font); - }, _ => {} - }; - result - } - } - } - - priv fn create_font_group(style: &SpecifiedFontStyle) -> @FontGroup { - // TODO(Issue #178, D): implement private font matching - // TODO(Issue #174): implement by-platform-name FontSelectors - let desc = FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy); - let fonts = DVec(); - match self.get_font_by_descriptor(&desc) { - Ok(instance) => fonts.push(instance), - Err(()) => {} - } - - assert fonts.len() > 0; - // TODO(Issue #179): Split FontStyle into specified and used styles - let used_style = copy *style; - - @FontGroup::new(style.families.to_managed(), &used_style, dvec::unwrap(move fonts)) - } - - priv fn create_font_instance(desc: &FontDescriptor) -> Result<@Font, ()> { - match desc.selector { - SelectorStubDummy => { - let font_bin = @test_font_bin(); - let handle = FontHandle::new(&self.handle, font_bin, desc.style.pt_size); - let handle = if handle.is_ok() { - result::unwrap(move handle) - } else { - return Err(handle.get_err()); - }; - - return Ok(@Font::new(font_bin, move handle, copy desc.style, self.backend)); - }, - // TODO(Issue #174): implement by-platform-name font selectors. - SelectorPlatformName(_) => { fail ~"FontContext::create_font_instance() can't yet handle SelectorPlatformName." } - } - } -} diff --git a/src/servo/gfx/font_list.rs b/src/servo/gfx/font_list.rs deleted file mode 100644 index 6d100efe1fa..00000000000 --- a/src/servo/gfx/font_list.rs +++ /dev/null @@ -1,140 +0,0 @@ -use gfx::font::{ - CSSFontWeight, - SpecifiedFontStyle, -}; -use gfx::native::FontHandle; - -use dvec::DVec; -use send_map::{linear, SendMap}; - -#[cfg(target_os = "macos")] -type FontListHandle/& = quartz::font_list::QuartzFontListHandle; - -#[cfg(target_os = "linux")] -type FontListHandle/& = freetype::font_list::FreeTypeFontListHandle; - -pub impl FontListHandle { - #[cfg(target_os = "macos")] - static pub fn new(fctx: &native::FontContextHandle) -> Result { - Ok(quartz::font_list::QuartzFontListHandle::new(fctx)) - } - - #[cfg(target_os = "linux")] - static pub fn new(fctx: &native::FontContextHandle) -> Result { - Ok(freetype::font_list::FreeTypeFontListHandle::new(fctx)) - } -} - -type FontFamilyMap = linear::LinearMap<~str, @FontFamily>; - -pub struct FontList { - mut family_map: FontFamilyMap, - mut handle: FontListHandle, -} - -pub impl FontList { - static fn new(fctx: &native::FontContextHandle) -> FontList { - let handle = result::unwrap(FontListHandle::new(fctx)); - let list = FontList { - handle: move handle, - family_map: linear::LinearMap(), - }; - list.refresh(fctx); - return move list; - } - - priv fn refresh(fctx: &native::FontContextHandle) { - // TODO(Issue #186): don't refresh unless something actually - // changed. Does OSX have a notification for this event? - do util::time::time("gfx::font_list: regenerating available font families and faces") { - self.family_map = self.handle.get_available_families(fctx); - } - } - - fn find_font_in_family(family_name: &str, - style: &SpecifiedFontStyle) -> Option<@FontEntry> { - let family = self.find_family(family_name); - let mut result : Option<@FontEntry> = None; - - // if such family exists, try to match style to a font - do family.iter |fam| { - result = fam.find_font_for_style(style); - } - return result; - } - - priv fn find_family(family_name: &str) -> Option<@FontFamily> { - // look up canonical name - let family = self.family_map.find(&str::from_slice(family_name)); - - // TODO(Issue #188): look up localized font family names if canonical name not found - return family; - } -} - -// Holds a specific font family, and the various -pub struct FontFamily { - family_name: @str, - entries: DVec<@FontEntry>, -} - -pub impl FontFamily { - static fn new(family_name: &str) -> FontFamily { - FontFamily { - family_name: str::from_slice(family_name).to_managed(), - entries: DVec(), - } - } - - pure fn find_font_for_style(style: &SpecifiedFontStyle) -> Option<@FontEntry> { - assert self.entries.len() > 0; - - // TODO(Issue #189): optimize lookup for - // regular/bold/italic/bolditalic with fixed offsets and a - // static decision table for fallback between these values. - - // TODO(Issue #190): if not in the fast path above, do - // expensive matching of weights, etc. - for self.entries.each |entry| { - if (style.weight.is_bold() == entry.is_bold()) && - (style.italic == entry.is_italic()) { - - return Some(*entry); - } - } - - return None; - } -} - -// This struct summarizes an available font's features. In the future, -// this will include fiddly settings such as special font table handling. - -// In the common case, each FontFamily will have a singleton FontEntry, or -// it will have the standard four faces: Normal, Bold, Italic, BoldItalic. -struct FontEntry { - family: @FontFamily, - face_name: ~str, - priv weight: CSSFontWeight, - priv italic: bool, - handle: FontHandle, - // TODO: array of OpenType features, etc. -} - -impl FontEntry { - static fn new(family: @FontFamily, handle: FontHandle) -> FontEntry { - FontEntry { - family: family, - face_name: handle.face_name(), - weight: handle.boldness(), - italic: handle.is_italic(), - handle: move handle - } - } - - pure fn is_bold() -> bool { - self.weight.is_bold() - } - - pure fn is_italic() -> bool { self.italic } -} diff --git a/src/servo/gfx/fontconfig/font_list.rs b/src/servo/gfx/fontconfig/font_list.rs deleted file mode 100644 index 109e8b65620..00000000000 --- a/src/servo/gfx/fontconfig/font_list.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub struct FontconfigFontContextHandle { - ctx: (), - - drop { } -} - -pub impl FontconfigFontContextHandle { - // this is a placeholder. - static pub fn new() -> FontconfigFontContextHandle { - FontconfigFontContextHandle { ctx: () } - } -} \ No newline at end of file diff --git a/src/servo/gfx/freetype/font.rs b/src/servo/gfx/freetype/font.rs deleted file mode 100644 index 2218eb61e1e..00000000000 --- a/src/servo/gfx/freetype/font.rs +++ /dev/null @@ -1,152 +0,0 @@ -extern mod freetype; - -use font::{FontMetrics, FractionalPixel}; -use font_context::FreeTypeFontContext; - -use gfx::geometry; -use gfx::geometry::Au; -use vec_as_buf = vec::as_imm_buf; -use ptr::{addr_of, null}; -use cast::reinterpret_cast; -use glyph::GlyphIndex; -use servo_util::text::{float_to_fixed, fixed_to_float}; - -use freetype::{ FT_Error, FT_Library, FT_Face, FT_Long, FT_ULong, FT_Size, FT_SizeRec, - FT_UInt, FT_GlyphSlot, FT_Size_Metrics, FT_FaceRec, FT_F26Dot6 }; -use freetype::bindgen::{ - FT_Init_FreeType, - FT_Done_FreeType, - FT_New_Memory_Face, - FT_Done_Face, - FT_Get_Char_Index, - FT_Load_Glyph, - FT_Set_Char_Size -}; - -fn float_to_fixed_ft(f: float) -> i32 { - float_to_fixed(6, f) -} - -fn fixed_to_float_ft(f: i32) -> float { - fixed_to_float(6, f) -} - -pub struct FreeTypeFontHandle { - /// The font binary. This must stay valid for the lifetime of the font - buf: @~[u8], - face: FT_Face, - - drop { - assert self.face.is_not_null(); - if !FT_Done_Face(self.face).succeeded() { - fail ~"FT_Done_Face failed"; - } - } -} - -pub impl FreeTypeFontHandle { - static pub fn new(fctx: &FreeTypeFontContext, - buf: @~[u8], pt_size: float) -> Result { - let ft_ctx = fctx.ctx; - assert ft_ctx.is_not_null(); - let face: FT_Face = null(); - return vec_as_buf(*buf, |cbuf, _len| { - if FT_New_Memory_Face(ft_ctx, cbuf, (*buf).len() as FT_Long, - 0 as FT_Long, addr_of(&face)).succeeded() { - let res = FT_Set_Char_Size(face, // the face - float_to_fixed_ft(pt_size) as FT_F26Dot6, // char width - float_to_fixed_ft(pt_size) as FT_F26Dot6, // char height - 72, // horiz. DPI - 72); // vert. DPI - if !res.succeeded() { fail ~"unable to set font char size" } - Ok(FreeTypeFontHandle { face: face, buf: buf }) - } else { - Err(()) - } - }) - } - - pub fn glyph_index(codepoint: char) -> Option { - assert self.face.is_not_null(); - let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong); - return if idx != 0 as FT_UInt { - Some(idx as GlyphIndex) - } else { - debug!("Invalid codepoint: %?", codepoint); - None - }; - } - - pub fn glyph_h_advance(glyph: GlyphIndex) -> Option { - assert self.face.is_not_null(); - let res = FT_Load_Glyph(self.face, glyph as FT_UInt, 0); - if res.succeeded() { - unsafe { - let void_glyph = (*self.face).glyph; - let slot: FT_GlyphSlot = reinterpret_cast(&void_glyph); - assert slot.is_not_null(); - debug!("metrics: %?", (*slot).metrics); - let advance = (*slot).metrics.horiAdvance; - debug!("h_advance for %? is %?", glyph, advance); - let advance = advance as i32; - return Some(fixed_to_float_ft(advance) as FractionalPixel); - } - } else { - debug!("Unable to load glyph %?. reason: %?", glyph, res); - return None; - } - } - - pub fn get_metrics() -> FontMetrics { - /* TODO(Issue #76): complete me */ - let face = self.get_face_rec(); - - let underline_size = self.font_units_to_au(face.underline_thickness as float); - let underline_offset = self.font_units_to_au(face.underline_position as float); - let em_size = self.font_units_to_au(face.units_per_EM as float); - let ascent = self.font_units_to_au(face.ascender as float); - let descent = self.font_units_to_au(face.descender as float); - let max_advance = self.font_units_to_au(face.max_advance_width as float); - - return FontMetrics { - underline_size: underline_size, - underline_offset: underline_offset, - leading: geometry::from_pt(0.0), //FIXME - x_height: geometry::from_pt(0.0), //FIXME - em_size: em_size, - ascent: ascent, - descent: descent, - max_advance: max_advance - } - } - - priv fn get_face_rec() -> &self/FT_FaceRec unsafe { - &(*self.face) - } - - priv fn font_units_to_au(value: float) -> Au { - - let face = self.get_face_rec(); - - // face.size is a *c_void in the bindings, presumably to avoid - // recursive structural types - let size: &FT_SizeRec = unsafe { cast::transmute(&(*face.size)) }; - let metrics: &FT_Size_Metrics = unsafe { &((*size).metrics) }; - - let em_size = face.units_per_EM as float; - let x_scale = (metrics.x_ppem as float) / em_size as float; - - // If this isn't true then we're scaling one of the axes wrong - assert metrics.x_ppem == metrics.y_ppem; - - return geometry::from_frac_px(value * x_scale); - } -} - -trait FTErrorMethods { - fn succeeded() -> bool; -} - -impl FT_Error : FTErrorMethods { - fn succeeded() -> bool { self == 0 as FT_Error } -} diff --git a/src/servo/gfx/freetype/font_context.rs b/src/servo/gfx/freetype/font_context.rs deleted file mode 100644 index 18a168fd77d..00000000000 --- a/src/servo/gfx/freetype/font_context.rs +++ /dev/null @@ -1,33 +0,0 @@ -extern mod freetype; - -use freetype::{ - FT_Error, - FT_Library, -}; -use freetype::bindgen::{ - FT_Init_FreeType, - FT_Done_FreeType -}; - - -pub struct FreeTypeFontContextHandle { - ctx: FT_Library, - - drop { - assert self.ctx.is_not_null(); - FT_Done_FreeType(self.ctx); - } -} - -pub impl FreeTypeFontContextHandle { - static pub fn new() -> FreeTypeFontContextHandle { - let lib: FT_Library = ptr::null(); - let res = FT_Init_FreeType(ptr::addr_of(&lib)); - // FIXME: error handling - assert res == 0 as FT_Error; - - FreeTypeFontContext { - ctx: lib, - } - } -} \ No newline at end of file diff --git a/src/servo/gfx/geometry.rs b/src/servo/gfx/geometry.rs deleted file mode 100644 index 904a4b9c4fb..00000000000 --- a/src/servo/gfx/geometry.rs +++ /dev/null @@ -1,80 +0,0 @@ -use geom::point::Point2D; -use geom::rect::Rect; -use geom::size::Size2D; -use num::{Num, from_int}; - -pub enum Au = i32; - -impl Au : Num { - pure fn add(other: &Au) -> Au { Au(*self + **other) } - pure fn sub(other: &Au) -> Au { Au(*self - **other) } - pure fn mul(other: &Au) -> Au { Au(*self * **other) } - pure fn div(other: &Au) -> Au { Au(*self / **other) } - pure fn modulo(other: &Au) -> Au { Au(*self % **other) } - pure fn neg() -> Au { Au(-*self) } - - pure fn to_int() -> int { *self as int } - - static pure fn from_int(n: int) -> Au { - Au((n & (i32::max_value as int)) as i32) - } -} - -impl Au : cmp::Ord { - pure fn lt(other: &Au) -> bool { *self < **other } - pure fn le(other: &Au) -> bool { *self <= **other } - pure fn ge(other: &Au) -> bool { *self >= **other } - pure fn gt(other: &Au) -> bool { *self > **other } -} - -impl Au : cmp::Eq { - pure fn eq(other: &Au) -> bool { *self == **other } - pure fn ne(other: &Au) -> bool { *self != **other } -} - -pub pure fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } } -pub pure fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } } - -pub fn box(x: A, y: A, w: A, h: A) -> Rect { - Rect(Point2D(x, y), Size2D(w, h)) -} - -impl Au { - pub fn scale_by(factor: float) -> Au { - Au(((*self as float) * factor) as i32) - } -} - -pub pure fn zero_rect() -> Rect { - let z = Au(0); - Rect(Point2D(z, z), Size2D(z, z)) -} - -pub pure fn zero_point() -> Point2D { - Point2D(Au(0), Au(0)) -} - -pub pure fn zero_size() -> Size2D { - Size2D(Au(0), Au(0)) -} - -pub pure fn from_frac_px(f: float) -> Au { - Au((f * 60f) as i32) -} - -pub pure fn from_px(i: int) -> Au { - from_int(i * 60) -} - -pub pure fn to_px(au: Au) -> int { - (*au / 60) as int -} - -pub pure fn to_frac_px(au: Au) -> float { - (*au as float) / 60f -} - -// assumes 72 points per inch, and 96 px per inch -pub pure fn from_pt(f: float) -> Au { - from_px((f / 72f * 96f) as int) -} diff --git a/src/servo/gfx/native.rs b/src/servo/gfx/native.rs deleted file mode 100644 index abcf8052944..00000000000 --- a/src/servo/gfx/native.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* This file exists just to make it easier to import platform-specific - implementations. - -Note that you still must define each of the files as a module in -servo.rc. This is not ideal and may be changed in the future. */ - -pub use gfx::font::FontHandle; -pub use gfx::font_context::FontContextHandle; -pub use gfx::font_list::FontListHandle; \ No newline at end of file diff --git a/src/servo/gfx/quartz/font.rs b/src/servo/gfx/quartz/font.rs deleted file mode 100644 index 3d1c2f43fae..00000000000 --- a/src/servo/gfx/quartz/font.rs +++ /dev/null @@ -1,190 +0,0 @@ -extern mod core_foundation; -extern mod core_graphics; -extern mod core_text; - -use font_context::QuartzFontContextHandle; -use gfx::au; -use gfx::font::{ - CSSFontWeight, - FontHandleMethods, - FontMetrics, - FontWeight100, - FontWeight200, - FontWeight300, - FontWeight400, - FontWeight500, - FontWeight600, - FontWeight700, - FontWeight800, - FontWeight900, - FractionalPixel -}; -use text::glyph::GlyphIndex; - -use libc::size_t; - -use cf = core_foundation; -use cf::base::{ - CFIndex, - CFRelease, - CFTypeRef -}; -use cf::string::UniChar; - -use cg = core_graphics; -use cg::base::{CGFloat, CGAffineTransform}; -use cg::data_provider::{ - CGDataProviderRef, CGDataProvider -}; -use cg::font::{ - CGFontCreateWithDataProvider, - CGFontRef, - CGFontRelease, - CGGlyph, -}; -use cg::geometry::CGRect; - -use ct = core_text; -use ct::font::CTFont; -use ct::font_descriptor::{ - kCTFontDefaultOrientation, - CTFontSymbolicTraits, - SymbolicTraitAccessors, -}; - -pub struct QuartzFontHandle { - priv mut cgfont: Option, - ctfont: CTFont, - - drop { - // TODO(Issue #152): use a wrapped CGFont. - do (copy self.cgfont).iter |cgfont| { - assert cgfont.is_not_null(); - CGFontRelease(*cgfont); - } - } -} - -pub impl QuartzFontHandle { - static fn new_from_buffer(_fctx: &QuartzFontContextHandle, buf: @~[u8], pt_size: float) -> Result { - let fontprov = vec::as_imm_buf(*buf, |cbuf, len| { - CGDataProvider::new_from_buffer(cbuf, len) - }); - - let cgfont = CGFontCreateWithDataProvider(fontprov.get_ref()); - if cgfont.is_null() { return Err(()); } - - let ctfont = CTFont::new_from_CGFont(cgfont, pt_size); - - let result = Ok(QuartzFontHandle { - cgfont : Some(cgfont), - ctfont : move ctfont, - }); - - return move result; - } - - static fn new_from_CTFont(_fctx: &QuartzFontContextHandle, ctfont: CTFont) -> Result { - let result = Ok(QuartzFontHandle { - mut cgfont: None, - ctfont: move ctfont, - }); - - return move result; - } - - fn get_CGFont() -> CGFontRef { - match self.cgfont { - Some(cg) => cg, - None => { - let cgfont = self.ctfont.copy_to_CGFont(); - self.cgfont = Some(cgfont); - cgfont - } - } - } -} - -pub impl QuartzFontHandle : FontHandleMethods { - pure fn family_name() -> ~str { - self.ctfont.family_name() - } - - pure fn face_name() -> ~str { - self.ctfont.face_name() - } - - pure fn is_italic() -> bool { - self.ctfont.symbolic_traits().is_italic() - } - - pure fn boldness() -> CSSFontWeight { - // -1.0 to 1.0 - let normalized = unsafe { self.ctfont.all_traits().normalized_weight() }; - // 0.0 to 9.0 - let normalized = (normalized + 1.0) / 2.0 * 9.0; - if normalized < 1.0 { return FontWeight100; } - if normalized < 2.0 { return FontWeight200; } - if normalized < 3.0 { return FontWeight300; } - if normalized < 4.0 { return FontWeight400; } - if normalized < 5.0 { return FontWeight500; } - if normalized < 6.0 { return FontWeight600; } - if normalized < 7.0 { return FontWeight700; } - if normalized < 8.0 { return FontWeight800; } - else { return FontWeight900; } - } - - - fn glyph_index(codepoint: char) -> Option { - let characters: ~[UniChar] = ~[codepoint as UniChar]; - let glyphs: ~[mut CGGlyph] = ~[mut 0 as CGGlyph]; - let count: CFIndex = 1; - - let result = do vec::as_imm_buf(characters) |character_buf, _l| { - do vec::as_imm_buf(glyphs) |glyph_buf, _l| { - self.ctfont.get_glyphs_for_characters(character_buf, glyph_buf, count) - } - }; - - if !result { - // No glyph for this character - return None; - } - - assert glyphs[0] != 0; // FIXME: error handling - return Some(glyphs[0] as GlyphIndex); - } - - fn glyph_h_advance(glyph: GlyphIndex) -> Option { - let glyphs = ~[glyph as CGGlyph]; - let advance = do vec::as_imm_buf(glyphs) |glyph_buf, _l| { - self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation, glyph_buf, ptr::null(), 1) - }; - - return Some(advance as FractionalPixel); - } - - fn get_metrics() -> FontMetrics { - let bounding_rect: CGRect = self.ctfont.bounding_box(); - let ascent = au::from_pt(self.ctfont.ascent() as float); - let descent = au::from_pt(self.ctfont.descent() as float); - - let metrics = FontMetrics { - underline_size: au::from_pt(self.ctfont.underline_thickness() as float), - // TODO: underline metrics are not reliable. Have to pull out of font table directly. - // see also: https://bugs.webkit.org/show_bug.cgi?id=16768 - // see also: https://bugreports.qt-project.org/browse/QTBUG-13364 - underline_offset: au::from_pt(self.ctfont.underline_position() as float), - leading: au::from_pt(self.ctfont.leading() as float), - x_height: au::from_pt(self.ctfont.x_height() as float), - em_size: ascent + descent, - ascent: ascent, - descent: descent, - max_advance: au::from_pt(bounding_rect.size.width as float) - }; - - debug!("Font metrics (@%f pt): %?", self.ctfont.pt_size() as float, metrics); - return metrics; - } -} - diff --git a/src/servo/gfx/quartz/font_context.rs b/src/servo/gfx/quartz/font_context.rs deleted file mode 100644 index 2e227923a7b..00000000000 --- a/src/servo/gfx/quartz/font_context.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub struct QuartzFontContextHandle { - ctx: (), - - drop { } -} - -pub impl QuartzFontContextHandle { - // this is a placeholder until NSFontManager or whatever is bound in here. - static pub fn new() -> QuartzFontContextHandle { - QuartzFontContextHandle { ctx: () } - } -} \ No newline at end of file diff --git a/src/servo/gfx/quartz/font_list.rs b/src/servo/gfx/quartz/font_list.rs deleted file mode 100644 index dabd4db0a56..00000000000 --- a/src/servo/gfx/quartz/font_list.rs +++ /dev/null @@ -1,64 +0,0 @@ -extern mod core_foundation; -extern mod core_text; - -use cf = core_foundation; -use cf::array::CFArray; -use ct = core_text; -use ct::font::{ - CTFont, - debug_font_names, - debug_font_traits, -}; -use ct::font_collection::CTFontCollection; -use ct::font_descriptor::{ - CTFontDescriptor, - CTFontDescriptorRef, - debug_descriptor, -}; - -use gfx::font::FontHandle; -use gfx::font_list::FontEntry; - -use font::{QuartzFontHandle}; - -use dvec::DVec; -use send_map::{linear, SendMap}; - -pub struct QuartzFontListHandle { - collection: CTFontCollection, -} - -pub impl QuartzFontListHandle { - static pub fn new(_fctx: &native::FontContextHandle) -> QuartzFontListHandle { - QuartzFontListHandle { collection: CTFontCollection::new() } - } - - fn get_available_families(&const self,fctx: &native::FontContextHandle) -> linear::LinearMap<~str, @FontFamily> { - // since we mutate it repeatedly, must be mut variable. - let mut family_map : linear::LinearMap<~str, @FontFamily> = linear::LinearMap(); - let descriptors : CFArray; - descriptors = self.collection.get_descriptors(); - for descriptors.each |desc: &CTFontDescriptor| { - // TODO: for each descriptor, make a FontEntry. - let font = CTFont::new_from_descriptor(desc, 0.0); - let handle = result::unwrap(QuartzFontHandle::new_from_CTFont(fctx, move font)); - let family_name = handle.family_name(); - debug!("Looking for family name: %s", family_name); - let family = match family_map.find(&family_name) { - Some(fam) => fam, - None => { - debug!("Creating new FontFamily for family: %s", family_name); - let new_family = @FontFamily::new(family_name); - family_map.insert(move family_name, new_family); - new_family - } - }; - - debug!("Creating new FontEntry for face: %s", handle.face_name()); - let entry = @FontEntry::new(family, move handle); - family.entries.push(entry); - // TODO: append FontEntry to hashtable value - } - return move family_map; - } -} diff --git a/src/servo/gfx/render_context.rs b/src/servo/gfx/render_context.rs deleted file mode 100644 index c82081c0376..00000000000 --- a/src/servo/gfx/render_context.rs +++ /dev/null @@ -1,103 +0,0 @@ -use compositor::LayerBuffer; -use gfx::au; -use gfx::{Au, Font, FontContext}; -use image::base::Image; -use opts::Opts; -use text::TextRun; -use util::range::Range; - -use azure::azure_hl::{AsAzureRect, B8G8R8A8, Color, ColorPattern, DrawOptions}; -use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions}; -use azure::{AzDrawOptions, AzFloat}; -use core::dvec::DVec; -use core::libc::types::common::c99::uint16_t; -use core::ptr::to_unsafe_ptr; -use geom::point::Point2D; -use geom::rect::Rect; -use geom::size::Size2D; -use std::arc::ARC; - -struct RenderContext { - canvas: &LayerBuffer, - font_ctx: @FontContext, - opts: &Opts -} - -impl RenderContext { - pub fn get_draw_target(&self) -> &self/DrawTarget { - &self.canvas.draw_target - } - - pub fn draw_solid_color(&self, bounds: &Rect, color: Color) { - self.canvas.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color)); - } - - pub fn draw_border(&self, bounds: &Rect, width: Au, color: Color) { - let pattern = ColorPattern(color); - let stroke_fields = 2; // CAP_SQUARE - let width_px = au::to_px(width); - let rect = if width_px % 2 == 0 { - bounds.to_azure_rect() - } else { - bounds.to_azure_snapped_rect() - }; - - let stroke_opts = StrokeOptions(width_px as AzFloat, 10 as AzFloat, stroke_fields); - let draw_opts = DrawOptions(1 as AzFloat, 0 as uint16_t); - - self.canvas.draw_target.stroke_rect(&rect, &pattern, &stroke_opts, &draw_opts); - } - - pub fn draw_image(&self, bounds: Rect, image: ARC<~Image>) { - let image = std::arc::get(&image); - let size = Size2D(image.width as i32, image.height as i32); - let stride = image.width * 4; - - let draw_target_ref = &self.canvas.draw_target; - let azure_surface = draw_target_ref.create_source_surface_from_data(image.data, size, - stride as i32, B8G8R8A8); - let source_rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat), - Size2D(image.width as AzFloat, image.height as AzFloat)); - let dest_rect = bounds.to_azure_rect(); - let draw_surface_options = DrawSurfaceOptions(Linear, true); - let draw_options = DrawOptions(1.0f as AzFloat, 0); - draw_target_ref.draw_surface(move azure_surface, dest_rect, source_rect, - draw_surface_options, draw_options); - } - - fn clear(&self) { - let pattern = ColorPattern(Color(1f as AzFloat, 1f as AzFloat, 1f as AzFloat, 1f as AzFloat)); - let rect = Rect(Point2D(self.canvas.rect.origin.x as AzFloat, - self.canvas.rect.origin.y as AzFloat), - Size2D(self.canvas.rect.size.width as AzFloat, - self.canvas.rect.size.height as AzFloat)); - self.canvas.draw_target.fill_rect(&rect, &pattern); - } -} - -trait to_float { - fn to_float() -> float; -} - -impl u8 : to_float { - fn to_float() -> float { - (self as float) / 255f - } -} - -trait ToAzureRect { - fn to_azure_rect() -> Rect; - fn to_azure_snapped_rect() -> Rect; -} - -impl Rect : ToAzureRect { - fn to_azure_rect() -> Rect { - Rect(Point2D(au::to_px(self.origin.x) as AzFloat, au::to_px(self.origin.y) as AzFloat), - Size2D(au::to_px(self.size.width) as AzFloat, au::to_px(self.size.height) as AzFloat)) - } - - fn to_azure_snapped_rect() -> Rect { - Rect(Point2D(au::to_px(self.origin.x) as AzFloat + 0.5f as AzFloat, au::to_px(self.origin.y) as AzFloat + 0.5f as AzFloat), - Size2D(au::to_px(self.size.width) as AzFloat, au::to_px(self.size.height) as AzFloat)) - } -} diff --git a/src/servo/gfx/render_layers.rs b/src/servo/gfx/render_layers.rs deleted file mode 100644 index 452610fe9b1..00000000000 --- a/src/servo/gfx/render_layers.rs +++ /dev/null @@ -1,137 +0,0 @@ -use gfx::display_list::DisplayList; -use gfx::compositor::{LayerBuffer, LayerBufferSet}; -use opts::Opts; -use util::time; - -use azure::AzFloat; -use azure::azure_hl::{B8G8R8A8, DrawTarget}; -use core::libc::c_int; -use core::pipes::Chan; -use geom::matrix2d::Matrix2D; -use geom::point::Point2D; -use geom::rect::Rect; -use geom::size::Size2D; -use std::arc::ARC; -use std::arc; - -const TILE_SIZE: uint = 512; - -pub struct RenderLayer { - display_list: DisplayList, - size: Size2D -} - -type RenderFn = &fn(layer: *RenderLayer, - buffer: LayerBuffer, - return_buffer: Chan); - -/// Given a layer and a buffer, either reuses the buffer (if it's of the right size and format) -/// or creates a new buffer (if it's not of the appropriate size and format) and invokes the -/// given callback with the render layer and the buffer. Returns the resulting layer buffer (which -/// might be the old layer buffer if it had the appropriate size and format). -pub fn render_layers(layer_ref: *RenderLayer, - buffer_set: LayerBufferSet, - opts: &Opts, - f: RenderFn) -> LayerBufferSet { - let mut buffers = match move buffer_set { LayerBufferSet { buffers: move b } => move b }; - - // FIXME: Try not to create a new array here. - let new_buffer_ports = dvec::DVec(); - - // Divide up the layer into tiles. - do time::time("rendering: preparing buffers") { - let layer: &RenderLayer = unsafe { cast::transmute(layer_ref) }; - let mut y = 0; - while y < layer.size.height { - let mut x = 0; - while x < layer.size.width { - // Figure out the dimension of this tile. - let right = uint::min(x + TILE_SIZE, layer.size.width); - let bottom = uint::min(y + TILE_SIZE, layer.size.height); - let width = right - x; - let height = bottom - y; - - // Round the width up the nearest 32 pixels for DMA on the Mac. - let mut stride = width; - if stride % 32 != 0 { - stride = (stride & !(32 - 1)) + 32; - } - assert stride % 32 == 0; - assert stride >= width; - - debug!("tile stride %u", stride); - - let tile_rect = Rect(Point2D(x, y), Size2D(width, height)); - - let buffer; - // FIXME: Try harder to search for a matching tile. - // FIXME: Don't use shift; it's bad for perf. Maybe reverse and pop. - /*if buffers.len() != 0 && buffers[0].rect == tile_rect { - debug!("reusing tile, (%u, %u)", x, y); - buffer = buffers.shift(); - } else {*/ - // Create a new buffer. - debug!("creating tile, (%u, %u)", x, y); - - let size = Size2D(stride as i32, height as i32); - - let mut data: ~[u8] = ~[0]; - let offset; - unsafe { - // FIXME: Evil black magic to ensure that we don't perform a slow memzero - // of this buffer. This should be made safe. - - let align = 256; - - let len = ((size.width * size.height * 4) as uint) + align; - vec::reserve(&mut data, len); - vec::raw::set_len(&mut data, len); - - // Round up to the nearest 32-byte-aligned address for DMA on the Mac. - let addr: uint = cast::transmute(ptr::to_unsafe_ptr(&data[0])); - if addr % align == 0 { - offset = 0; - } else { - offset = align - addr % align; - } - - debug!("tile offset is %u, expected addr is %x", offset, addr + offset); - } - - buffer = LayerBuffer { - draw_target: DrawTarget::new_with_data(opts.render_backend, - move data, - offset, - size, - size.width * 4, - B8G8R8A8), - rect: tile_rect, - stride: stride - }; - //} - - // Create a port and channel pair to receive the new buffer. - let (new_buffer_chan, new_buffer_port) = pipes::stream(); - - // Send the buffer to the child. - f(layer_ref, move buffer, move new_buffer_chan); - - // Enqueue the port. - new_buffer_ports.push(move new_buffer_port); - - x += TILE_SIZE; - } - y += TILE_SIZE; - } - } - - let new_buffers = dvec::DVec(); - do time::time("rendering: waiting on subtasks") { - for new_buffer_ports.each |new_buffer_port| { - new_buffers.push(new_buffer_port.recv()); - } - } - - return LayerBufferSet { buffers: move dvec::unwrap(move new_buffers) }; -} - diff --git a/src/servo/gfx/render_task.rs b/src/servo/gfx/render_task.rs deleted file mode 100644 index 157a4f0b376..00000000000 --- a/src/servo/gfx/render_task.rs +++ /dev/null @@ -1,159 +0,0 @@ -use dl = display_list; -use gfx::{FontContext, RenderContext, RenderLayer}; -use gfx::compositor::{Compositor, LayerBufferSet}; -use gfx::render_layers; -use opts::Opts; -use platform::osmain; -use render_layers::render_layers; - -use azure::AzFloat; -use core::comm::*; -use core::libc::size_t; -use core::libc::types::common::c99::uint16_t; -use core::pipes::{Port, Chan}; -use core::task::SingleThreaded; -use geom::matrix2d::Matrix2D; -use std::arc::ARC; -use std::arc; -use std::cell::Cell; -use std::thread_pool::ThreadPool; - -pub enum Msg { - RenderMsg(RenderLayer), - ExitMsg(pipes::Chan<()>) -} - -pub type RenderTask = comm::Chan; - -pub fn RenderTask(compositor: C, opts: Opts) -> RenderTask { - let compositor_cell = Cell(move compositor); - let opts_cell = Cell(move opts); - do task::spawn_listener |po: comm::Port, move compositor_cell, move opts_cell| { - let (layer_buffer_channel, layer_buffer_set_port) = pipes::stream(); - - let compositor = compositor_cell.take(); - compositor.begin_drawing(move layer_buffer_channel); - - // FIXME: Annoying three-cell dance here. We need one-shot closures. - let opts = opts_cell.with_ref(|o| copy *o); - let n_threads = opts.n_render_threads; - let new_opts_cell = Cell(move opts); - - let thread_pool = do ThreadPool::new(n_threads, Some(SingleThreaded)) - |move new_opts_cell| { - let opts_cell = Cell(new_opts_cell.with_ref(|o| copy *o)); - let f: ~fn(uint) -> ThreadRenderContext = |thread_index, move opts_cell| { - ThreadRenderContext { - thread_index: thread_index, - font_ctx: @FontContext::new(opts_cell.with_ref(|o| o.render_backend), false), - opts: opts_cell.with_ref(|o| copy *o), - } - }; - move f - }; - - Renderer { - port: po, - compositor: move compositor, - mut layer_buffer_set_port: Cell(move layer_buffer_set_port), - thread_pool: move thread_pool, - opts: opts_cell.take() - }.start(); - } -} - -/// Data that needs to be kept around for each render thread. -priv struct ThreadRenderContext { - thread_index: uint, - font_ctx: @FontContext, - opts: Opts, -} - -priv struct Renderer { - port: comm::Port, - compositor: C, - layer_buffer_set_port: Cell>, - thread_pool: ThreadPool, - opts: Opts, -} - -impl Renderer { - fn start() { - debug!("renderer: beginning rendering loop"); - - loop { - match self.port.recv() { - RenderMsg(move render_layer) => self.render(move render_layer), - ExitMsg(response_ch) => { - response_ch.send(()); - break; - } - } - } - } - - fn render(render_layer: RenderLayer) { - debug!("renderer: got render request"); - - let layer_buffer_set_port = self.layer_buffer_set_port.take(); - - if !layer_buffer_set_port.peek() { - warn!("renderer: waiting on layer buffer"); - } - - let layer_buffer_set = layer_buffer_set_port.recv(); - let (layer_buffer_set_channel, new_layer_buffer_set_port) = pipes::stream(); - self.layer_buffer_set_port.put_back(move new_layer_buffer_set_port); - - let layer_buffer_set_cell = Cell(move layer_buffer_set); - let layer_buffer_set_channel_cell = Cell(move layer_buffer_set_channel); - - #debug("renderer: rendering"); - - do util::time::time(~"rendering") { - let layer_buffer_set = layer_buffer_set_cell.take(); - let layer_buffer_set_channel = layer_buffer_set_channel_cell.take(); - - let layer_buffer_set = do render_layers(ptr::to_unsafe_ptr(&render_layer), - move layer_buffer_set, - &self.opts) - |render_layer_ref, layer_buffer, buffer_chan| { - let layer_buffer_cell = Cell(move layer_buffer); - do self.thread_pool.execute |thread_render_context, - move render_layer_ref, - move buffer_chan, - move layer_buffer_cell| { - do layer_buffer_cell.with_ref |layer_buffer| { - // Build the render context. - let ctx = RenderContext { - canvas: layer_buffer, - font_ctx: thread_render_context.font_ctx, - opts: &thread_render_context.opts - }; - - // Apply the translation to render the tile we want. - let matrix: Matrix2D = Matrix2D::identity(); - let matrix = matrix.translate(&-(layer_buffer.rect.origin.x as AzFloat), - &-(layer_buffer.rect.origin.y as AzFloat)); - layer_buffer.draw_target.set_transform(&matrix); - - // Clear the buffer. - ctx.clear(); - - // Draw the display list. - let render_layer: &RenderLayer = unsafe { - cast::transmute(render_layer_ref) - }; - render_layer.display_list.draw_into_context(&ctx); - } - - // Send back the buffer. - buffer_chan.send(layer_buffer_cell.take()); - } - }; - - #debug("renderer: returning surface"); - self.compositor.draw(move layer_buffer_set_channel, move layer_buffer_set); - } - } -} diff --git a/src/servo/gfx/surface.rs b/src/servo/gfx/surface.rs deleted file mode 100644 index be1b5438ce3..00000000000 --- a/src/servo/gfx/surface.rs +++ /dev/null @@ -1,40 +0,0 @@ -use geom::size::Size2D; - -pub enum format { - fo_rgba_8888 - // TODO: RGB 565, others? -} - -impl format: cmp::Eq { - pure fn eq(other: &format) -> bool { - match (self, *other) { - (fo_rgba_8888, fo_rgba_8888) => true, - } - } - pure fn ne(other: &format) -> bool { - return !self.eq(other); - } -} - -pub type image_surface = { - size: Size2D, - format: format, - buffer: ~[u8] -}; - -impl format { - fn bpp() -> uint { - match self { - fo_rgba_8888 => 32u - } - } -} - -pub fn image_surface(size: Size2D, format: format) -> image_surface { - { - size: copy size, - format: format, - buffer: vec::from_elem((size.area() as uint) * format.bpp(), 0u8) - } -} - diff --git a/src/servo/image/base.rs b/src/servo/image/base.rs deleted file mode 100644 index 5c10331085d..00000000000 --- a/src/servo/image/base.rs +++ /dev/null @@ -1,49 +0,0 @@ -export Image; - -export load; -export load_from_memory; -export test_image_bin; - -use stb_image = stb_image::image; - -// FIXME: Images must not be copied every frame. Instead we should atomically -// reference count them. - -pub type Image = stb_image::Image; - -pub fn Image(width: uint, height: uint, depth: uint, data: ~[u8]) -> Image { - stb_image::new_image(width, height, depth, move data) -} - -const TEST_IMAGE: [u8 * 4962] = #include_bin("test.jpeg"); - -fn test_image_bin() -> ~[u8] { - return vec::from_fn(4962, |i| TEST_IMAGE[i]); -} - -pub fn load_from_memory(buffer: &[u8]) -> Option { - - // Can't remember why we do this. Maybe it's what cairo wants - const FORCE_DEPTH: uint = 4; - - do stb_image::load_from_memory_with_depth(buffer, FORCE_DEPTH).map |image| { - - assert image.depth == 4; - // Do color space conversion :( - let data = do vec::from_fn(image.width * image.height * 4) |i| { - let color = i % 4; - let pixel = i / 4; - match color { - 0 => image.data[pixel * 4 + 2], - 1 => image.data[pixel * 4 + 1], - 2 => image.data[pixel * 4 + 0], - 3 => 0xffu8, - _ => fail - } - }; - - assert image.data.len() == data.len(); - - Image(image.width, image.height, image.depth, move data) - } -} diff --git a/src/servo/image/encode/tga.rs b/src/servo/image/encode/tga.rs deleted file mode 100644 index 6957666a2d3..00000000000 --- a/src/servo/image/encode/tga.rs +++ /dev/null @@ -1,24 +0,0 @@ -use gfx::surface; -use io::WriterUtil; - -fn encode(writer: io::Writer, surface: &surface::image_surface) { - assert surface.format == gfx::surface::fo_rgba_8888; - - writer.write_u8(0u8); // identsize - writer.write_u8(0u8); // colourmaptype - writer.write_u8(2u8); // imagetype - - writer.write_le_u16(0u16); // colourmapstart - writer.write_le_u16(0u16); // colourmaplength - writer.write_u8(16u8); // colourmapbits - - writer.write_le_u16(0u16); // xstart - writer.write_le_u16(0u16); // ystart - writer.write_le_u16(surface.size.width as u16); // width - writer.write_le_u16(surface.size.height as u16); // height - writer.write_u8(32u8); // bits - writer.write_u8(0x30u8); // descriptor - - writer.write(surface.buffer); -} - diff --git a/src/servo/image/holder.rs b/src/servo/image/holder.rs deleted file mode 100644 index ecfdf5aa6b1..00000000000 --- a/src/servo/image/holder.rs +++ /dev/null @@ -1,97 +0,0 @@ -use core::util::replace; -use std::net::url::Url; -use std::arc::{ARC, clone, get}; -use resource::image_cache_task::{ImageCacheTask, ImageReady, ImageNotReady, ImageFailed}; -use mod resource::image_cache_task; -use resource::local_image_cache::LocalImageCache; -use geom::size::Size2D; - -/** A struct to store image data. The image will be loaded once, the - first time it is requested, and an arc will be stored. Clones of - this arc are given out on demand. - */ -pub struct ImageHolder { - url : Url, - mut image : Option>, - mut cached_size: Size2D, - local_image_cache: @LocalImageCache, -} - -fn ImageHolder(url : Url, local_image_cache: @LocalImageCache) -> ImageHolder { - debug!("ImageHolder() %?", url.to_str()); - let holder = ImageHolder { - url : move url, - image : None, - cached_size : Size2D(0,0), - local_image_cache: local_image_cache, - }; - - // Tell the image cache we're going to be interested in this url - // FIXME: These two messages must be sent to prep an image for use - // but they are intended to be spread out in time. Ideally prefetch - // should be done as early as possible and decode only once we - // are sure that the image will be used. - local_image_cache.prefetch(&holder.url); - local_image_cache.decode(&holder.url); - - move holder -} - -impl ImageHolder { - /** - This version doesn't perform any computation, but may be stale w.r.t. - newly-available image data that determines size. - - The intent is that the impure version is used during layout when - dimensions are used for computing layout. - */ - pure fn size() -> Size2D { - self.cached_size - } - - /** Query and update current image size */ - fn get_size() -> Option> { - debug!("get_size() %?", self.url); - match self.get_image() { - Some(img) => { - let img_ref = get(&img); - self.cached_size = Size2D(img_ref.width as int, - img_ref.height as int); - Some(copy self.cached_size) - }, - None => None - } - } - - fn get_image() -> Option> { - debug!("get_image() %?", self.url); - - // If this is the first time we've called this function, load - // the image and store it for the future - if self.image.is_none() { - match self.local_image_cache.get_image(&self.url).recv() { - ImageReady(move image) => { - self.image = Some(move image); - } - ImageNotReady => { - debug!("image not ready for %s", self.url.to_str()); - } - ImageFailed => { - debug!("image decoding failed for %s", self.url.to_str()); - } - } - } - - // Clone isn't pure so we have to swap out the mutable image option - let image = replace(&mut self.image, None); - - let result = match image { - Some(ref image) => Some(clone(image)), - None => None - }; - - replace(&mut self.image, move image); - - return move result; - } -} diff --git a/src/servo/image/test.jpeg b/src/servo/image/test.jpeg deleted file mode 100644 index 1a0bdb7acd1..00000000000 Binary files a/src/servo/image/test.jpeg and /dev/null differ diff --git a/src/servo/layout/block.rs b/src/servo/layout/block.rs index 6f06fe1f103..e2d06c5c950 100644 --- a/src/servo/layout/block.rs +++ b/src/servo/layout/block.rs @@ -3,10 +3,11 @@ use newcss::values::*; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; -use gfx::display_list::{DisplayList, DisplayListBuilder}; +use gfx::display_list::DisplayList; use gfx::geometry::Au; use layout::box::{RenderBox}; use layout::context::LayoutContext; +use layout::display_list_builder::DisplayListBuilder; use layout::flow::{FlowContext, FlowTree, InlineBlockFlow, BlockFlow, RootFlow}; use util::tree; diff --git a/src/servo/layout/box.rs b/src/servo/layout/box.rs index e64080c6160..ab2126661fb 100644 --- a/src/servo/layout/box.rs +++ b/src/servo/layout/box.rs @@ -1,39 +1,34 @@ /* Fundamental layout structures and algorithms. */ -use geom::{Rect, Size2D, Point2D}; - use dom::element::{ElementKind, HTMLDivElement, HTMLImageElement}; use dom::node::{Element, Node, NodeData, NodeKind, NodeTree}; -use gfx::{au, dl}; -use gfx::{ - Au, - DisplayItem, - DisplayList, -}; -use image::{Image, ImageHolder}; use layout::context::LayoutContext; use layout::debug::BoxedDebugMethods; use layout::display_list_builder::DisplayListBuilder; use layout::flow::FlowContext; use layout::text::TextBoxData; -use newcss::color::{Color, rgba, rgb}; -use newcss::complete::CompleteStyle; -use newcss::units::{BoxSizing, Length, Px}; -use newcss::values::{CSSBorderColor, CSSPositionAbsolute}; -use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium}; -use newcss::values::{CSSDisplay, Specified, CSSBackgroundColorColor, CSSBackgroundColorTransparent}; -use servo_text::TextRun; -use util::range::*; -use util::tree; use util::tree::ReadMethods; use arc = std::arc; -use arc::ARC; use core::dvec::DVec; use core::to_str::ToStr; use core::rand; +use core::task::spawn; +use geom::{Point2D, Rect, Size2D}; +use gfx::display_list::{DisplayItem, DisplayList}; +use gfx::geometry::Au; +use gfx::image::base::Image; +use gfx::image::holder::ImageHolder; +use gfx::text::text_run::TextRun; +use gfx::util::range::*; +use newcss::color::{Color, rgba, rgb}; +use newcss::complete::CompleteStyle; +use newcss::units::{BoxSizing, Length, Px}; +use newcss::values::{CSSBackgroundColorColor, CSSBackgroundColorTransparent, CSSBorderColor}; +use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium, CSSDisplay, CSSPositionAbsolute}; +use newcss::values::{Specified}; +use std::arc::ARC; use std::net::url::Url; -use task::spawn; /** Render boxes (`struct RenderBox`) are the leafs of the layout @@ -131,7 +126,7 @@ fn RenderBoxData(node: Node, ctx: @FlowContext, id: int) -> RenderBoxData { RenderBoxData { node : node, mut ctx : ctx, - mut position : au::zero_rect(), + mut position : Au::zero_rect(), font_size: Px(0.0), id : id } @@ -187,7 +182,7 @@ impl RenderBox : RenderBoxMethods { let mut pieces_processed_count : uint = 0; let mut remaining_width : Au = max_width; - let left_range = MutableRange(data.range.begin(), 0); + let left_range = MutableRange::new(data.range.begin(), 0); let mut right_range : Option = None; debug!("split_to_width: splitting text box (strlen=%u, range=%?, avail_width=%?)", data.run.text.len(), data.range, max_width); @@ -264,7 +259,7 @@ impl RenderBox : RenderBoxMethods { GenericBox(*) => Au(0), // TODO: consult CSS 'width', margin, border. // TODO: If image isn't available, consult 'width'. - ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width), + ImageBox(_,i) => Au::from_px(i.get_size().get_default(Size2D(0,0)).width), TextBox(_,d) => d.run.min_width_for_range(d.range), UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here." } @@ -278,7 +273,7 @@ impl RenderBox : RenderBoxMethods { // FlowContext will combine the width of this element and // that of its children to arrive at the context width. GenericBox(*) => Au(0), - ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width), + ImageBox(_,i) => Au::from_px(i.get_size().get_default(Size2D(0,0)).width), // a text box cannot span lines, so assume that this is an unsplit text box. @@ -293,7 +288,7 @@ impl RenderBox : RenderBoxMethods { for d.run.glyphs.iter_glyphs_for_range(line_range) |_char_i, glyph| { line_width += glyph.advance() } - max_line_width = au::max(max_line_width, line_width); + max_line_width = Au::max(max_line_width, line_width); } max_line_width @@ -328,8 +323,8 @@ impl RenderBox : RenderBoxMethods { let size = i.size(); Rect { origin: copy self.d().position.origin, - size: Size2D(au::from_px(size.width), - au::from_px(size.height)) + size: Size2D(Au::from_px(size.width), + Au::from_px(size.height)) } }, GenericBox(*) => { @@ -421,7 +416,7 @@ impl RenderBox : RenderBoxMethods { // debug frames for text box bounds debug!("%?", { list.append_item(~DisplayItem::new_Border(&abs_box_bounds, - au::from_px(1), + Au::from_px(1), rgb(0, 0, 200).to_gfx_color())) ; ()}); }, @@ -469,10 +464,10 @@ impl RenderBox : RenderBoxMethods { CSSBorderWidthLength(Px(right)), CSSBorderWidthLength(Px(bottom)), CSSBorderWidthLength(Px(left))) => { - let top_au = au::from_frac_px(top); - let right_au = au::from_frac_px(right); - let bottom_au = au::from_frac_px(bottom); - let left_au = au::from_frac_px(left); + let top_au = Au::from_frac_px(top); + let right_au = Au::from_frac_px(right); + let bottom_au = Au::from_frac_px(bottom); + let left_au = Au::from_frac_px(left); let all_widths_equal = [top_au, right_au, bottom_au].all(|a| *a == left_au); diff --git a/src/servo/layout/box_builder.rs b/src/servo/layout/box_builder.rs index 56ff9cab487..4be26b4b09e 100644 --- a/src/servo/layout/box_builder.rs +++ b/src/servo/layout/box_builder.rs @@ -1,20 +1,21 @@ /** Creates CSS boxes from a DOM. */ -use au = gfx::geometry; -use core::dvec::DVec; -use newcss::values::{CSSDisplay, CSSDisplayBlock, CSSDisplayInline, CSSDisplayInlineBlock, CSSDisplayNone}; -use newcss::values::{Inherit, Specified}; + use dom::element::*; use dom::node::{Comment, Doctype, Element, Text, Node, LayoutData}; -use image::holder::ImageHolder; use layout::box::*; use layout::block::BlockFlowData; use layout::context::LayoutContext; use layout::flow::*; use layout::inline::InlineFlowData; use layout::root::RootFlowData; -use option::is_none; use util::tree; +use core::dvec::DVec; +use gfx::image::holder::ImageHolder; +use gfx::util::range::MutableRange; +use newcss::values::{CSSDisplay, CSSDisplayBlock, CSSDisplayInline, CSSDisplayInlineBlock}; +use newcss::values::{CSSDisplayNone, Inherit, Specified}; + pub struct LayoutTreeBuilder { mut root_flow: Option<@FlowContext>, mut next_bid: int, @@ -150,7 +151,7 @@ impl BoxGenerator { self.flow.inline().boxes.push(*spacer); } } - let node_range : MutableRange = MutableRange(self.range_stack.pop(), 0); + let node_range: MutableRange = MutableRange::new(self.range_stack.pop(), 0); node_range.extend_to(self.flow.inline().boxes.len()); assert node_range.length() > 0; @@ -393,8 +394,8 @@ impl LayoutTreeBuilder { // TODO: this could be written as a pattern guard, but it triggers // an ICE (mozilla/rust issue #3601) if d.image.is_some() { - let holder = ImageHolder({copy *d.image.get_ref()}, - layout_ctx.image_cache); + let holder = ImageHolder::new({copy *d.image.get_ref()}, + layout_ctx.image_cache); @ImageBox(RenderBoxData(node, ctx, self.next_box_id()), move holder) } else { diff --git a/src/servo/layout/context.rs b/src/servo/layout/context.rs index 972e8c61aa4..c85284bc923 100644 --- a/src/servo/layout/context.rs +++ b/src/servo/layout/context.rs @@ -1,9 +1,7 @@ use geom::rect::Rect; -use gfx::{ - Au, - FontContext, -}; -use resource::local_image_cache::LocalImageCache; +use gfx::font_context::FontContext; +use gfx::geometry::Au; +use gfx::resource::local_image_cache::LocalImageCache; use std::net::url::Url; /* Represents layout task context. */ diff --git a/src/servo/layout/display_list_builder.rs b/src/servo/layout/display_list_builder.rs index 164cc55454d..9753492a974 100644 --- a/src/servo/layout/display_list_builder.rs +++ b/src/servo/layout/display_list_builder.rs @@ -11,7 +11,6 @@ use either::{Left, Right}; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; -use gfx::display_list::DisplayList; use layout::box::{RenderBox, TextBox}; use layout::context::LayoutContext; use layout::flow::FlowContext; @@ -19,6 +18,8 @@ use layout::text::TextBoxData; use util::tree; use vec::push; +use gfx::display_list::DisplayList; + /** A builder object that manages display list builder should mainly hold information about the initial request and desired result---for example, whether the DisplayList to be used for painting or hit diff --git a/src/servo/layout/flow.rs b/src/servo/layout/flow.rs index 04d634c3f88..8677a22a1e3 100644 --- a/src/servo/layout/flow.rs +++ b/src/servo/layout/flow.rs @@ -1,20 +1,20 @@ -use au = gfx::geometry; -use au::Au; -use core::dvec::DVec; use dom::node::Node; -use geom::rect::Rect; -use geom::point::Point2D; -use gfx::display_list::{DisplayList, DisplayListBuilder}; -// TODO: pub-use these use layout::block::BlockFlowData; use layout::box::RenderBox; use layout::context::LayoutContext; use layout::debug::BoxedDebugMethods; +use layout::display_list_builder::DisplayListBuilder; use layout::inline::{InlineFlowData, NodeRange}; use layout::root::RootFlowData; -use util::range::{Range, MutableRange}; use util::tree; +use core::dvec::DVec; +use geom::rect::Rect; +use geom::point::Point2D; +use gfx::display_list::DisplayList; +use gfx::geometry::Au; +use gfx::util::range::{Range, MutableRange}; + /** Servo's experimental layout system builds a tree of FlowContexts and RenderBoxes, and figures out positions and display attributes of tree nodes. Positions are computed in several tree traversals driven @@ -103,7 +103,7 @@ fn FlowData(id: int) -> FlowData { min_width: Au(0), pref_width: Au(0), - position: au::zero_rect() + position: Au::zero_rect() } } diff --git a/src/servo/layout/inline.rs b/src/servo/layout/inline.rs index d82da18192c..df2650a91e6 100644 --- a/src/servo/layout/inline.rs +++ b/src/servo/layout/inline.rs @@ -1,24 +1,19 @@ -use au = gfx::geometry; -use geom::{Point2D, Rect, Size2D}; - use dom::node::Node; -use gfx::dl; -use gfx::{ - Au, - FontStyle, -}; use layout::box::*; use layout::context::LayoutContext; use layout::flow::{FlowContext, InlineFlow}; use layout::text::TextBoxData; -use newcss::values::{BoxAuto, BoxLength, Px}; -use servo_text::util::*; -use util::range::{MutableRange, Range}; use util::tree; use core::dlist::DList; use core::dvec::DVec; -use num::Num; +use core::num::Num; +use geom::{Point2D, Rect, Size2D}; +use gfx::font::FontStyle; +use gfx::geometry::Au; +use gfx::text::util::*; +use gfx::util::range::{MutableRange, Range}; +use newcss::values::{BoxAuto, BoxLength, Px}; use std::arc; /* @@ -158,7 +153,7 @@ struct TextRunScanner { fn TextRunScanner(flow: @FlowContext) -> TextRunScanner { TextRunScanner { - clump: util::range::empty_mut(), + clump: MutableRange::empty(), flow: flow, } } @@ -333,7 +328,7 @@ fn LineboxScanner(inline: @FlowContext) -> LineboxScanner { flow: inline, new_boxes: DVec(), work_list: DList(), - pending_line: {range: util::range::empty_mut(), mut width: Au(0)}, + pending_line: {range: MutableRange::empty(), mut width: Au(0)}, line_spans: DVec() } } @@ -567,8 +562,8 @@ impl FlowContext : InlineLayout { for self.inline().boxes.each |box| { debug!("FlowContext[%d]: measuring %s", self.d().id, box.debug_str()); - min_width = au::max(min_width, box.get_min_width(ctx)); - pref_width = au::max(pref_width, box.get_pref_width(ctx)); + min_width = Au::max(min_width, box.get_min_width(ctx)); + pref_width = Au::max(pref_width, box.get_pref_width(ctx)); } self.d().min_width = min_width; @@ -586,11 +581,11 @@ impl FlowContext : InlineLayout { // over the box list, and/or put into RenderBox. for self.inline().boxes.each |box| { box.d().position.size.width = match *box { - @ImageBox(_,img) => au::from_px(img.get_size().get_default(Size2D(0,0)).width), + @ImageBox(_,img) => Au::from_px(img.get_size().get_default(Size2D(0,0)).width), @TextBox(*) => { /* text boxes are initialized with dimensions */ box.d().position.size.width }, - @GenericBox(*) => au::from_px(45), /* TODO: should use CSS 'width'? */ + @GenericBox(*) => Au::from_px(45), /* TODO: should use CSS 'width'? */ _ => fail fmt!("Tried to assign width to unknown Box variant: %?", box) }; } // for boxes.each |box| @@ -609,24 +604,24 @@ impl FlowContext : InlineLayout { fn assign_height_inline(@self, _ctx: &LayoutContext) { // TODO: get from CSS 'line-height' property - let line_height = au::from_px(20); + let line_height = Au::from_px(20); let mut cur_y = Au(0); for self.inline().lines.eachi |i, line_span| { debug!("assign_height_inline: processing line %u with box span: %?", i, line_span); // coords relative to left baseline - let mut linebox_bounding_box = au::zero_rect(); + let mut linebox_bounding_box = Au::zero_rect(); let boxes = &self.inline().boxes; for line_span.eachi |box_i| { let cur_box = boxes[box_i]; // compute box height. cur_box.d().position.size.height = match cur_box { - @ImageBox(_,img) => au::from_px(img.size().height), + @ImageBox(_,img) => Au::from_px(img.size().height), @TextBox(*) => { /* text boxes are initialized with dimensions */ cur_box.d().position.size.height }, - @GenericBox(*) => au::from_px(30), /* TODO: should use CSS 'height'? */ + @GenericBox(*) => Au::from_px(30), /* TODO: should use CSS 'height'? */ _ => fail fmt!("Tried to assign height to unknown Box variant: %s", cur_box.debug_str()) }; @@ -654,7 +649,7 @@ impl FlowContext : InlineLayout { debug!("assign_height_inline: linebox bounding box = %?", linebox_bounding_box); } let linebox_height = linebox_bounding_box.size.height; - cur_y += au::max(line_height, linebox_height); + cur_y += Au::max(line_height, linebox_height); } // /lines.each |line_span| self.d().position.size.height = cur_y; diff --git a/src/servo/layout/layout_task.rs b/src/servo/layout/layout_task.rs index f923a328c2e..8a43bf9cfe5 100644 --- a/src/servo/layout/layout_task.rs +++ b/src/servo/layout/layout_task.rs @@ -7,21 +7,16 @@ use content::content_task; use css::select::new_css_select_ctx; use dom::event::{Event, ReflowEvent}; use dom::node::{Node, LayoutData}; -use gfx::render_task; -use gfx::{Au, DisplayList, FontContext, RenderLayer}; -use gfx::{au, dl}; use layout::box::RenderBox; use layout::box_builder::LayoutTreeBuilder; use layout::context::LayoutContext; +use layout::display_list_builder::DisplayListBuilder; use layout::traverse::*; -use opt = core::option; -use opts::Opts; -use render_task::RenderTask; use resource::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use resource::local_image_cache::LocalImageCache; use util::time::time; -use core::comm::*; +use core::comm::*; // FIXME: Bad! Pipe-ify me. use core::dvec::DVec; use core::mutable::Mut; use core::task::*; @@ -29,9 +24,16 @@ use core::util::replace; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; +use gfx::display_list::DisplayList; +use gfx::font_context::FontContext; +use gfx::geometry::Au; +use gfx::opts::Opts; +use gfx::render_layers::RenderLayer; +use gfx::render_task::{RenderMsg, RenderTask}; use newcss::select::SelectCtx; use newcss::stylesheet::Stylesheet; use newcss::types::OriginAuthor; +use opt = core::option; use std::arc::ARC; use std::cell::Cell; use std::net::url::Url; @@ -159,8 +161,8 @@ impl Layout { // Reset the image cache self.local_image_cache.next_round(self.make_on_image_available_cb(move dom_event_chan)); - let screen_size = Size2D(au::from_px(data.window_size.width as int), - au::from_px(data.window_size.height as int)); + let screen_size = Size2D(Au::from_px(data.window_size.width as int), + Au::from_px(data.window_size.height as int)); let layout_ctx = LayoutContext { image_cache: self.local_image_cache, @@ -202,20 +204,20 @@ impl Layout { } do time("layout: display list building") { - let builder = dl::DisplayListBuilder { + let builder = DisplayListBuilder { ctx: &layout_ctx, }; let mut render_layer = RenderLayer { display_list: DisplayList::new(), - size: Size2D(au::to_px(screen_size.width) as uint, - au::to_px(screen_size.height) as uint) + size: Size2D(screen_size.width.to_px() as uint, + screen_size.height.to_px() as uint) }; // TODO: set options on the builder before building // TODO: be smarter about what needs painting layout_root.build_display_list(&builder, © layout_root.d().position, &mut render_layer.display_list); - self.render_task.send(render_task::RenderMsg(move render_layer)); + self.render_task.send(RenderMsg(move render_layer)); } // time(layout: display list building) // Tell content we're done @@ -242,8 +244,8 @@ impl Layout { match rect { None => Err(()), Some(rect) => { - let size = Size2D(au::to_px(rect.size.width), - au::to_px(rect.size.height)); + let size = Size2D(rect.size.width.to_px(), + rect.size.height.to_px()); Ok(ContentSize(move size)) } } diff --git a/src/servo/layout/root.rs b/src/servo/layout/root.rs index c0640ca1599..8d3d5cdd540 100644 --- a/src/servo/layout/root.rs +++ b/src/servo/layout/root.rs @@ -2,11 +2,12 @@ use au = gfx::geometry; use newcss::values::*; use geom::point::Point2D; use geom::rect::Rect; -use gfx::display_list::{DisplayList, DisplayListBuilder}; +use gfx::display_list::DisplayList; use gfx::geometry::Au; use layout::box::RenderBox; use layout::context::LayoutContext; use layout::flow::{FlowContext, FlowTree, InlineBlockFlow, BlockFlow, RootFlow}; +use layout::display_list_builder::DisplayListBuilder; use util::tree; struct RootFlowData { diff --git a/src/servo/layout/text.rs b/src/servo/layout/text.rs index f3f5e24117f..4ffdee41c1b 100644 --- a/src/servo/layout/text.rs +++ b/src/servo/layout/text.rs @@ -1,8 +1,9 @@ /** Text layout. */ -use servo_text::TextRun; use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox}; -use util::range::Range; + +use gfx::text::text_run::TextRun; +use gfx::util::range::Range; pub struct TextBoxData { run: @TextRun, diff --git a/src/servo/opts.rs b/src/servo/opts.rs deleted file mode 100644 index f6b2567c6e7..00000000000 --- a/src/servo/opts.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! Configuration options for a single run of the servo application. Created -//! from command line arguments. - -use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend}; -use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend}; - -pub struct Opts { - urls: ~[~str], - render_mode: RenderMode, - render_backend: BackendType, - n_render_threads: uint, -} - -pub enum RenderMode { - Screen, - Png(~str) -} - -#[allow(non_implicitly_copyable_typarams)] -pub fn from_cmdline_args(args: &[~str]) -> Opts { - use std::getopts; - - let args = args.tail(); - - let opts = ~[ - getopts::optopt(~"o"), - getopts::optopt(~"r"), - getopts::optopt(~"t"), - ]; - - let opt_match = match getopts::getopts(args, opts) { - result::Ok(m) => { copy m } - result::Err(f) => { fail getopts::fail_str(copy f) } - }; - - let urls = if opt_match.free.is_empty() { - fail ~"servo asks that you provide 1 or more URLs" - } else { - copy opt_match.free - }; - - let render_mode = match getopts::opt_maybe_str(copy opt_match, ~"o") { - Some(move output_file) => { Png(move output_file) } - None => { Screen } - }; - - let render_backend = match getopts::opt_maybe_str(copy opt_match, ~"r") { - Some(move backend_str) => { - if backend_str == ~"direct2d" { - Direct2DBackend - } else if backend_str == ~"core-graphics" { - CoreGraphicsBackend - } else if backend_str == ~"core-graphics-accelerated" { - CoreGraphicsAcceleratedBackend - } else if backend_str == ~"cairo" { - CairoBackend - } else if backend_str == ~"skia" { - SkiaBackend - } else { - fail ~"unknown backend type" - } - } - None => CairoBackend - }; - - let n_render_threads: uint = match getopts::opt_maybe_str(move opt_match, ~"t") { - Some(move n_render_threads_str) => from_str::from_str(n_render_threads_str).get(), - None => 1, // FIXME: Number of cores. - }; - - Opts { - urls: move urls, - render_mode: move render_mode, - render_backend: move render_backend, - n_render_threads: n_render_threads, - } -} diff --git a/src/servo/platform/osmain.rs b/src/servo/platform/osmain.rs index 8f6561a5317..31dd22f3779 100644 --- a/src/servo/platform/osmain.rs +++ b/src/servo/platform/osmain.rs @@ -1,10 +1,6 @@ use ShareGlContext = sharegl::platform::Context; use dom::event::{Event, ResizeEvent}; -use gfx::compositor::{Compositor, LayerBuffer, LayerBufferSet}; -use layers::ImageLayer; -use opts::Opts; use resize_rate_limiter::ResizeRateLimiter; -use util::time; use azure::azure_hl::{BackendType, B8G8R8A8, DataSourceSurface, DrawTarget, SourceSurfaceMethods}; use core::dvec::DVec; @@ -15,10 +11,16 @@ use geom::matrix::{Matrix4, identity}; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; +use gfx::compositor::{Compositor, LayerBuffer, LayerBufferSet}; +use gfx::opts::Opts; +use gfx::util::time; +use layers::ImageLayer; use std::cell::Cell; use std::cmp::FuzzyEq; -pub type OSMain = comm::Chan; +pub struct OSMain { + chan: comm::Chan +} // FIXME: Move me over to opts.rs. enum Mode { @@ -40,18 +42,20 @@ pub enum Msg { fn OSMain(dom_event_chan: pipes::SharedChan, opts: Opts) -> OSMain { let dom_event_chan = Cell(move dom_event_chan); - do on_osmain:: |po, move dom_event_chan, move opts| { - do platform::runmain { - #debug("preparing to enter main loop"); + OSMain { + chan: do on_osmain:: |po, move dom_event_chan, move opts| { + do platform::runmain { + #debug("preparing to enter main loop"); - // FIXME: Use the servo options. - let mode; - match os::getenv("SERVO_SHARE") { - Some(_) => mode = ShareMode, - None => mode = GlutMode - } + // FIXME: Use the servo options. + let mode; + match os::getenv("SERVO_SHARE") { + Some(_) => mode = ShareMode, + None => mode = GlutMode + } - mainloop(mode, po, dom_event_chan.take(), &opts); + mainloop(mode, po, dom_event_chan.take(), &opts); + } } } } @@ -258,10 +262,10 @@ compositor for the renderer */ impl OSMain : Compositor { fn begin_drawing(next_dt: pipes::Chan) { - self.send(BeginDrawing(move next_dt)) + self.chan.send(BeginDrawing(move next_dt)) } fn draw(next_dt: pipes::Chan, draw_me: LayerBufferSet) { - self.send(Draw(move next_dt, move draw_me)) + self.chan.send(Draw(move next_dt, move draw_me)) } } diff --git a/src/servo/resource/file_loader.rs b/src/servo/resource/file_loader.rs deleted file mode 100644 index ab92f26c4a4..00000000000 --- a/src/servo/resource/file_loader.rs +++ /dev/null @@ -1,29 +0,0 @@ -export factory; - -use comm::Chan; -use task::spawn; -use resource_task::{ProgressMsg, Payload, Done}; -use std::net::url::Url; -use io::{file_reader, ReaderUtil}; - -const READ_SIZE: uint = 1024; - -pub fn factory(url: Url, progress_chan: Chan) { - assert url.scheme == ~"file"; - - do spawn |move url| { - // FIXME: Resolve bug prevents us from moving the path out of the URL. - match file_reader(&Path(url.path)) { - Ok(reader) => { - while !reader.eof() { - let data = reader.read_bytes(READ_SIZE); - progress_chan.send(Payload(move data)); - } - progress_chan.send(Done(Ok(()))); - } - Err(*) => { - progress_chan.send(Done(Err(()))); - } - }; - } -} diff --git a/src/servo/resource/http_loader.rs b/src/servo/resource/http_loader.rs deleted file mode 100644 index 8c20c7eb559..00000000000 --- a/src/servo/resource/http_loader.rs +++ /dev/null @@ -1,38 +0,0 @@ -export factory; - -use comm::Chan; -use task::spawn; -use resource_task::{ProgressMsg, Payload, Done}; -use std::net::url::Url; -use http_client::{uv_http_request}; - -pub fn factory(url: Url, progress_chan: Chan) { - assert url.scheme == ~"http"; - - do spawn |move url| { - #debug("http_loader: requesting via http: %?", copy url); - let request = uv_http_request(copy url); - let errored = @mut false; - do request.begin |event, copy url| { - let url = copy url; - match event { - http_client::Status(*) => { } - http_client::Payload(data) => { - #debug("http_loader: got data from %?", url); - let mut junk = None; - *data <-> junk; - progress_chan.send(Payload(option::unwrap(move junk))); - } - http_client::Error(*) => { - #debug("http_loader: error loading %?", url); - *errored = true; - progress_chan.send(Done(Err(()))); - } - } - } - - if !*errored { - progress_chan.send(Done(Ok(()))); - } - } -} diff --git a/src/servo/resource/image_cache_task.rs b/src/servo/resource/image_cache_task.rs deleted file mode 100644 index 9c23d6aa993..00000000000 --- a/src/servo/resource/image_cache_task.rs +++ /dev/null @@ -1,1100 +0,0 @@ -use core::util::replace; -use image::base::{Image, load_from_memory, test_image_bin}; -use std::net::url::Url; -use util::url::{make_url, UrlMap, url_map}; -use pipes::{stream, SharedChan, Chan, Port}; -use task::{spawn, spawn_listener}; -use resource::resource_task; -use resource_task::ResourceTask; -use std::arc::ARC; -use clone_arc = std::arc::clone; -use std::cell::Cell; -use to_str::ToStr; - -pub enum Msg { - /// Tell the cache that we may need a particular image soon. Must be posted - /// before Decode - pub Prefetch(Url), - - // FIXME: We can probably get rid of this Cell now - /// Used be the prefetch tasks to post back image binaries - priv StorePrefetchedImageData(Url, Result, ()>), - - /// Tell the cache to decode an image. Must be posted before GetImage/WaitForImage - pub Decode(Url), - - /// Used by the decoder tasks to post decoded images back to the cache - priv StoreImage(Url, Option>), - - /// Request an Image object for a URL. If the image is not is not immediately - /// available then ImageNotReady is returned. - pub GetImage(Url, Chan), - - /// Wait for an image to become available (or fail to load). - pub WaitForImage(Url, Chan), - - /// For testing - priv OnMsg(fn~(msg: &Msg)), - - /// Clients must wait for a response before shutting down the ResourceTask - pub Exit(Chan<()>) -} - -pub enum ImageResponseMsg { - ImageReady(ARC<~Image>), - ImageNotReady, - ImageFailed -} - -impl ImageResponseMsg { - pure fn clone() -> ImageResponseMsg { - match self { - ImageReady(img) => ImageReady(unsafe { clone_arc(&img) }), - ImageNotReady => ImageNotReady, - ImageFailed => ImageFailed - } - } -} - -impl ImageResponseMsg: cmp::Eq { - pure fn eq(other: &ImageResponseMsg) -> bool { - // FIXME: Bad copies - match (self.clone(), other.clone()) { - (ImageReady(*), ImageReady(*)) => fail ~"unimplemented comparison", - (ImageNotReady, ImageNotReady) => true, - (ImageFailed, ImageFailed) => true, - - (ImageReady(*), _) - | (ImageNotReady, _) - | (ImageFailed, _) => false - } - } - pure fn ne(other: &ImageResponseMsg) -> bool { - return !self.eq(other); - } -} - -pub type ImageCacheTask = SharedChan; - -type DecoderFactory = ~fn() -> ~fn(&[u8]) -> Option; - -pub fn ImageCacheTask(resource_task: ResourceTask) -> ImageCacheTask { - ImageCacheTask_(resource_task, default_decoder_factory) -} - -pub fn ImageCacheTask_(resource_task: ResourceTask, decoder_factory: DecoderFactory) -> ImageCacheTask { - // FIXME: Doing some dancing to avoid copying decoder_factory, our test - // version of which contains an uncopyable type which rust will currently - // copy unsoundly - let decoder_factory_cell = Cell(move decoder_factory); - - let (chan, port) = stream(); - let chan = SharedChan(move chan); - let port_cell = Cell(move port); - let chan_cell = Cell(chan.clone()); - - do spawn |move port_cell, move chan_cell, move decoder_factory_cell| { - ImageCache { - resource_task: resource_task, - decoder_factory: decoder_factory_cell.take(), - port: port_cell.take(), - chan: chan_cell.take(), - state_map: url_map(), - wait_map: url_map(), - need_exit: None - }.run(); - } - - move chan -} - -fn SyncImageCacheTask(resource_task: ResourceTask) -> ImageCacheTask { - let (chan, port) = stream(); - let port_cell = Cell(move port); - - do spawn |move port_cell, move resource_task| { - let port = port_cell.take(); - let inner_cache = ImageCacheTask(resource_task); - - loop { - let msg: Msg = port.recv(); - - match move msg { - GetImage(move url, move response) => { - inner_cache.send(WaitForImage(move url, move response)); - } - Exit(move response) => { - inner_cache.send(Exit(move response)); - break; - } - move msg => inner_cache.send(move msg) - } - } - } - - return move SharedChan(move chan); -} - -struct ImageCache { - /// A handle to the resource task for fetching the image binaries - resource_task: ResourceTask, - /// Creates image decoders - decoder_factory: DecoderFactory, - /// The port on which we'll receive client requests - port: Port, - /// A copy of the shared chan to give to child tasks - chan: SharedChan, - /// The state of processsing an image for a URL - state_map: UrlMap, - /// List of clients waiting on a WaitForImage response - wait_map: UrlMap<@mut ~[Chan]>, - mut need_exit: Option>, -} - -enum ImageState { - Init, - Prefetching(AfterPrefetch), - Prefetched(@Cell<~[u8]>), - Decoding, - Decoded(@ARC<~Image>), - Failed -} - -enum AfterPrefetch { - DoDecode, - DoNotDecode -} - -#[allow(non_implicitly_copyable_typarams)] -impl ImageCache { - - pub fn run() { - - let mut msg_handlers: ~[fn~(msg: &Msg)] = ~[]; - - loop { - let msg = self.port.recv(); - - for msg_handlers.each |handler| { (*handler)(&msg) } - - #debug("image_cache_task: received: %?", msg); - - match move msg { - Prefetch(move url) => self.prefetch(move url), - StorePrefetchedImageData(move url, move data) => { - self.store_prefetched_image_data(move url, move data); - } - Decode(move url) => self.decode(move url), - StoreImage(move url, move image) => self.store_image(move url, move image), - GetImage(move url, move response) => self.get_image(move url, move response), - WaitForImage(move url, move response) => { - self.wait_for_image(move url, move response) - } - OnMsg(move handler) => msg_handlers += [move handler], - Exit(move response) => { - assert self.need_exit.is_none(); - self.need_exit = Some(move response); - } - } - - let need_exit = replace(&mut self.need_exit, None); - - match move need_exit { - Some(move response) => { - // Wait until we have no outstanding requests and subtasks - // before exiting - let mut can_exit = true; - for self.state_map.each_value |state| { - match state { - Prefetching(*) => can_exit = false, - Decoding => can_exit = false, - - Init - | Prefetched(*) - | Decoded(*) - | Failed => () - } - } - - if can_exit { - response.send(()); - break; - } else { - self.need_exit = Some(move response); - } - } - None => () - } - } - } - - priv fn get_state(url: Url) -> ImageState { - match move self.state_map.find(move url) { - Some(move state) => move state, - None => Init - } - } - - priv fn set_state(url: Url, state: ImageState) { - self.state_map.insert(move url, move state); - } - - priv fn prefetch(url: Url) { - match self.get_state(copy url) { - Init => { - let to_cache = self.chan.clone(); - let resource_task = self.resource_task; - let url_cell = Cell(copy url); - - do spawn |move to_cache, move url_cell| { - let url = url_cell.take(); - #debug("image_cache_task: started fetch for %s", url.to_str()); - - let image = load_image_data(copy url, resource_task); - - let result = if image.is_ok() { - Ok(Cell(result::unwrap(move image))) - } else { - Err(()) - }; - to_cache.send(StorePrefetchedImageData(copy url, move result)); - #debug("image_cache_task: ended fetch for %s", (copy url).to_str()); - } - - self.set_state(move url, Prefetching(DoNotDecode)); - } - - Prefetching(*) - | Prefetched(*) - | Decoding - | Decoded(*) - | Failed => { - // We've already begun working on this image - } - } - } - - priv fn store_prefetched_image_data(url: Url, data: Result, ()>) { - match self.get_state(copy url) { - Prefetching(next_step) => { - match data { - Ok(data_cell) => { - let data = data_cell.take(); - self.set_state(copy url, Prefetched(@Cell(move data))); - match next_step { - DoDecode => self.decode(move url), - _ => () - } - } - Err(*) => { - self.set_state(copy url, Failed); - self.purge_waiters(move url, || ImageFailed); - } - } - } - - Init - | Prefetched(*) - | Decoding - | Decoded(*) - | Failed => { - fail ~"wrong state for storing prefetched image" - } - } - } - - priv fn decode(url: Url) { - match self.get_state(copy url) { - Init => fail ~"decoding image before prefetch", - - Prefetching(DoNotDecode) => { - // We don't have the data yet, queue up the decode - self.set_state(move url, Prefetching(DoDecode)) - } - - Prefetching(DoDecode) => { - // We don't have the data yet, but the decode request is queued up - } - - Prefetched(data_cell) => { - assert !data_cell.is_empty(); - - let data = data_cell.take(); - let to_cache = self.chan.clone(); - let url_cell = Cell(copy url); - let decode = self.decoder_factory(); - - do spawn |move url_cell, move decode, move data, move to_cache| { - let url = url_cell.take(); - #debug("image_cache_task: started image decode for %s", url.to_str()); - let image = decode(data); - let image = if image.is_some() { - Some(ARC(~option::unwrap(move image))) - } else { - None - }; - to_cache.send(StoreImage(copy url, move image)); - #debug("image_cache_task: ended image decode for %s", url.to_str()); - } - - self.set_state(move url, Decoding); - } - - Decoding | Decoded(*) | Failed => { - // We've already begun decoding - } - } - } - - priv fn store_image(url: Url, image: Option>) { - - match self.get_state(copy url) { - Decoding => { - match image { - Some(image) => { - self.set_state(copy url, Decoded(@clone_arc(&image))); - self.purge_waiters(move url, || ImageReady(clone_arc(&image)) ); - } - None => { - self.set_state(copy url, Failed); - self.purge_waiters(move url, || ImageFailed ); - } - } - } - - Init - | Prefetching(*) - | Prefetched(*) - | Decoded(*) - | Failed => { - fail ~"incorrect state in store_image" - } - } - - } - - priv fn purge_waiters(url: Url, f: fn() -> ImageResponseMsg) { - match self.wait_map.find(copy url) { - Some(@waiters) => { - for waiters.each |response| { - response.send(f()); - } - self.wait_map.remove(move url); - } - None => () - } - } - - - priv fn get_image(url: Url, response: Chan) { - - match self.get_state(copy url) { - Init => fail ~"request for image before prefetch", - - Prefetching(DoDecode) => { - response.send(ImageNotReady); - } - - Prefetching(DoNotDecode) - | Prefetched(*) => fail ~"request for image before decode", - - Decoding => { - response.send(ImageNotReady) - } - - Decoded(image) => { - response.send(ImageReady(clone_arc(image))); - } - - Failed => { - response.send(ImageFailed); - } - } - } - - priv fn wait_for_image(url: Url, response: Chan) { - match self.get_state(copy url) { - Init => fail ~"request for image before prefetch", - - Prefetching(DoNotDecode) | Prefetched(*) => fail ~"request for image before decode", - - Prefetching(DoDecode) | Decoding => { - // We don't have this image yet - match self.wait_map.find(copy url) { - Some(waiters) => { - vec::push(&mut *waiters, move response); - } - None => { - self.wait_map.insert(move url, @mut ~[move response]); - } - } - } - - Decoded(image) => { - response.send(ImageReady(clone_arc(image))); - } - - Failed => { - response.send(ImageFailed); - } - } - } - -} - - -trait ImageCacheTaskClient { - fn exit(); -} - -impl ImageCacheTask: ImageCacheTaskClient { - - fn exit() { - let (response_chan, response_port) = stream(); - self.send(Exit(move response_chan)); - response_port.recv(); - } - -} - -fn load_image_data(url: Url, resource_task: ResourceTask) -> Result<~[u8], ()> { - let response_port = Port(); - resource_task.send(resource_task::Load(move url, response_port.chan())); - - let mut image_data = ~[]; - - loop { - match response_port.recv() { - resource_task::Payload(data) => { - image_data += data; - } - resource_task::Done(result::Ok(*)) => { - return Ok(move image_data); - } - resource_task::Done(result::Err(*)) => { - return Err(()); - } - } - } -} - -fn default_decoder_factory() -> ~fn(&[u8]) -> Option { - fn~(data: &[u8]) -> Option { load_from_memory(data) } -} - -#[cfg(test)] -fn mock_resource_task(on_load: ~fn(resource: comm::Chan)) -> ResourceTask { - do spawn_listener |port: comm::Port, move on_load| { - loop { - match port.recv() { - resource_task::Load(_, response) => { - on_load(response); - } - resource_task::Exit => break - } - } - } -} - -#[test] -fn should_exit_on_request() { - - let mock_resource_task = mock_resource_task(|_response| () ); - - let image_cache_task = ImageCacheTask(mock_resource_task); - let _url = make_url(~"file", None); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -#[should_fail] -fn should_fail_if_unprefetched_image_is_requested() { - - let mock_resource_task = mock_resource_task(|_response| () ); - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - let (chan, port) = stream(); - image_cache_task.send(GetImage(move url, move chan)); - port.recv(); -} - -#[test] -fn should_request_url_from_resource_task_on_prefetch() { - let url_requested = Port(); - let url_requested_chan = url_requested.chan(); - - let mock_resource_task = do mock_resource_task |response| { - url_requested_chan.send(()); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(move url)); - url_requested.recv(); - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - - -#[test] -#[should_fail] -fn should_fail_if_requesting_decode_of_an_unprefetched_image() { - - let mock_resource_task = mock_resource_task(|_response| () ); - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Decode(move url)); - image_cache_task.exit(); -} - -#[test] -#[should_fail] -fn should_fail_if_requesting_image_before_requesting_decode() { - - let mock_resource_task = do mock_resource_task |response| { - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - // no decode message - - let (chan, _port) = stream(); - image_cache_task.send(GetImage(move url, move chan)); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_not_request_url_from_resource_task_on_multiple_prefetches() { - let url_requested = Port(); - let url_requested_chan = url_requested.chan(); - - let mock_resource_task = do mock_resource_task |response| { - url_requested_chan.send(()); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Prefetch(move url)); - url_requested.recv(); - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - assert !url_requested.peek() -} - -#[test] -fn should_return_image_not_ready_if_data_has_not_arrived() { - - let (wait_chan, wait_port) = pipes::stream(); - - let mock_resource_task = do mock_resource_task |response, move wait_port| { - // Don't send the data until after the client requests - // the image - wait_port.recv(); - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(move url, move response_chan)); - assert response_port.recv() == ImageNotReady; - wait_chan.send(()); - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_decoded_image_data_if_data_has_arrived() { - - let mock_resource_task = do mock_resource_task |response| { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - let wait_for_image = Port(); - let wait_for_image_chan = wait_for_image.chan(); - - image_cache_task.send(OnMsg(|msg| { - match *msg { - StoreImage(*) => wait_for_image_chan.send(()), - _ => () - } - })); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - wait_for_image_chan.recv(); - - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(move url, move response_chan)); - match response_port.recv() { - ImageReady(_) => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_decoded_image_data_for_multiple_requests() { - - let mock_resource_task = do mock_resource_task |response| { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - let wait_for_image = Port(); - let wait_for_image_chan = wait_for_image.chan(); - - image_cache_task.send(OnMsg(|msg| { - match *msg { - StoreImage(*) => wait_for_image_chan.send(()), - _ => () - } - })); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - wait_for_image.recv(); - - for iter::repeat(2) { - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(copy url, move response_chan)); - match response_port.recv() { - ImageReady(_) => (), - _ => fail - } - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_not_request_image_from_resource_task_if_image_is_already_available() { - - let image_bin_sent = Port(); - let image_bin_sent_chan = image_bin_sent.chan(); - - let resource_task_exited = Port(); - let resource_task_exited_chan = resource_task_exited.chan(); - - let mock_resource_task = do spawn_listener |port: comm::Port| { - loop { - match port.recv() { - resource_task::Load(_, response) => { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - image_bin_sent_chan.send(()); - } - resource_task::Exit => { - resource_task_exited_chan.send(()); - break - } - } - } - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - image_bin_sent.recv(); - - image_cache_task.send(Prefetch(copy url)); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - - resource_task_exited.recv(); - - // Our resource task should not have received another request for the image - // because it's already cached - assert !image_bin_sent.peek(); -} - -#[test] -fn should_not_request_image_from_resource_task_if_image_fetch_already_failed() { - - let image_bin_sent = Port(); - let image_bin_sent_chan = image_bin_sent.chan(); - - let resource_task_exited = Port(); - let resource_task_exited_chan = resource_task_exited.chan(); - - let mock_resource_task = do spawn_listener |port: comm::Port| { - loop { - match port.recv() { - resource_task::Load(_, response) => { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Err(()))); - image_bin_sent_chan.send(()); - } - resource_task::Exit => { - resource_task_exited_chan.send(()); - break - } - } - } - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - image_bin_sent.recv(); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - - resource_task_exited.recv(); - - // Our resource task should not have received another request for the image - // because it's already cached - assert !image_bin_sent.peek(); -} - -#[test] -fn should_return_failed_if_image_bin_cannot_be_fetched() { - - let mock_resource_task = do mock_resource_task |response| { - response.send(resource_task::Payload(test_image_bin())); - // ERROR fetching image - response.send(resource_task::Done(result::Err(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - let wait_for_prefetech = Port(); - let wait_for_prefetech_chan = wait_for_prefetech.chan(); - - image_cache_task.send(OnMsg(|msg| { - match *msg { - StorePrefetchedImageData(*) => wait_for_prefetech_chan.send(()), - _ => () - } - })); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - wait_for_prefetech.recv(); - - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(move url, move response_chan)); - match response_port.recv() { - ImageFailed => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_failed_for_multiple_get_image_requests_if_image_bin_cannot_be_fetched() { - - let mock_resource_task = do mock_resource_task |response | { - response.send(resource_task::Payload(test_image_bin())); - // ERROR fetching image - response.send(resource_task::Done(result::Err(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - let wait_for_prefetech = Port(); - let wait_for_prefetech_chan = wait_for_prefetech.chan(); - - image_cache_task.send(OnMsg(|msg| { - match *msg { - StorePrefetchedImageData(*) => wait_for_prefetech_chan.send(()), - _ => () - } - })); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - wait_for_prefetech.recv(); - - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(copy url, move response_chan)); - match response_port.recv() { - ImageFailed => (), - _ => fail - } - - // And ask again, we should get the same response - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(move url, move response_chan)); - match response_port.recv() { - ImageFailed => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_not_ready_if_image_is_still_decoding() { - - let (wait_to_decode_chan, wait_to_decode_port) = pipes::stream(); - - let mock_resource_task = do mock_resource_task |response| { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - }; - - let wait_to_decode_port_cell = Cell(move wait_to_decode_port); - let decoder_factory = fn~(move wait_to_decode_port_cell) -> ~fn(&[u8]) -> Option { - let wait_to_decode_port = wait_to_decode_port_cell.take(); - fn~(data: &[u8], move wait_to_decode_port) -> Option { - // Don't decode until after the client requests the image - wait_to_decode_port.recv(); - load_from_memory(data) - } - }; - - let image_cache_task = ImageCacheTask_(mock_resource_task, move decoder_factory); - let url = make_url(~"file", None); - - let wait_for_prefetech = Port(); - let wait_for_prefetech_chan = wait_for_prefetech.chan(); - - image_cache_task.send(OnMsg(|msg| { - match *msg { - StorePrefetchedImageData(*) => wait_for_prefetech_chan.send(()), - _ => () - } - })); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - wait_for_prefetech.recv(); - - // Make the request - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(move url, move response_chan)); - - match response_port.recv() { - ImageNotReady => (), - _ => fail - } - - // Now decode - wait_to_decode_chan.send(()); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_failed_if_image_decode_fails() { - - let mock_resource_task = do mock_resource_task |response| { - // Bogus data - response.send(resource_task::Payload(~[])); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - let wait_for_decode = Port(); - let wait_for_decode_chan = wait_for_decode.chan(); - - image_cache_task.send(OnMsg(|msg| { - match *msg { - StoreImage(*) => wait_for_decode_chan.send(()), - _ => () - } - })); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - wait_for_decode.recv(); - - // Make the request - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(move url, move response_chan)); - - match response_port.recv() { - ImageFailed => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_image_on_wait_if_image_is_already_loaded() { - - let mock_resource_task = do mock_resource_task |response| { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - let wait_for_decode = Port(); - let wait_for_decode_chan = wait_for_decode.chan(); - - image_cache_task.send(OnMsg(|msg| { - match *msg { - StoreImage(*) => wait_for_decode_chan.send(()), - _ => () - } - })); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - // Wait until our mock resource task has sent the image to the image cache - wait_for_decode.recv(); - - let (response_chan, response_port) = stream(); - image_cache_task.send(WaitForImage(move url, move response_chan)); - match response_port.recv() { - ImageReady(*) => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_image_on_wait_if_image_is_not_yet_loaded() { - - let (wait_chan, wait_port) = pipes::stream(); - - let mock_resource_task = do mock_resource_task |response, move wait_port| { - wait_port.recv(); - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - let (response_chan, response_port) = stream(); - image_cache_task.send(WaitForImage(move url, move response_chan)); - - wait_chan.send(()); - - match response_port.recv() { - ImageReady(*) => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn should_return_image_failed_on_wait_if_image_fails_to_load() { - - let (wait_chan, wait_port) = pipes::stream(); - - let mock_resource_task = do mock_resource_task |response, move wait_port| { - wait_port.recv(); - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Err(()))); - }; - - let image_cache_task = ImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - let (response_chan, response_port) = stream(); - image_cache_task.send(WaitForImage(move url, move response_chan)); - - wait_chan.send(()); - - match response_port.recv() { - ImageFailed => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} - -#[test] -fn sync_cache_should_wait_for_images() { - let mock_resource_task = do mock_resource_task |response| { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(result::Ok(()))); - }; - - let image_cache_task = SyncImageCacheTask(mock_resource_task); - let url = make_url(~"file", None); - - image_cache_task.send(Prefetch(copy url)); - image_cache_task.send(Decode(copy url)); - - let (response_chan, response_port) = stream(); - image_cache_task.send(GetImage(move url, move response_chan)); - match response_port.recv() { - ImageReady(_) => (), - _ => fail - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); -} diff --git a/src/servo/resource/local_image_cache.rs b/src/servo/resource/local_image_cache.rs deleted file mode 100644 index 7dcb82f6ad2..00000000000 --- a/src/servo/resource/local_image_cache.rs +++ /dev/null @@ -1,150 +0,0 @@ -/*! -An adapter for ImageCacheTask that does local caching to avoid -extra message traffic, it also avoids waiting on the same image -multiple times and thus triggering reflows multiple times. -*/ - -use clone_arc = std::arc::clone; -use std::net::url::Url; -use pipes::{Port, Chan, stream}; -use image_cache_task::{ImageCacheTask, ImageResponseMsg, Prefetch, Decode, GetImage, WaitForImage, ImageReady, ImageNotReady, ImageFailed}; -use util::url::{UrlMap, url_map}; - -pub fn LocalImageCache(image_cache_task: ImageCacheTask) -> LocalImageCache { - LocalImageCache { - image_cache_task: move image_cache_task, - round_number: 1, - mut on_image_available: None, - state_map: url_map() - } -} - -pub struct LocalImageCache { - priv image_cache_task: ImageCacheTask, - priv mut round_number: uint, - priv mut on_image_available: Option<@fn() -> ~fn(ImageResponseMsg)>, - priv state_map: UrlMap<@ImageState> -} - -priv struct ImageState { - mut prefetched: bool, - mut decoded: bool, - mut last_request_round: uint, - mut last_response: ImageResponseMsg -} - -#[allow(non_implicitly_copyable_typarams)] // Using maps of Urls -pub impl LocalImageCache { - /// The local cache will only do a single remote request for a given - /// URL in each 'round'. Layout should call this each time it begins - // FIXME: 'pub' is an unexpected token? - /* pub */ fn next_round(on_image_available: @fn() -> ~fn(ImageResponseMsg)) { - self.round_number += 1; - self.on_image_available = Some(move on_image_available); - } - - pub fn prefetch(url: &Url) { - let state = self.get_state(url); - if !state.prefetched { - self.image_cache_task.send(Prefetch(copy *url)); - state.prefetched = true; - } - } - - pub fn decode(url: &Url) { - let state = self.get_state(url); - if !state.decoded { - self.image_cache_task.send(Decode(copy *url)); - state.decoded = true; - } - } - - // FIXME: Should return a Future - pub fn get_image(url: &Url) -> Port { - let state = self.get_state(url); - - // Save the previous round number for comparison - let last_round = state.last_request_round; - // Set the current round number for this image - state.last_request_round = self.round_number; - - match state.last_response { - ImageReady(ref image) => { - // FIXME: appease borrowck - unsafe { - let (chan, port) = pipes::stream(); - chan.send(ImageReady(clone_arc(image))); - return move port; - } - } - ImageNotReady => { - if last_round == self.round_number { - let (chan, port) = pipes::stream(); - chan.send(ImageNotReady); - return move port; - } else { - // We haven't requested the image from the - // remote cache this round - } - } - ImageFailed => { - let (chan, port) = pipes::stream(); - chan.send(ImageFailed); - return move port; - } - } - - let (response_chan, response_port) = pipes::stream(); - self.image_cache_task.send(GetImage(copy *url, move response_chan)); - - let response = response_port.recv(); - match response { - ImageNotReady => { - // Need to reflow when the image is available - // FIXME: Instead we should be just passing a Future - // to the caller, then to the display list. Finally, - // the compositor should be resonsible for waiting - // on the image to load and triggering layout - let image_cache_task = self.image_cache_task.clone(); - assert self.on_image_available.is_some(); - let on_image_available = self.on_image_available.get()(); - let url = copy *url; - do task::spawn |move url, move on_image_available, move image_cache_task| { - let (response_chan, response_port) = pipes::stream(); - image_cache_task.send(WaitForImage(copy url, move response_chan)); - on_image_available(response_port.recv()); - } - } - _ => () - } - - // Put a copy of the response in the cache - let response_copy = match response { - ImageReady(ref image) => ImageReady(clone_arc(image)), - ImageNotReady => ImageNotReady, - ImageFailed => ImageFailed - }; - state.last_response = move response_copy; - - let (chan, port) = pipes::stream(); - chan.send(move response); - return move port; - } - - priv fn get_state(url: &Url) -> @ImageState { - match self.state_map.find(copy *url) { - Some(state) => state, - None => { - let new_state = @ImageState { - prefetched: false, - decoded: false, - last_request_round: 0, - last_response: ImageNotReady - }; - self.state_map.insert(copy *url, new_state); - self.get_state(url) - } - } - } -} - diff --git a/src/servo/resource/resource_task.rs b/src/servo/resource/resource_task.rs deleted file mode 100644 index 9bca9cdf8dc..00000000000 --- a/src/servo/resource/resource_task.rs +++ /dev/null @@ -1,157 +0,0 @@ -/*! - -A task that takes a URL and streams back the binary data - -*/ - -use comm::{Chan, Port}; -use task::{spawn, spawn_listener}; -use std::net::url; -use std::net::url::{Url, to_str}; - -pub enum ControlMsg { - /// Request the data associated with a particular URL - Load(Url, Chan), - Exit -} - -/// Messages sent in response to a `Load` message -pub enum ProgressMsg { - /// Binary data - there may be multiple of these - Payload(~[u8]), - /// Indicates loading is complete, either successfully or not - Done(Result<(), ()>) -} - -impl ProgressMsg: cmp::Eq { - pure fn eq(other: &ProgressMsg) -> bool { - match (copy self, copy *other) { - (Payload(a), Payload(b)) => a == b, - (Done(a), Done(b)) => a == b, - - (Payload(*), _) - | (Done(*), _) => false - } - } - pure fn ne(other: &ProgressMsg) -> bool { - return !self.eq(other); - } -} - -/// Handle to a resource task -type ResourceTask = Chan; - -/** -Creates a task to load a specific resource - -The ResourceManager delegates loading to a different type of loader task for -each URL scheme -*/ -type LoaderTaskFactory = fn~(url: Url, Chan); - -/// Create a ResourceTask with the default loaders -fn ResourceTask() -> ResourceTask { - let loaders = ~[ - (~"file", file_loader::factory), - (~"http", http_loader::factory) - ]; - create_resource_task_with_loaders(move loaders) -} - -fn create_resource_task_with_loaders(loaders: ~[(~str, LoaderTaskFactory)]) -> ResourceTask { - do spawn_listener |from_client, move loaders| { - // TODO: change copy to move once we can move out of closures - ResourceManager(from_client, copy loaders).start() - } -} - -pub struct ResourceManager { - from_client: Port, - /// Per-scheme resource loaders - loaders: ~[(~str, LoaderTaskFactory)], -} - - -pub fn ResourceManager(from_client: Port, - loaders: ~[(~str, LoaderTaskFactory)]) -> ResourceManager { - ResourceManager { - from_client : move from_client, - loaders : move loaders, - } -} - - -impl ResourceManager { - fn start() { - loop { - match self.from_client.recv() { - Load(url, progress_chan) => { - self.load(copy url, progress_chan) - } - Exit => { - break - } - } - } - } - - fn load(url: Url, progress_chan: Chan) { - - match self.get_loader_factory(&url) { - Some(loader_factory) => { - #debug("resource_task: loading url: %s", to_str(copy url)); - loader_factory(move url, progress_chan); - } - None => { - #debug("resource_task: no loader for scheme %s", url.scheme); - progress_chan.send(Done(Err(()))); - } - } - } - - fn get_loader_factory(url: &Url) -> Option { - for self.loaders.each |scheme_loader| { - let (scheme, loader_factory) = copy *scheme_loader; - if scheme == url.scheme { - return Some(move loader_factory); - } - } - return None; - } -} - -#[test] -fn test_exit() { - let resource_task = ResourceTask(); - resource_task.send(Exit); -} - -#[test] -#[allow(non_implicitly_copyable_typarams)] -fn test_bad_scheme() { - let resource_task = ResourceTask(); - let progress = Port(); - resource_task.send(Load(url::from_str(~"bogus://whatever").get(), progress.chan())); - match progress.recv() { - Done(result) => { assert result.is_err() } - _ => fail - } - resource_task.send(Exit); -} - -#[test] -#[allow(non_implicitly_copyable_typarams)] -fn should_delegate_to_scheme_loader() { - let payload = ~[1, 2, 3]; - let loader_factory = fn~(_url: Url, progress_chan: Chan, copy payload) { - progress_chan.send(Payload(copy payload)); - progress_chan.send(Done(Ok(()))); - }; - let loader_factories = ~[(~"snicklefritz", move loader_factory)]; - let resource_task = create_resource_task_with_loaders(move loader_factories); - let progress = Port(); - resource_task.send(Load(url::from_str(~"snicklefritz://heya").get(), progress.chan())); - assert progress.recv() == Payload(move payload); - assert progress.recv() == Done(Ok(())); - resource_task.send(Exit); -} diff --git a/src/servo/servo.rc b/src/servo/servo.rc index 416b7b40c19..2add8bd301d 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -10,6 +10,7 @@ extern mod azure; extern mod cairo; extern mod geom; +extern mod gfx (name = "servo_gfx"); extern mod glut; extern mod http_client; extern mod hubbub; @@ -69,54 +70,6 @@ pub mod layout { mod aux; } -pub mod gfx { - priv mod render_context; - - // rendering - pub mod color; - pub mod compositor; - pub mod display_list; - pub mod geometry; - pub mod render_layers; - pub mod render_task; - pub mod surface; - - // fonts - pub mod font; - pub mod font_context; - pub mod font_list; - - // Pub-uses for multiple implementations. Platform selection happens in - // font.rs, font_list.rs, font_context.rs - pub mod native; - - #[cfg(target_os = "macos")] - pub mod quartz { - pub mod font; - pub mod font_context; - pub mod font_list; - } - - #[cfg(target_os = "linux")] - pub mod freetype { - pub mod font; - pub mod font_context; - } - - #[cfg(target_os = "linux")] - pub mod fontconfig { - pub mod font_list; - } -} - -pub mod image { - pub mod base; - pub mod encode { - pub mod tga; - } - pub mod holder; -} - pub mod html { pub mod cssparse; pub mod hubbub_html_parser; @@ -128,37 +81,9 @@ pub mod platform { priv mod resize_rate_limiter; } -pub mod resource { - pub mod file_loader; - pub mod http_loader; - pub mod image_cache_task; - pub mod local_image_cache; - pub mod resource_task; -} - -pub mod text { - pub mod glyph; - pub mod text_run; - pub mod util; - pub mod shaper; - - // Below are the actual platform-specific parts. - pub mod harfbuzz { - pub mod shaper; - } -} - pub mod util { pub mod actor; - pub mod cache; - pub mod range; - pub mod time; pub mod tree; - pub mod url; - pub mod vec; } -pub mod opts; - use servo_util = util; -use servo_text = text; diff --git a/src/servo/servo.rs b/src/servo/servo.rs index 18ea420883a..7c23784072a 100644 --- a/src/servo/servo.rs +++ b/src/servo/servo.rs @@ -3,22 +3,23 @@ extern mod core_graphics; #[cfg(target_os="macos")] extern mod core_text; -use comm::*; -use option::swap_unwrap; -use platform::osmain; -use osmain::{OSMain, AddKeyHandler}; -use opts::{Opts, Screen, Png}; -use engine::{Engine, ExitMsg, LoadURLMsg}; -use resource::image_cache_task::ImageCacheTask; -use resource::resource_task::ResourceTask; +use engine::{Engine, ExitMsg, LoadURLMsg}; // FIXME: "ExitMsg" is pollution. +use platform::osmain::{AddKeyHandler, OSMain}; -use util::url::make_url; +use core::comm::*; // FIXME: Bad! +use core::option::swap_unwrap; +use core::pipes::{Port, Chan}; -use pipes::{Port, Chan}; +pub use gfx::opts::{Opts, Png, Screen}; // FIXME: Do we really want "Screen" and "Png" visible? +pub use gfx::resource; +pub use gfx::resource::image_cache_task::ImageCacheTask; +pub use gfx::resource::resource_task::ResourceTask; +pub use gfx::text; +pub use gfx::util::url::make_url; fn main() { let args = os::args(); - run(&opts::from_cmdline_args(args)) + run(&gfx::opts::from_cmdline_args(args)) } #[allow(non_implicitly_copyable_typarams)] @@ -44,7 +45,7 @@ fn run_pipeline_screen(opts: &Opts) { // Send each file to render then wait for keypress let (keypress_to_engine, keypress_from_osmain) = pipes::stream(); - osmain.send(AddKeyHandler(move keypress_to_engine)); + osmain.chan.send(AddKeyHandler(move keypress_to_engine)); // Create a servo instance let resource_task = ResourceTask(); @@ -70,7 +71,7 @@ fn run_pipeline_screen(opts: &Opts) { engine_task.send(engine::ExitMsg(move exit_chan)); exit_response_from_engine.recv(); - osmain.send(osmain::Exit); + osmain.chan.send(platform::osmain::Exit); } fn run_pipeline_png(_opts: &Opts, _outfile: &str) { diff --git a/src/servo/text.rs b/src/servo/text.rs deleted file mode 100644 index 6b3fdd4ff0a..00000000000 --- a/src/servo/text.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* This file exists just to make it easier to import things inside of - ./text/ without specifying the file they came out of imports. - -Note that you still must define each of the files as a module in -servo.rc. This is not ideal and may be changed in the future. */ - -pub use shaper::Shaper; -pub use text_run::TextRun; -pub use text_run::SendableTextRun; diff --git a/src/servo/text/glyph.rs b/src/servo/text/glyph.rs deleted file mode 100644 index 9214356ade8..00000000000 --- a/src/servo/text/glyph.rs +++ /dev/null @@ -1,619 +0,0 @@ -use au = gfx::geometry; -use au::Au; -use core::cmp::{Ord, Eq}; -use core::dvec::DVec; -use core::u16; -use geom::point::Point2D; -use num::from_int; -use std::sort; -use servo_util::range::Range; -use servo_util::vec::*; - - -// GlyphEntry is a port of Gecko's CompressedGlyph scheme for storing -// glyph data compactly. -// -// In the common case (reasonable glyph advances, no offsets from the -// font em-box, and one glyph per character), we pack glyph advance, -// glyph id, and some flags into a single u32. -// -// In the uncommon case (multiple glyphs per unicode character, large -// glyph index/advance, or glyph offsets), we pack the glyph count -// into GlyphEntry, and store the other glyph information in -// DetailedGlyphStore. -struct GlyphEntry { - value : u32 -} - -pure fn GlyphEntry(value: u32) -> GlyphEntry { GlyphEntry { value: value } } - -/// The index of a particular glyph within a font -type GlyphIndex = u32; - -// TODO: unify with bit flags? -enum BreakType { - BreakTypeNone, - BreakTypeNormal, - BreakTypeHyphen -} - -const BREAK_TYPE_NONE : u8 = 0x0u8; -const BREAK_TYPE_NORMAL : u8 = 0x1u8; -const BREAK_TYPE_HYPHEN : u8 = 0x2u8; - -pure fn break_flag_to_enum(flag: u8) -> BreakType { - if (flag & BREAK_TYPE_NONE) as bool { return BreakTypeNone; } - if (flag & BREAK_TYPE_NORMAL) as bool { return BreakTypeNormal; } - if (flag & BREAK_TYPE_HYPHEN) as bool { return BreakTypeHyphen; } - fail ~"Unknown break setting" -} - -pure fn break_enum_to_flag(e: BreakType) -> u8 { - match e { - BreakTypeNone => BREAK_TYPE_NONE, - BreakTypeNormal => BREAK_TYPE_NORMAL, - BreakTypeHyphen => BREAK_TYPE_HYPHEN, - } -} - -// TODO: make this more type-safe. - -const FLAG_CHAR_IS_SPACE : u32 = 0x10000000u32; -// These two bits store some BREAK_TYPE_* flags -const FLAG_CAN_BREAK_MASK : u32 = 0x60000000u32; -const FLAG_CAN_BREAK_SHIFT : u32 = 29; -const FLAG_IS_SIMPLE_GLYPH : u32 = 0x80000000u32; - -// glyph advance; in Au's. -const GLYPH_ADVANCE_MASK : u32 = 0x0FFF0000u32; -const GLYPH_ADVANCE_SHIFT : u32 = 16; -const GLYPH_ID_MASK : u32 = 0x0000FFFFu32; - -// Non-simple glyphs (more than one glyph per char; missing glyph, -// newline, tab, large advance, or nonzero x/y offsets) may have one -// or more detailed glyphs associated with them. They are stored in a -// side array so that there is a 1:1 mapping of GlyphEntry to -// unicode char. - -// The number of detailed glyphs for this char. If the char couldn't -// be mapped to a glyph (!FLAG_NOT_MISSING), then this actually holds -// the UTF8 code point instead. -const GLYPH_COUNT_MASK : u32 = 0x00FFFF00u32; -const GLYPH_COUNT_SHIFT : u32 = 8; -// N.B. following Gecko, these are all inverted so that a lot of -// missing chars can be memset with zeros in one fell swoop. -const FLAG_NOT_MISSING : u32 = 0x00000001u32; -const FLAG_NOT_CLUSTER_START : u32 = 0x00000002u32; -const FLAG_NOT_LIGATURE_GROUP_START : u32 = 0x00000004u32; - -const FLAG_CHAR_IS_TAB : u32 = 0x00000008u32; -const FLAG_CHAR_IS_NEWLINE : u32 = 0x00000010u32; -const FLAG_CHAR_IS_LOW_SURROGATE : u32 = 0x00000020u32; -const CHAR_IDENTITY_FLAGS_MASK : u32 = 0x00000038u32; - -pure fn is_simple_glyph_id(glyphId: GlyphIndex) -> bool { - ((glyphId as u32) & GLYPH_ID_MASK) == glyphId -} - -pure fn is_simple_advance(advance: Au) -> bool { - let unsignedAu = advance.to_int() as u32; - (unsignedAu & (GLYPH_ADVANCE_MASK >> GLYPH_ADVANCE_SHIFT)) == unsignedAu -} - -type DetailedGlyphCount = u16; - -pure fn InitialGlyphEntry() -> GlyphEntry { - GlyphEntry { value: 0 } -} - -// Creates a GlyphEntry for the common case -pure fn SimpleGlyphEntry(index: GlyphIndex, advance: Au) -> GlyphEntry { - assert is_simple_glyph_id(index); - assert is_simple_advance(advance); - - let index_mask = index as u32; - let advance_mask = (*advance as u32) << GLYPH_ADVANCE_SHIFT; - - GlyphEntry { - value: index_mask | advance_mask | FLAG_IS_SIMPLE_GLYPH - } -} - -// Create a GlyphEntry for uncommon case; should be accompanied by -// initialization of the actual DetailedGlyph data in DetailedGlyphStore -pure fn ComplexGlyphEntry(startsCluster: bool, startsLigature: bool, glyphCount: uint) -> GlyphEntry { - assert glyphCount <= u16::max_value as uint; - - let mut val = FLAG_NOT_MISSING; - - if !startsCluster { - val |= FLAG_NOT_CLUSTER_START; - } - if !startsLigature { - val |= FLAG_NOT_LIGATURE_GROUP_START; - } - val |= (glyphCount as u32) << GLYPH_COUNT_SHIFT; - - GlyphEntry { - value: val - } -} - -// Create a GlyphEntry for the case where glyphs couldn't be found -// for the specified character. -pure fn MissingGlyphsEntry(glyphCount: uint) -> GlyphEntry { - assert glyphCount <= u16::max_value as uint; - - GlyphEntry { - value: (glyphCount as u32) << GLYPH_COUNT_SHIFT - } -} - -// Getters and setters for GlyphEntry. Setter methods are functional, -// because GlyphEntry is immutable and only a u32 in size. -impl GlyphEntry { - // getter methods - pure fn advance() -> Au { - assert self.is_simple(); - from_int(((self.value & GLYPH_ADVANCE_MASK) >> GLYPH_ADVANCE_SHIFT) as int) - } - - pure fn index() -> GlyphIndex { - assert self.is_simple(); - self.value & GLYPH_ID_MASK - } - - pure fn offset() -> Point2D { - assert self.is_simple(); - Point2D(Au(0), Au(0)) - } - - pure fn is_ligature_start() -> bool { - self.has_flag(!FLAG_NOT_LIGATURE_GROUP_START) - } - - pure fn is_cluster_start() -> bool { - self.has_flag(!FLAG_NOT_CLUSTER_START) - } - - // True if original char was normal (U+0020) space. Other chars may - // map to space glyph, but this does not account for them. - pure fn char_is_space() -> bool { - self.has_flag(FLAG_CHAR_IS_SPACE) - } - - pure fn char_is_tab() -> bool { - !self.is_simple() && self.has_flag(FLAG_CHAR_IS_TAB) - } - - pure fn char_is_newline() -> bool { - !self.is_simple() && self.has_flag(FLAG_CHAR_IS_NEWLINE) - } - - pure fn can_break_before() -> BreakType { - let flag = ((self.value & FLAG_CAN_BREAK_MASK) >> FLAG_CAN_BREAK_SHIFT) as u8; - break_flag_to_enum(flag) - } - - // setter methods - pure fn set_char_is_space() -> GlyphEntry { - GlyphEntry(self.value | FLAG_CHAR_IS_SPACE) - } - - pure fn set_char_is_tab() -> GlyphEntry { - assert !self.is_simple(); - GlyphEntry(self.value | FLAG_CHAR_IS_TAB) - } - - pure fn set_char_is_newline() -> GlyphEntry { - assert !self.is_simple(); - GlyphEntry(self.value | FLAG_CHAR_IS_NEWLINE) - } - - // returns a glyph entry only if the setting had changed. - pure fn set_can_break_before(e: BreakType) -> Option { - let flag = break_enum_to_flag(e); - let mask = (flag as u32) << FLAG_CAN_BREAK_SHIFT; - let toggle = mask ^ (self.value & FLAG_CAN_BREAK_MASK); - - match (toggle as bool) { - true => Some(GlyphEntry(self.value ^ toggle)), - false => None - } - } - - // helper methods - - /*priv*/ pure fn glyph_count() -> u16 { - assert !self.is_simple(); - ((self.value & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT) as u16 - } - - pure fn is_simple() -> bool { - self.has_flag(FLAG_IS_SIMPLE_GLYPH) - } - - /*priv*/ pure fn has_flag(flag: u32) -> bool { - (self.value & flag) != 0 - } -} - -// Stores data for a detailed glyph, in the case that several glyphs -// correspond to one character, or the glyph's data couldn't be packed. -struct DetailedGlyph { - index: GlyphIndex, - // glyph's advance, in the text's direction (RTL or RTL) - advance: Au, - // glyph's offset from the font's em-box (from top-left) - offset: Point2D -} - - -fn DetailedGlyph(index: GlyphIndex, - advance: Au, offset: Point2D) -> DetailedGlyph { - DetailedGlyph { - index: index, - advance: advance, - offset: offset - } -} - -struct DetailedGlyphRecord { - // source string offset/GlyphEntry offset in the TextRun - entry_offset: uint, - // offset into the detailed glyphs buffer - detail_offset: uint -} - -impl DetailedGlyphRecord : Ord { - pure fn lt(other: &DetailedGlyphRecord) -> bool { self.entry_offset < other.entry_offset } - pure fn le(other: &DetailedGlyphRecord) -> bool { self.entry_offset <= other.entry_offset } - pure fn ge(other: &DetailedGlyphRecord) -> bool { self.entry_offset >= other.entry_offset } - pure fn gt(other: &DetailedGlyphRecord) -> bool { self.entry_offset > other.entry_offset } -} - -impl DetailedGlyphRecord : Eq { - pure fn eq(other : &DetailedGlyphRecord) -> bool { self.entry_offset == other.entry_offset } - pure fn ne(other : &DetailedGlyphRecord) -> bool { self.entry_offset != other.entry_offset } -} - -// Manages the lookup table for detailed glyphs. Sorting is deferred -// until a lookup is actually performed; this matches the expected -// usage pattern of setting/appending all the detailed glyphs, and -// then querying without setting. -struct DetailedGlyphStore { - detail_buffer: DVec, - detail_lookup: DVec, - mut lookup_is_sorted: bool, -} - -fn DetailedGlyphStore() -> DetailedGlyphStore { - DetailedGlyphStore { - detail_buffer: DVec(), - detail_lookup: DVec(), - lookup_is_sorted: false - } -} - -impl DetailedGlyphStore { - fn add_detailed_glyphs_for_entry(entry_offset: uint, glyphs: &[DetailedGlyph]) { - let entry = DetailedGlyphRecord { - entry_offset: entry_offset, - detail_offset: self.detail_buffer.len() - }; - - /* TODO: don't actually assert this until asserts are compiled - in/out based on severity, debug/release, etc. This assertion - would wreck the complexity of the lookup. - - See Rust Issue #3647, #2228, #3627 for related information. - - do self.detail_lookup.borrow |arr| { - assert !arr.contains(entry) - } - */ - - self.detail_lookup.push(entry); - self.detail_buffer.push_all(glyphs); - self.lookup_is_sorted = false; - } - - // not pure; may perform a deferred sort. - fn get_detailed_glyphs_for_entry(&self, entry_offset: uint, count: u16) -> &[DetailedGlyph] { - assert count > 0; - assert (count as uint) <= self.detail_buffer.len(); - self.ensure_sorted(); - - let key = DetailedGlyphRecord { - entry_offset: entry_offset, - detail_offset: 0 // unused - }; - - do self.detail_lookup.borrow |records : &[DetailedGlyphRecord]| { - match records.binary_search_index(&key) { - None => fail ~"Invalid index not found in detailed glyph lookup table!", - Some(i) => { - do self.detail_buffer.borrow |glyphs : &[DetailedGlyph]| { - assert i + (count as uint) <= glyphs.len(); - // return a view into the buffer - vec::view(glyphs, i, i + count as uint) - } - } - } - } - } - - fn get_detailed_glyph_with_index(&self, entry_offset: uint, detail_offset: u16) -> &DetailedGlyph { - assert (detail_offset as uint) <= self.detail_buffer.len(); - self.ensure_sorted(); - - let key = DetailedGlyphRecord { - entry_offset: entry_offset, - detail_offset: 0 // unused - }; - - do self.detail_lookup.borrow |records : &[DetailedGlyphRecord]| { - match records.binary_search_index(&key) { - None => fail ~"Invalid index not found in detailed glyph lookup table!", - Some(i) => { - do self.detail_buffer.borrow |glyphs : &[DetailedGlyph]| { - assert i + (detail_offset as uint) < glyphs.len(); - &glyphs[i+(detail_offset as uint)] - } - } - } - } - } - - /*priv*/ fn ensure_sorted() { - if self.lookup_is_sorted { - return; - } - - do self.detail_lookup.borrow_mut |arr| { - sort::quick_sort3(arr); - }; - self.lookup_is_sorted = true; - } -} - -// This struct is used by GlyphStore clients to provide new glyph data. -// It should be allocated on the stack and passed by reference to GlyphStore. -struct GlyphData { - index: GlyphIndex, - advance: Au, - offset: Point2D, - is_missing: bool, - cluster_start: bool, - ligature_start: bool, -} - -pure fn GlyphData(index: GlyphIndex, - advance: Au, - offset: Option>, - is_missing: bool, - cluster_start: bool, - ligature_start: bool) -> GlyphData { - - let _offset = match offset { - None => au::zero_point(), - Some(o) => o - }; - - GlyphData { - index: index, - advance: advance, - offset: _offset, - is_missing: is_missing, - cluster_start: cluster_start, - ligature_start: ligature_start, - } -} - -// This enum is a proxy that's provided to GlyphStore clients when iterating -// through glyphs (either for a particular TextRun offset, or all glyphs). -// Rather than eagerly assembling and copying glyph data, it only retrieves -// values as they are needed from the GlyphStore, using provided offsets. -enum GlyphInfo { - SimpleGlyphInfo(&GlyphStore, uint), - DetailGlyphInfo(&GlyphStore, uint, u16) -} - -impl GlyphInfo { - fn index() -> GlyphIndex { - match self { - SimpleGlyphInfo(store, entry_i) => store.entry_buffer[entry_i].index(), - DetailGlyphInfo(store, entry_i, detail_j) => store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).index - } - } - - fn advance() -> Au { - match self { - SimpleGlyphInfo(store, entry_i) => store.entry_buffer[entry_i].advance(), - DetailGlyphInfo(store, entry_i, detail_j) => store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).advance - } - } - - fn offset() -> Option> { - match self { - SimpleGlyphInfo(_, _) => None, - DetailGlyphInfo(store, entry_i, detail_j) => Some(store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).offset) - } - } - - fn is_ligature_start() -> bool { - match self { - SimpleGlyphInfo(store, entry_i) => store.entry_buffer[entry_i].is_ligature_start(), - DetailGlyphInfo(store, entry_i, _) => store.entry_buffer[entry_i].is_ligature_start() - } - } - - fn is_cluster_start() -> bool { - match self { - SimpleGlyphInfo(store, entry_i) => store.entry_buffer[entry_i].is_cluster_start(), - DetailGlyphInfo(store, entry_i, _) => store.entry_buffer[entry_i].is_cluster_start() - } - } -} - -// Public data structure and API for storing and retrieving glyph data -struct GlyphStore { - // we use a DVec here instead of a mut vec, since this is much safer. - entry_buffer: DVec, - detail_store: DetailedGlyphStore, -} - -// Initializes the glyph store, but doesn't actually shape anything. -// Use the set_glyph, set_glyphs() methods to store glyph data. -fn GlyphStore(length: uint) -> GlyphStore { - assert length > 0; - - let buffer = vec::from_elem(length, InitialGlyphEntry()); - - GlyphStore { - entry_buffer: dvec::from_vec(move buffer), - detail_store: DetailedGlyphStore(), - } -} - -impl GlyphStore { - fn add_glyph_for_index(i: uint, data: &GlyphData) { - - pure fn glyph_is_compressible(data: &GlyphData) -> bool { - is_simple_glyph_id(data.index) - && is_simple_advance(data.advance) - && data.offset == au::zero_point() - } - - assert i < self.entry_buffer.len(); - - let entry = match (data.is_missing, glyph_is_compressible(data)) { - (true, _) => MissingGlyphsEntry(1), - (false, true) => { SimpleGlyphEntry(data.index, data.advance) }, - (false, false) => { - let glyph = [DetailedGlyph(data.index, data.advance, data.offset)]; - self.detail_store.add_detailed_glyphs_for_entry(i, glyph); - ComplexGlyphEntry(data.cluster_start, data.ligature_start, 1) - } - }; - - self.entry_buffer.set_elt(i, entry); - } - - fn add_glyphs_for_index(i: uint, data_for_glyphs: &[GlyphData]) { - assert i < self.entry_buffer.len(); - assert data_for_glyphs.len() > 0; - - let glyph_count = data_for_glyphs.len(); - - let first_glyph_data = data_for_glyphs[0]; - let entry = match first_glyph_data.is_missing { - true => MissingGlyphsEntry(glyph_count), - false => { - let glyphs_vec = vec::from_fn(glyph_count, |i| { - DetailedGlyph(data_for_glyphs[i].index, - data_for_glyphs[i].advance, - data_for_glyphs[i].offset) - }); - - self.detail_store.add_detailed_glyphs_for_entry(i, glyphs_vec); - ComplexGlyphEntry(first_glyph_data.cluster_start, - first_glyph_data.ligature_start, - glyph_count) - } - }; - - self.entry_buffer.set_elt(i, entry); - } - - fn iter_glyphs_for_index(&self, i: uint, cb: fn&(uint, GlyphInfo/&) -> T) { - assert i < self.entry_buffer.len(); - - let entry = &self.entry_buffer[i]; - match entry.is_simple() { - true => { - let proxy = SimpleGlyphInfo(self, i); - cb(i, proxy); - }, - false => { - let glyphs = self.detail_store.get_detailed_glyphs_for_entry(i, entry.glyph_count()); - for uint::range(0, glyphs.len()) |j| { - let proxy = DetailGlyphInfo(self, i, j as u16); - cb(i, proxy); - } - } - } - } - - fn iter_glyphs_for_range(&self, range: Range, cb: fn&(uint, GlyphInfo/&) -> T) { - assert range.begin() < self.entry_buffer.len(); - assert range.end() <= self.entry_buffer.len(); - - for range.eachi |i| { self.iter_glyphs_for_index(i, cb); } - } - - fn iter_all_glyphs(cb: fn&(uint, GlyphInfo/&) -> T) { - for uint::range(0, self.entry_buffer.len()) |i| { - self.iter_glyphs_for_index(i, cb); - } - } - - // getter methods - fn char_is_space(i: uint) -> bool { - assert i < self.entry_buffer.len(); - self.entry_buffer[i].char_is_space() - } - - fn char_is_tab(i: uint) -> bool { - assert i < self.entry_buffer.len(); - self.entry_buffer[i].char_is_tab() - } - - fn char_is_newline(i: uint) -> bool { - assert i < self.entry_buffer.len(); - self.entry_buffer[i].char_is_newline() - } - - fn is_ligature_start(i: uint) -> bool { - assert i < self.entry_buffer.len(); - self.entry_buffer[i].is_ligature_start() - } - - fn is_cluster_start(i: uint) -> bool { - assert i < self.entry_buffer.len(); - self.entry_buffer[i].is_cluster_start() - } - - fn can_break_before(i: uint) -> BreakType { - assert i < self.entry_buffer.len(); - self.entry_buffer[i].can_break_before() - } - - // setter methods - fn set_char_is_space(i: uint) { - assert i < self.entry_buffer.len(); - let entry = self.entry_buffer[i]; - self.entry_buffer.set_elt(i, entry.set_char_is_space()) - } - - fn set_char_is_tab(i: uint) { - assert i < self.entry_buffer.len(); - let entry = self.entry_buffer[i]; - self.entry_buffer.set_elt(i, entry.set_char_is_tab()) - } - - fn set_char_is_newline(i: uint) { - assert i < self.entry_buffer.len(); - let entry = self.entry_buffer[i]; - self.entry_buffer.set_elt(i, entry.set_char_is_newline()) - } - - fn set_can_break_before(i: uint, t: BreakType) { - assert i < self.entry_buffer.len(); - let entry = self.entry_buffer[i]; - match entry.set_can_break_before(t) { - Some(e) => self.entry_buffer.set_elt(i, e), - None => {} - }; - } -} diff --git a/src/servo/text/harfbuzz/shaper.rs b/src/servo/text/harfbuzz/shaper.rs deleted file mode 100644 index f5e24de6851..00000000000 --- a/src/servo/text/harfbuzz/shaper.rs +++ /dev/null @@ -1,191 +0,0 @@ -extern mod harfbuzz; - -use geom::Point2D; - -use gfx::au; -use gfx::{ - Au, - Font, -}; -use glyph::{GlyphStore, GlyphIndex, GlyphData}; - -use libc::types::common::c99::int32_t; -use libc::{c_uint, c_int, c_void, c_char}; -use ptr::{null, to_unsafe_ptr, offset}; -use std::arc; - -use harfbuzz::{HB_MEMORY_MODE_READONLY, - HB_DIRECTION_LTR}; -use harfbuzz::{hb_blob_t, hb_face_t, hb_font_t, hb_font_funcs_t, hb_buffer_t, - hb_codepoint_t, hb_bool_t, hb_glyph_position_t, - hb_glyph_info_t, hb_var_int_t, hb_position_t}; -use harfbuzz::bindgen::{hb_blob_create, hb_blob_destroy, - hb_face_create, hb_face_destroy, - hb_font_create, hb_font_destroy, - hb_buffer_create, hb_buffer_destroy, - hb_buffer_add_utf8, hb_shape, - hb_buffer_get_glyph_infos, - hb_buffer_get_glyph_positions, - hb_font_set_ppem, hb_font_set_scale, - hb_buffer_set_direction, - hb_font_funcs_create, hb_font_funcs_destroy, - hb_font_set_funcs, - hb_font_funcs_set_glyph_h_advance_func, - hb_font_funcs_set_glyph_func, - hb_font_funcs_set_glyph_h_kerning_func}; - -pub struct HarfbuzzShaper { - priv font: @Font, - priv hb_blob: *hb_blob_t, - priv hb_face: *hb_face_t, - priv hb_font: *hb_font_t, - priv hb_funcs: *hb_font_funcs_t, - - drop { - assert self.hb_blob.is_not_null(); - hb_blob_destroy(self.hb_blob); - - assert self.hb_face.is_not_null(); - hb_face_destroy(self.hb_face); - - assert self.hb_font.is_not_null(); - hb_font_destroy(self.hb_font); - - assert self.hb_funcs.is_not_null(); - hb_font_funcs_destroy(self.hb_funcs); - } -} - -pub impl HarfbuzzShaper { - static pub fn new(font: @Font) -> HarfbuzzShaper { - // TODO(Issue #92): font tables should be stored in Font object and cached per-task - let hb_blob: *hb_blob_t = vec::as_imm_buf(*(font).buf(), |buf: *u8, len: uint| { - hb_blob_create(buf as *c_char, - len as c_uint, - HB_MEMORY_MODE_READONLY, - null(), - null()) - }); - - let hb_face: *hb_face_t = hb_face_create(hb_blob, 0 as c_uint); - let hb_font: *hb_font_t = hb_font_create(hb_face); - // Set points-per-em. if zero, performs no hinting in that direction. - let pt_size = font.style.pt_size; - hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint); - // Set scaling. Note that this takes 16.16 fixed point. - hb_font_set_scale(hb_font, - HarfbuzzShaper::float_to_fixed(pt_size) as c_int, - HarfbuzzShaper::float_to_fixed(pt_size) as c_int); - - // configure static function callbacks. - // NB. This funcs structure could be reused globally, as it never changes. - let hb_funcs: *hb_font_funcs_t = hb_font_funcs_create(); - hb_font_funcs_set_glyph_func(hb_funcs, glyph_func, null(), null()); - hb_font_funcs_set_glyph_h_advance_func(hb_funcs, glyph_h_advance_func, null(), null()); - unsafe { - let font_data: *c_void = core::ptr::addr_of(font) as *c_void; - hb_font_set_funcs(hb_font, hb_funcs, font_data, null()); - }; - - HarfbuzzShaper { - font: font, - hb_blob: hb_blob, - hb_face: hb_face, - hb_font: hb_font, - hb_funcs: hb_funcs, - } - } - - /** - Calculate the layout metrics associated with a some given text - when rendered in a specific font. - */ - pub fn shape_text(text: &str, glyphs: &GlyphStore) { - debug!("shaping text '%s'", text); - - // TODO(Issue #94): harfbuzz fonts and faces should be cached on the - // Shaper object, which is owned by the Font instance. - - let hb_buffer: *hb_buffer_t = hb_buffer_create(); - hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR); - - // Using as_buf because it never does a copy - we don't need the trailing null - str::as_buf(text, |ctext: *u8, _l: uint| { - hb_buffer_add_utf8(hb_buffer, - ctext as *c_char, - text.len() as c_int, - 0 as c_uint, - text.len() as c_int); - }); - - hb_shape(self.hb_font, hb_buffer, null(), 0 as c_uint); - - let info_buf_len = 0 as c_uint; - let info_buf = hb_buffer_get_glyph_infos(hb_buffer, to_unsafe_ptr(&info_buf_len)); - assert info_buf.is_not_null(); - let pos_buf_len = 0 as c_uint; - let pos_buf = hb_buffer_get_glyph_positions(hb_buffer, to_unsafe_ptr(&pos_buf_len)); - assert pos_buf.is_not_null(); - - assert info_buf_len == pos_buf_len; - - for uint::range(0u, info_buf_len as uint) |i| { unsafe { - let hb_info: hb_glyph_info_t = *offset(info_buf, i); - let hb_pos: hb_glyph_position_t = *offset(pos_buf, i); - let codepoint = hb_info.codepoint as GlyphIndex; - let advance: Au = au::from_frac_px(HarfbuzzShaper::fixed_to_float(hb_pos.x_advance)); - let offset = match (hb_pos.x_offset, hb_pos.y_offset) { - (0, 0) => None, - (x, y) => Some(Point2D(au::from_frac_px(HarfbuzzShaper::fixed_to_float(x)), - au::from_frac_px(HarfbuzzShaper::fixed_to_float(y)))) - }; - // TODO: convert pos.y_advance into offset adjustment - // TODO: handle multiple glyphs per char, ligatures, etc. - // NB. this debug statement is commented out, as it must be checked for every shaped char. - //debug!("glyph %?: index %?, advance %?, offset %?", i, codepoint, advance, offset); - - let data = GlyphData(codepoint, advance, offset, false, false, false); - glyphs.add_glyph_for_index(i, &data); - } /* unsafe */ } - - hb_buffer_destroy(hb_buffer); - } - - static priv fn float_to_fixed(f: float) -> i32 { - util::float_to_fixed(16, f) - } - - static priv fn fixed_to_float(i: hb_position_t) -> float { - util::fixed_to_float(16, i) - } - - static priv fn fixed_to_rounded_int(f: hb_position_t) -> int { - util::fixed_to_rounded_int(16, f) - } -} - -/// Callbacks from Harfbuzz when font map and glyph advance lookup needed. -extern fn glyph_func(_font: *hb_font_t, - font_data: *c_void, - unicode: hb_codepoint_t, - _variant_selector: hb_codepoint_t, - glyph: *mut hb_codepoint_t, - _user_data: *c_void) -> hb_bool_t unsafe { - let font: *Font = font_data as *Font; - assert font.is_not_null(); - return match (*font).glyph_index(unicode as char) { - Some(g) => { *glyph = g as hb_codepoint_t; true }, - None => false - } as hb_bool_t; -} - -extern fn glyph_h_advance_func(_font: *hb_font_t, - font_data: *c_void, - glyph: hb_codepoint_t, - _user_data: *c_void) -> hb_position_t unsafe { - let font: *Font = font_data as *Font; - assert font.is_not_null(); - - let advance = (*font).glyph_h_advance(glyph as GlyphIndex); - HarfbuzzShaper::float_to_fixed(advance) -} diff --git a/src/servo/text/shaper.rs b/src/servo/text/shaper.rs deleted file mode 100644 index 6face0ef525..00000000000 --- a/src/servo/text/shaper.rs +++ /dev/null @@ -1,17 +0,0 @@ -/** -Shaper encapsulates a specific shaper, such as Harfbuzz, -Uniscribe, Pango, or Coretext. - -Currently, only harfbuzz bindings are implemented. -*/ -use gfx::Font; - -pub type Shaper/& = harfbuzz::shaper::HarfbuzzShaper; - -// TODO(Issue #163): this is a workaround for static methods and -// typedefs not working well together. It should be removed. -impl Shaper { - static pub fn new(font: @Font) -> Shaper { - harfbuzz::shaper::HarfbuzzShaper::new(font) - } -} \ No newline at end of file diff --git a/src/servo/text/text_run.rs b/src/servo/text/text_run.rs deleted file mode 100644 index cf271dabc9b..00000000000 --- a/src/servo/text/text_run.rs +++ /dev/null @@ -1,210 +0,0 @@ -use arc = std::arc; -use arc::ARC; -use geom::point::Point2D; -use geom::size::Size2D; -use gfx::au; -use gfx::{ - Au, - Font, - FontContext, - FontDescriptor, - RunMetrics, -}; -use glyph::GlyphStore; -use layout::context::LayoutContext; -use libc::{c_void}; -use newcss::color; -use std::arc; -use servo_util::range::{Range, MutableRange}; - -pub struct TextRun { - text: ~str, - font: @Font, - priv glyphs: GlyphStore, -} - -// This is a hack until TextRuns are normally sendable, or -// we instead use ARC everywhere. -pub struct SendableTextRun { - text: ~str, - font: FontDescriptor, - priv glyphs: GlyphStore, -} - -impl SendableTextRun { - pub fn deserialize(&self, fctx: @FontContext) -> TextRun { - let font = match fctx.get_font_by_descriptor(&self.font) { - Ok(f) => f, - Err(_) => fail fmt!("Font descriptor deserialization failed! desc=%?", self.font) - }; - - TextRun { - text: copy self.text, - font: font, - glyphs: copy self.glyphs - } - } -} - -impl TextRun { - static fn new(font: @Font, text: ~str) -> TextRun { - let glyph_store = font.shape_text(text); - let run = TextRun { - text: move text, - font: font, - glyphs: move glyph_store, - }; - return move run; - } - - pub fn serialize(&self) -> SendableTextRun { - SendableTextRun { - text: copy self.text, - font: self.font.get_descriptor(), - glyphs: copy self.glyphs, - } - } - - pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs } - - pure fn range_is_trimmable_whitespace(&self, range: Range) -> bool { - let mut i = range.begin(); - while i < range.end() { - // jump i to each new char - let {ch, next} = str::char_range_at(self.text, i); - match ch { - ' ' | '\t' | '\r' => {}, - _ => { return false; } - } - i = next; - } - return true; - } - - fn metrics_for_range(&self, range: Range) -> RunMetrics { - self.font.measure_text(self, range) - } - - fn min_width_for_range(&self, range: Range) -> Au { - assert range.is_valid_for_string(self.text); - - let mut max_piece_width = Au(0); - for self.iter_indivisible_pieces_for_range(range) |piece_range| { - let metrics = self.font.measure_text(self, piece_range); - max_piece_width = au::max(max_piece_width, metrics.advance_width); - } - return max_piece_width; - } - - fn iter_natural_lines_for_range(&self, range: Range, f: fn(Range) -> bool) { - assert range.is_valid_for_string(self.text); - - let clump = MutableRange(range.begin(), 0); - let mut in_clump = false; - - // clump non-linebreaks of nonzero length - for range.eachi |i| { - match (self.glyphs.char_is_newline(i), in_clump) { - (false, true) => { clump.extend_by(1); } - (false, false) => { in_clump = true; clump.reset(i, 1); } - (true, false) => { /* chomp whitespace */ } - (true, true) => { - in_clump = false; - // don't include the linebreak 'glyph' - // (we assume there's one GlyphEntry for a newline, and no actual glyphs) - if !f(clump.as_immutable()) { break } - } - } - } - - // flush any remaining chars as a line - if in_clump { - clump.extend_to(range.end()); - f(clump.as_immutable()); - } - } - - fn iter_indivisible_pieces_for_range(&self, range: Range, f: fn(Range) -> bool) { - assert range.is_valid_for_string(self.text); - - let clump = MutableRange(range.begin(), 0); - loop { - // find next non-whitespace byte index, then clump all whitespace before it. - match str::find_between(self.text, clump.begin(), range.end(), |c| !char::is_whitespace(c)) { - Some(nonws_char_offset) => { - clump.extend_to(nonws_char_offset); - if !f(clump.as_immutable()) { break } - clump.reset(clump.end(), 0); - }, - None => { - // nothing left, flush last piece containing only whitespace - if clump.end() < range.end() { - clump.extend_to(range.end()); - f(clump.as_immutable()); - break; - } - } - }; - - // find next whitespace byte index, then clump all non-whitespace before it. - match str::find_between(self.text, clump.begin(), range.end(), |c| char::is_whitespace(c)) { - Some(ws_char_offset) => { - clump.extend_to(ws_char_offset); - if !f(clump.as_immutable()) { break } - clump.reset(clump.end(), 0); - } - None => { - // nothing left, flush last piece containing only non-whitespaces - if clump.end() < range.end() { - clump.extend_to(range.end()); - f(clump.as_immutable()); - break; - } - } - } - } - } -} - -// this test can't run until LayoutContext is removed as an argument -// to min_width_for_range. -/* -#[test] -fn test_calc_min_break_width() { - - fn test_min_width_for_run(text: ~str, width: Au) { - let flib = FontCache(); - let font = flib.get_test_font(); - let run = TextRun(font, text); - run.min_width_for_range(0, text.len()) - } - - test_min_width_for_run(~"firecracker", au::from_px(84)); - test_min_width_for_run(~"firecracker yumyum", au::from_px(84)); - test_min_width_for_run(~"yumyum firecracker", au::from_px(84)); - test_min_width_for_run(~"yumyum firecracker yumyum", au::from_px(84)); -} -*/ - -/*#[test] -#[ignore] -fn test_iter_indivisible_pieces() { - fn test_pieces(text: ~str, res: ~[~str]) { - let flib = FontCache(); - let font = flib.get_test_font(); - let run = TextRun::new(font, copy text); - let mut slices : ~[~str] = ~[]; - for run.iter_indivisible_pieces_for_range(Range(0, text.len())) |subrange| { - slices.push(str::slice(text, subrange.begin(), subrange.length())); - } - assert slices == res; - } - - test_pieces(~"firecracker yumyum woopwoop", ~[~"firecracker", ~" ", ~"yumyum", ~" ", ~"woopwoop"]); - test_pieces(~"firecracker yumyum ", ~[~"firecracker", ~" ", ~"yumyum", ~" "]); - test_pieces(~" firecracker yumyum", ~[~" ", ~"firecracker", ~" ", ~"yumyum"]); - test_pieces(~" ", ~[~" "]); - test_pieces(~"", ~[]); -} - -*/ \ No newline at end of file diff --git a/src/servo/text/util.rs b/src/servo/text/util.rs deleted file mode 100644 index 61bfa4db21f..00000000000 --- a/src/servo/text/util.rs +++ /dev/null @@ -1,223 +0,0 @@ -enum CompressionMode { - CompressNone, - CompressWhitespace, - CompressWhitespaceNewline, - DiscardNewline -} - -impl CompressionMode : cmp::Eq { - pure fn eq(other: &CompressionMode) -> bool { - match (self, *other) { - (CompressNone, CompressNone) => true, - (CompressWhitespace, CompressWhitespace) => true, - (CompressWhitespaceNewline, CompressWhitespaceNewline) => true, - (DiscardNewline, DiscardNewline) => true, - _ => false - } - } - pure fn ne(other: &CompressionMode) -> bool { - !self.eq(other) - } -} - -// ported from Gecko's nsTextFrameUtils::TransformText. -// -// High level TODOs: -// -// * Issue #113: consider incoming text state (preceding spaces, arabic, etc) -// and propogate outgoing text state (dual of above) -// -// * Issue #114: record skipped and kept chars for mapping original to new text -// -// * Untracked: various edge cases for bidi, CJK, etc. -pub fn transform_text(text: &str, mode: CompressionMode) -> ~str { - let mut out_str: ~str = ~""; - match mode { - CompressNone | DiscardNewline => { - for str::each_char(text) |ch: char| { - if is_discardable_char(ch, mode) { - // TODO: record skipped char - } else { - // TODO: record kept char - if ch == '\t' { - // TODO: set "has tab" flag - } - str::push_char(&mut out_str, ch); - } - } - }, - - CompressWhitespace | CompressWhitespaceNewline => { - let mut in_whitespace: bool = false; - for str::each_char(text) |ch: char| { - // TODO: discard newlines between CJK chars - let mut next_in_whitespace: bool = match (ch, mode) { - (' ', _) => true, - ('\t', _) => true, - ('\n', CompressWhitespaceNewline) => true, - (_, _) => false - }; - - if !next_in_whitespace { - if is_always_discardable_char(ch) { - // revert whitespace setting, since this char was discarded - next_in_whitespace = in_whitespace; - // TODO: record skipped char - } else { - // TODO: record kept char - str::push_char(&mut out_str, ch); - } - } else { /* next_in_whitespace; possibly add a space char */ - if in_whitespace { - // TODO: record skipped char - } else { - // TODO: record kept char - str::push_char(&mut out_str, ' '); - } - } - // save whitespace context for next char - in_whitespace = next_in_whitespace; - } /* /for str::each_char */ - } - } - - return move out_str; - - fn is_discardable_char(ch: char, mode: CompressionMode) -> bool { - if is_always_discardable_char(ch) { - return true; - } - match mode { - DiscardNewline | CompressWhitespaceNewline => ch == '\n', - _ => false - } - } - - fn is_always_discardable_char(_ch: char) -> bool { - // TODO: check for bidi control chars, soft hyphens. - false - } -} - -pub fn float_to_fixed(before: int, f: float) -> i32 { - (1i32 << before) * (f as i32) -} - -pub fn fixed_to_float(before: int, f: i32) -> float { - f as float * 1.0f / ((1i32 << before) as float) -} - -pub fn fixed_to_rounded_int(before: int, f: i32) -> int { - let half = 1i32 << (before-1); - if f > 0i32 { - ((half + f) >> before) as int - } else { - -((half - f) >> before) as int - } -} - -/* Generate a 32-bit TrueType tag from its 4 characters */ -pub fn true_type_tag(a: char, b: char, c: char, d: char) -> u32 { - (a << 24 | b << 16 | c << 8 | d) as u32 -} - -#[test] -fn test_true_type_tag() { - assert true_type_tag('c', 'm', 'a', 'p') == 0x_63_6D_61_70_u32; -} - -#[test] -fn test_transform_compress_none() { - - let test_strs : ~[~str] = ~[~" foo bar", - ~"foo bar ", - ~"foo\n bar", - ~"foo \nbar", - ~" foo bar \nbaz", - ~"foo bar baz", - ~"foobarbaz\n\n"]; - let mode = CompressNone; - - for uint::range(0, test_strs.len()) |i| { - assert transform_text(test_strs[i], mode) == test_strs[i]; - } -} - -#[test] -fn test_transform_discard_newline() { - - let test_strs : ~[~str] = ~[~" foo bar", - ~"foo bar ", - ~"foo\n bar", - ~"foo \nbar", - ~" foo bar \nbaz", - ~"foo bar baz", - ~"foobarbaz\n\n"]; - - let oracle_strs : ~[~str] = ~[~" foo bar", - ~"foo bar ", - ~"foo bar", - ~"foo bar", - ~" foo bar baz", - ~"foo bar baz", - ~"foobarbaz"]; - - assert test_strs.len() == oracle_strs.len(); - let mode = DiscardNewline; - - for uint::range(0, test_strs.len()) |i| { - assert transform_text(test_strs[i], mode) == oracle_strs[i]; - } -} - -#[test] -fn test_transform_compress_whitespace() { - let test_strs : ~[~str] = ~[~" foo bar", - ~"foo bar ", - ~"foo\n bar", - ~"foo \nbar", - ~" foo bar \nbaz", - ~"foo bar baz", - ~"foobarbaz\n\n"]; - - let oracle_strs : ~[~str] = ~[~" foo bar", - ~"foo bar ", - ~"foo\n bar", - ~"foo \nbar", - ~" foo bar \nbaz", - ~"foo bar baz", - ~"foobarbaz\n\n"]; - - assert test_strs.len() == oracle_strs.len(); - let mode = CompressWhitespace; - - for uint::range(0, test_strs.len()) |i| { - assert transform_text(test_strs[i], mode) == oracle_strs[i]; - } -} - -#[test] -fn test_transform_compress_whitespace_newline() { - let test_strs : ~[~str] = ~[~" foo bar", - ~"foo bar ", - ~"foo\n bar", - ~"foo \nbar", - ~" foo bar \nbaz", - ~"foo bar baz", - ~"foobarbaz\n\n"]; - - let oracle_strs : ~[~str] = ~[~" foo bar", - ~"foo bar ", - ~"foo bar", - ~"foo bar", - ~" foo bar baz", - ~"foo bar baz", - ~"foobarbaz "]; - - assert test_strs.len() == oracle_strs.len(); - let mode = CompressWhitespaceNewline; - - for uint::range(0, test_strs.len()) |i| { - assert transform_text(test_strs[i], mode) == oracle_strs[i]; - } -} diff --git a/src/servo/util/cache.rs b/src/servo/util/cache.rs deleted file mode 100644 index ef2825042db..00000000000 --- a/src/servo/util/cache.rs +++ /dev/null @@ -1,59 +0,0 @@ -use core::cmp::*; - -trait Cache { - static fn new(size: uint) -> self; - fn insert(key: &K, value: V); - fn find(key: &K) -> Option; - fn find_or_create(key: &K, blk: pure fn&(&K) -> V) -> V; - fn evict_all(); -} - -pub struct MonoCache { - mut entry: Option<(K,V)>, -} - -pub impl MonoCache : Cache { - static fn new(_size: uint) -> MonoCache { - MonoCache { entry: None } - } - - fn insert(key: &K, value: V) { - self.entry = Some((copy *key, value)); - } - - fn find(key: &K) -> Option { - match self.entry { - None => None, - Some((ref k,v)) => if *k == *key { Some(v) } else { None } - } - } - - fn find_or_create(key: &K, blk: pure fn&(&K) -> V) -> V { - return match self.find(key) { - None => { - let value = blk(key); - self.entry = Some((copy *key, copy value)); - move value - }, - Some(v) => v - }; - } - fn evict_all() { - self.entry = None; - } -} - -#[test] -fn test_monocache() { - // TODO: this is hideous because of Rust Issue #3902 - let cache = cache::new::>(10); - let one = @"one"; - let two = @"two"; - cache.insert(&1, one); - - assert cache.find(&1).is_some(); - assert cache.find(&2).is_none(); - cache.find_or_create(&2, |_v| { two }); - assert cache.find(&2).is_some(); - assert cache.find(&1).is_none(); -} \ No newline at end of file diff --git a/src/servo/util/range.rs b/src/servo/util/range.rs deleted file mode 100644 index 4e236775bdb..00000000000 --- a/src/servo/util/range.rs +++ /dev/null @@ -1,158 +0,0 @@ -pub struct Range { - priv off: u16, - priv len: u16 -} - -pub pure fn Range(off: uint, len: uint) -> Range { - assert off <= u16::max_value as uint; - assert len <= u16::max_value as uint; - - Range { - off: off as u16, - len: len as u16 - } -} - -pub pure fn empty() -> Range { Range(0,0) } - -enum RangeRelation { - OverlapsBegin(/* overlap */ uint), - OverlapsEnd(/* overlap */ uint), - ContainedBy, - Contains, - Coincides, - EntirelyBefore, - EntirelyAfter -} - -pub impl Range { - pub pure fn begin() -> uint { self.off as uint } - pub pure fn length() -> uint { self.len as uint } - pub pure fn end() -> uint { (self.off as uint) + (self.len as uint) } - - pub pure fn eachi(cb: fn&(uint) -> bool) { - do uint::range(self.off as uint, - (self.off as uint) + (self.len as uint)) |i| { - cb(i) - } - } - - pub pure fn is_valid_for_string(s: &str) -> bool { - self.begin() < s.len() && self.end() <= s.len() && self.length() <= s.len() - } - - pub pure fn shift_by(i: int) -> Range { - Range(((self.off as int) + i) as uint, self.len as uint) - } - - pub pure fn extend_by(i: int) -> Range { - Range(self.off as uint, ((self.len as int) + i) as uint) - } - - pub pure fn adjust_by(off_i: int, len_i: int) -> Range { - Range(((self.off as int) + off_i) as uint, ((self.len as int) + len_i) as uint) - } - - /// Computes the relationship between two ranges (`self` and `other`), - /// from the point of view of `self`. So, 'EntirelyBefore' means - /// that the `self` range is entirely before `other` range. - fn relation_to_range(&self, other: Range) -> RangeRelation { - if other.begin() > self.end() { - return EntirelyBefore; - } - if self.begin() > other.end() { - return EntirelyAfter; - } - if self.begin() == other.begin() && self.end() == other.end() { - return Coincides; - } - if self.begin() <= other.begin() && self.end() >= other.end() { - return Contains; - } - if self.begin() >= other.begin() && self.end() <= other.end() { - return ContainedBy; - } - if self.begin() < other.begin() && self.end() < other.end() { - let overlap = self.end() - other.begin(); - return OverlapsBegin(overlap); - } - if self.begin() > other.begin() && self.end() > other.end() { - let overlap = other.end() - self.begin(); - return OverlapsEnd(overlap); - } - fail fmt!("relation_to_range(): didn't classify self=%?, other=%?", - self, other); - } - - fn repair_after_coalesced_range(&self, other: Range) -> Range { - let relation = self.relation_to_range(other); - debug!("repair_after_coalesced_range: possibly repairing range %?", self); - debug!("repair_after_coalesced_range: relation of original range and coalesced range(%?): %?", - other, relation); - let new_range = match relation { - EntirelyBefore => { *self }, - EntirelyAfter => { self.shift_by(-(other.length() as int)) }, - Coincides | ContainedBy => { Range(other.begin(), 1) }, - Contains => { self.extend_by(-(other.length() as int)) }, - OverlapsBegin(overlap) => { self.extend_by(1 - (overlap as int)) }, - OverlapsEnd(overlap) => - { Range(other.begin(), self.length() - overlap + 1) } - }; - debug!("repair_after_coalesced_range: new range: ---- %?", new_range); - new_range - } -} - -pub pure fn empty_mut() -> MutableRange { MutableRange(0,0) } - -pub struct MutableRange { - priv mut off: uint, - priv mut len: uint -} - -pure fn MutableRange(off: uint, len :uint) -> MutableRange { - MutableRange { off: off, len: len } -} - -impl MutableRange { - pub pure fn begin() -> uint { self.off } - pub pure fn length() -> uint { self.len } - pub pure fn end() -> uint { self.off + self.len } - pub pure fn eachi(cb: fn&(uint) -> bool) { - do uint::range(self.off, self.off + self.len) |i| { cb(i) } - } - - fn relation_to_range(&self, other: &MutableRange) -> RangeRelation { - self.as_immutable().relation_to_range(other.as_immutable()) - } - - pub pure fn as_immutable() -> Range { - Range(self.begin(), self.length()) - } - - pub pure fn is_valid_for_string(s: &str) -> bool { - self.begin() < s.len() && self.end() <= s.len() && self.length() <= s.len() - } - - pub fn shift_by(i: int) { - self.off = ((self.off as int) + i) as uint; - } - - pub fn extend_by(i: int) { - self.len = ((self.len as int) + i) as uint; - } - - pub fn extend_to(i: uint) { - self.len = i - self.off; - } - - pub fn adjust_by(off_i: int, len_i: int) { - self.off = ((self.off as int) + off_i) as uint; - self.len = ((self.len as int) + len_i) as uint; - } - - pub fn reset(off_i: uint, len_i: uint) { - self.off = off_i; - self.len = len_i; - } -} diff --git a/src/servo/util/time.rs b/src/servo/util/time.rs deleted file mode 100644 index a62626b5284..00000000000 --- a/src/servo/util/time.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Timing functions. -use std::time::precise_time_ns; - -pub fn time(msg: &str, callback: fn() -> T) -> T{ - let start_time = precise_time_ns(); - let val = callback(); - let end_time = precise_time_ns(); - let ms = ((end_time - start_time) / 1000000u64) as uint; - if ms >= 5 { - #debug("%s took %u ms", msg, ms); - } - return move val; -} - - diff --git a/src/servo/util/url.rs b/src/servo/util/url.rs deleted file mode 100644 index c84bba41511..00000000000 --- a/src/servo/util/url.rs +++ /dev/null @@ -1,109 +0,0 @@ -export make_url, UrlMap, url_map; - -use std::net::url; -use std::net::url::Url; -use std::map::HashMap; -use path::Path; - -/** -Create a URL object from a string. Does various helpful browsery things like - -* If there's no current url and the path looks like a file then it will - create a file url based of the current working directory -* If there's a current url and the new path is relative then the new url - is based off the current url - -*/ -#[allow(non_implicitly_copyable_typarams)] -fn make_url(str_url: ~str, current_url: Option) -> Url { - let mut schm = url::get_scheme(str_url); - let str_url = if result::is_err(&schm) { - if current_url.is_none() { - // If all we have is a filename, assume it's a local relative file - // and build an absolute path with the cwd - ~"file://" + os::getcwd().push(str_url).to_str() - } else { - let current_url = current_url.get(); - #debug("make_url: current_url: %?", current_url); - if current_url.path.is_empty() || current_url.path.ends_with("/") { - current_url.scheme + "://" + current_url.host + "/" + str_url - } else { - let path = str::split_char(current_url.path, '/'); - let path = path.init(); - let path = str::connect(path + ~[move str_url], "/"); - - current_url.scheme + "://" + current_url.host + path - } - } - } else { - move str_url - }; - - // FIXME: Need to handle errors - url::from_str(str_url).get() -} - -mod make_url_tests { - - #[test] - fn should_create_absolute_file_url_if_current_url_is_none_and_str_url_looks_filey() { - let file = ~"local.html"; - let url = make_url(move file, None); - #debug("url: %?", url); - assert url.scheme == ~"file"; - assert url.path.contains(os::getcwd().to_str()); - } - - #[test] - fn should_create_url_based_on_old_url_1() { - let old_str = ~"http://example.com"; - let old_url = make_url(move old_str, None); - let new_str = ~"index.html"; - let new_url = make_url(move new_str, Some(move old_url)); - assert new_url.scheme == ~"http"; - assert new_url.host == ~"example.com"; - assert new_url.path == ~"/index.html"; - } - - #[test] - fn should_create_url_based_on_old_url_2() { - let old_str = ~"http://example.com/"; - let old_url = make_url(move old_str, None); - let new_str = ~"index.html"; - let new_url = make_url(move new_str, Some(move old_url)); - assert new_url.scheme == ~"http"; - assert new_url.host == ~"example.com"; - assert new_url.path == ~"/index.html"; - } - - #[test] - fn should_create_url_based_on_old_url_3() { - let old_str = ~"http://example.com/index.html"; - let old_url = make_url(move old_str, None); - let new_str = ~"crumpet.html"; - let new_url = make_url(move new_str, Some(move old_url)); - assert new_url.scheme == ~"http"; - assert new_url.host == ~"example.com"; - assert new_url.path == ~"/crumpet.html"; - } - - #[test] - fn should_create_url_based_on_old_url_4() { - let old_str = ~"http://example.com/snarf/index.html"; - let old_url = make_url(move old_str, None); - let new_str = ~"crumpet.html"; - let new_url = make_url(move new_str, Some(move old_url)); - assert new_url.scheme == ~"http"; - assert new_url.host == ~"example.com"; - assert new_url.path == ~"/snarf/crumpet.html"; - } - -} - -type UrlMap = HashMap; - -fn url_map() -> UrlMap { - use core::to_str::ToStr; - - HashMap::() -} diff --git a/src/servo/util/vec.rs b/src/servo/util/vec.rs deleted file mode 100644 index 1d476ab2420..00000000000 --- a/src/servo/util/vec.rs +++ /dev/null @@ -1,102 +0,0 @@ -use core::cmp::{Ord, Eq}; - -export BinarySearchMethods, binary_search, binary_search_index; - -trait BinarySearchMethods { - pure fn binary_search(&self, key: &T) -> Option<&self/T>; - pure fn binary_search_index(&self, key: &T) -> Option; -} - -impl &[T]: BinarySearchMethods { - pure fn binary_search(&self, key: &T) -> Option<&self/T> { - match self.binary_search_index(key) { - None => None, - Some(i) => Some(&self[i]) - } - } - - pure fn binary_search_index(&self, key: &T) -> Option { - if self.len() == 0 { - return None; - } - - let mut low : int = 0; - let mut high : int = (self.len() as int) - 1; - - while (low <= high) { - // http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html - let mid : int = (((low as uint) + (high as uint)) >> 1) as int; - let midv = &self[mid]; - - if (midv < key) { - low = mid + 1; - } else if (midv > key) { - high = mid - 1; - } else { - return Some(mid as uint); - } - } - return None; - } -} - -fn test_find_all_elems(arr: &[T]) { - let mut i = 0; - while i < arr.len() { - assert test_match(&arr[i], arr.binary_search(&arr[i])); - i += 1; - } -} - -fn test_miss_all_elems(arr: &[T], misses: &[T]) { - let mut i = 0; - while i < misses.len() { - let res = arr.binary_search(&misses[i]); - debug!("%? == %? ?", misses[i], res); - assert !test_match(&misses[i], arr.binary_search(&misses[i])); - i += 1; - } -} - -fn test_match(b: &T, a: Option<&T>) -> bool { - match a { - None => false, - Some(t) => t == b - } -} - -fn should_find_all_elements() { - #[test]; - - let arr_odd = [1, 2, 4, 6, 7, 8, 9]; - let arr_even = [1, 2, 5, 6, 7, 8, 9, 42]; - let arr_double = [1, 1, 2, 2, 6, 8, 22]; - let arr_one = [234986325]; - let arr_two = [3044, 8393]; - let arr_three = [12, 23, 34]; - - test_find_all_elems(arr_odd); - test_find_all_elems(arr_even); - test_find_all_elems(arr_double); - test_find_all_elems(arr_one); - test_find_all_elems(arr_two); - test_find_all_elems(arr_three); -} - -fn should_not_find_missing_elements() { - #[test]; - - let arr_odd = [1, 2, 4, 6, 7, 8, 9]; - let arr_even = [1, 2, 5, 6, 7, 8, 9, 42]; - let arr_double = [1, 1, 2, 2, 6, 8, 22]; - let arr_one = [234986325]; - let arr_two = [3044, 8393]; - let arr_three = [12, 23, 34]; - - test_miss_all_elems(arr_odd, [-22, 0, 3, 5, 34938, 10, 11, 12]); - test_miss_all_elems(arr_even, [-1, 0, 3, 34938, 10, 11, 12]); - test_miss_all_elems(arr_double, [-1, 0, 3, 4, 34938, 10, 11, 12, 234, 234, 33]); - test_miss_all_elems(arr_one, [-1, 0, 3, 34938, 10, 11, 12, 234, 234, 33]); - test_miss_all_elems(arr_two, [-1, 0, 3, 34938, 10, 11, 12, 234, 234, 33]); - test_miss_all_elems(arr_three, [-2, 0, 1, 2, 3, 34938, 10, 11, 234, 234, 33]); -}