diff options
29 files changed, 806 insertions, 893 deletions
diff --git a/src/components/compositing/constellation.rs b/src/components/compositing/constellation.rs index f6d12ebcedd..a243bf4f3ee 100644 --- a/src/components/compositing/constellation.rs +++ b/src/components/compositing/constellation.rs @@ -22,6 +22,7 @@ use servo_msg::constellation_msg::{NavigationType, PipelineId, RendererReadyMsg, use servo_msg::constellation_msg::{SubpageId, WindowSizeData}; use servo_msg::constellation_msg; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; +use gfx::font_cache_task::FontCacheTask; use servo_net::resource_task::ResourceTask; use servo_net::resource_task; use servo_util::geometry::PagePx; @@ -43,6 +44,7 @@ pub struct Constellation { pub resource_task: ResourceTask, pub image_cache_task: ImageCacheTask, pipelines: HashMap<PipelineId, Rc<Pipeline>>, + font_cache_task: FontCacheTask, navigation_context: NavigationContext, next_pipeline_id: PipelineId, pending_frames: Vec<FrameChange>, @@ -243,6 +245,7 @@ impl Constellation { opts: &Opts, resource_task: ResourceTask, image_cache_task: ImageCacheTask, + font_cache_task: FontCacheTask, time_profiler_chan: TimeProfilerChan) -> ConstellationChan { let (constellation_port, constellation_chan) = ConstellationChan::new(); @@ -255,6 +258,7 @@ impl Constellation { compositor_chan: compositor_chan, resource_task: resource_task, image_cache_task: image_cache_task, + font_cache_task: font_cache_task, pipelines: HashMap::new(), navigation_context: NavigationContext::new(), next_pipeline_id: PipelineId(0), @@ -368,6 +372,7 @@ impl Constellation { } self.image_cache_task.exit(); self.resource_task.send(resource_task::Exit); + self.font_cache_task.exit(); self.compositor_chan.send(ShutdownComplete); } @@ -422,6 +427,7 @@ impl Constellation { self.chan.clone(), self.compositor_chan.clone(), self.image_cache_task.clone(), + self.font_cache_task.clone(), self.resource_task.clone(), self.time_profiler_chan.clone(), self.window_size, @@ -449,6 +455,7 @@ impl Constellation { self.chan.clone(), self.compositor_chan.clone(), self.image_cache_task.clone(), + self.font_cache_task.clone(), self.resource_task.clone(), self.time_profiler_chan.clone(), self.window_size, @@ -575,6 +582,7 @@ impl Constellation { self.chan.clone(), self.compositor_chan.clone(), self.image_cache_task.clone(), + self.font_cache_task.clone(), self.time_profiler_chan.clone(), self.opts.clone(), source_pipeline.clone(), @@ -587,6 +595,7 @@ impl Constellation { self.chan.clone(), self.compositor_chan.clone(), self.image_cache_task.clone(), + self.font_cache_task.clone(), self.resource_task.clone(), self.time_profiler_chan.clone(), self.window_size, @@ -643,6 +652,7 @@ impl Constellation { self.chan.clone(), self.compositor_chan.clone(), self.image_cache_task.clone(), + self.font_cache_task.clone(), self.resource_task.clone(), self.time_profiler_chan.clone(), self.window_size, diff --git a/src/components/compositing/pipeline.rs b/src/components/compositing/pipeline.rs index cabf2984b15..dbd054feb55 100644 --- a/src/components/compositing/pipeline.rs +++ b/src/components/compositing/pipeline.rs @@ -14,6 +14,7 @@ use script::script_task; use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId}; use servo_msg::constellation_msg::WindowSizeData; use servo_net::image_cache_task::ImageCacheTask; +use gfx::font_cache_task::FontCacheTask; use servo_net::resource_task::ResourceTask; use servo_util::opts::Opts; use servo_util::time::TimeProfilerChan; @@ -49,6 +50,7 @@ impl Pipeline { constellation_chan: ConstellationChan, compositor_chan: CompositorChan, image_cache_task: ImageCacheTask, + font_cache_task: FontCacheTask, time_profiler_chan: TimeProfilerChan, opts: Opts, script_pipeline: Rc<Pipeline>, @@ -68,6 +70,7 @@ impl Pipeline { render_port, compositor_chan.clone(), constellation_chan.clone(), + font_cache_task.clone(), failure.clone(), opts.clone(), time_profiler_chan.clone(), @@ -81,6 +84,7 @@ impl Pipeline { script_pipeline.script_chan.clone(), render_chan.clone(), image_cache_task.clone(), + font_cache_task.clone(), opts.clone(), time_profiler_chan, layout_shutdown_chan); @@ -110,6 +114,7 @@ impl Pipeline { constellation_chan: ConstellationChan, compositor_chan: CompositorChan, image_cache_task: ImageCacheTask, + font_cache_task: FontCacheTask, resource_task: ResourceTask, time_profiler_chan: TimeProfilerChan, window_size: WindowSizeData, @@ -150,6 +155,7 @@ impl Pipeline { render_port, compositor_chan.clone(), constellation_chan.clone(), + font_cache_task.clone(), failure.clone(), opts.clone(), time_profiler_chan.clone(), @@ -163,6 +169,7 @@ impl Pipeline { script_chan.clone(), render_chan.clone(), image_cache_task, + font_cache_task, opts.clone(), time_profiler_chan, layout_shutdown_chan); diff --git a/src/components/gfx/display_list/mod.rs b/src/components/gfx/display_list/mod.rs index 15d39896051..355eab40223 100644 --- a/src/components/gfx/display_list/mod.rs +++ b/src/components/gfx/display_list/mod.rs @@ -32,6 +32,12 @@ use std::mem; use std::slice::Items; use style::computed_values::border_style; use sync::Arc; +use std::num::Zero; +use std::ptr; + +use azure::AzFloat; +use azure::scaled_font::ScaledFont; +use azure::azure_hl::ColorPattern; pub mod optimizer; @@ -52,6 +58,80 @@ impl OpaqueNode { } } +trait ScaledFontExtensionMethods { + fn draw_text_into_context(&self, + rctx: &RenderContext, + run: &Box<TextRun>, + range: &Range<CharIndex>, + baseline_origin: Point2D<Au>, + color: Color); +} + +impl ScaledFontExtensionMethods for ScaledFont { + fn draw_text_into_context(&self, + rctx: &RenderContext, + run: &Box<TextRun>, + range: &Range<CharIndex>, + baseline_origin: Point2D<Au>, + color: Color) { + use libc::types::common::c99::{uint16_t, uint32_t}; + use azure::{struct__AzDrawOptions, + struct__AzGlyph, + struct__AzGlyphBuffer, + struct__AzPoint}; + use azure::azure::{AzDrawTargetFillGlyphs}; + + let target = rctx.get_draw_target(); + let pattern = ColorPattern::new(color); + let azure_pattern = pattern.azure_color_pattern; + assert!(azure_pattern.is_not_null()); + + let options = struct__AzDrawOptions { + mAlpha: 1f64 as AzFloat, + fields: 0x0200 as uint16_t + }; + + let mut origin = baseline_origin.clone(); + let mut azglyphs = vec!(); + azglyphs.reserve(range.length().to_uint()); + + for (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) { + for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) { + let glyph_advance = glyph.advance(); + let glyph_offset = glyph.offset().unwrap_or(Zero::zero()); + + let azglyph = struct__AzGlyph { + mIndex: glyph.id() as uint32_t, + mPosition: struct__AzPoint { + x: (origin.x + glyph_offset.x).to_nearest_px() as AzFloat, + y: (origin.y + glyph_offset.y).to_nearest_px() as AzFloat + } + }; + origin = Point2D(origin.x + glyph_advance, origin.y); + azglyphs.push(azglyph) + }; + } + + let azglyph_buf_len = azglyphs.len(); + if azglyph_buf_len == 0 { return; } // Otherwise the Quartz backend will assert. + + let glyphbuf = struct__AzGlyphBuffer { + mGlyphs: azglyphs.as_ptr(), + mNumGlyphs: azglyph_buf_len as uint32_t + }; + + unsafe { + // TODO(Issue #64): this call needs to move into azure_hl.rs + AzDrawTargetFillGlyphs(target.azure_draw_target, + self.get_ref(), + &glyphbuf, + azure_pattern, + &options, + ptr::null()); + } + } +} + /// "Steps" as defined by CSS 2.1 § E.2. #[deriving(Clone, PartialEq)] pub enum StackingLevel { @@ -510,25 +590,27 @@ impl DisplayItem { // FIXME(pcwalton): Allocating? Why? let text_run = text.text_run.clone(); - let font = render_context.font_ctx.get_font_by_descriptor(&text_run.font_descriptor).unwrap(); - let font_metrics = { - font.borrow().metrics.clone() - }; + let font = render_context.font_ctx.get_render_font_from_template( + &text_run.font_template, + text_run.pt_size, + render_context.opts.render_backend); + let font = font.borrow(); + let origin = text.base.bounds.origin; - let baseline_origin = Point2D(origin.x, origin.y + font_metrics.ascent); + let baseline_origin = Point2D(origin.x, origin.y + text_run.font_metrics.ascent); { - font.borrow_mut().draw_text_into_context(render_context, - &*text.text_run, - &text.range, - baseline_origin, - text.text_color); + font.draw_text_into_context(render_context, + &*text.text_run, + &text.range, + baseline_origin, + text.text_color); } let width = text.base.bounds.size.width; - let underline_size = font_metrics.underline_size; - let underline_offset = font_metrics.underline_offset; - let strikeout_size = font_metrics.strikeout_size; - let strikeout_offset = font_metrics.strikeout_offset; + let underline_size = text_run.font_metrics.underline_size; + let underline_offset = text_run.font_metrics.underline_offset; + let strikeout_size = text_run.font_metrics.strikeout_size; + let strikeout_offset = text_run.font_metrics.strikeout_offset; for underline_color in text.text_decorations.underline.iter() { let underline_y = baseline_origin.y - underline_offset; diff --git a/src/components/gfx/font.rs b/src/components/gfx/font.rs index 98c356514eb..2630b018190 100644 --- a/src/components/gfx/font.rs +++ b/src/components/gfx/font.rs @@ -2,34 +2,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use azure::{AzFloat, AzScaledFontRef}; -use azure::azure_hl::{BackendType, ColorPattern}; -use azure::scaled_font::ScaledFont; use geom::{Point2D, Rect, Size2D}; use std::mem; -use std::num::Zero; -use std::ptr; use std::str; use std::rc::Rc; use std::cell::RefCell; use servo_util::cache::{Cache, HashCache}; -use servo_util::range::Range; use style::computed_values::{text_decoration, font_weight, font_style}; use sync::Arc; -use color::Color; -use font_context::FontContext; use servo_util::geometry::Au; use platform::font_context::FontContextHandle; use platform::font::{FontHandle, FontTable}; -use render_context::RenderContext; -use text::glyph::{CharIndex, GlyphStore, GlyphId}; +use text::glyph::{GlyphStore, GlyphId}; use text::shaping::ShaperMethods; use text::{Shaper, TextRun}; - -#[cfg(target_os="linux")] -#[cfg(target_os="android")] -use azure::scaled_font::NativeFont; +use font_template::FontTemplateDescriptor; +use platform::font_template::FontTemplateData; // FontHandle encapsulates access to the platform's font API, // e.g. quartz, FreeType. It provides access to metrics and tables @@ -37,11 +26,9 @@ use azure::scaled_font::NativeFont; // resources needed by the graphics layer to draw glyphs. pub trait FontHandleMethods { - fn new_from_buffer(fctx: &FontContextHandle, buf: Vec<u8>, style: &SpecifiedFontStyle) + fn new_from_template(fctx: &FontContextHandle, template: Arc<FontTemplateData>, pt_size: Option<f64>) -> Result<Self,()>; - - // an identifier usable by FontContextHandle to recreate this FontHandle. - fn face_identifier(&self) -> String; + fn get_template(&self) -> Arc<FontTemplateData>; fn family_name(&self) -> String; fn face_name(&self) -> String; fn is_italic(&self) -> bool; @@ -110,152 +97,30 @@ pub struct FontStyle { pub type SpecifiedFontStyle = FontStyle; pub type UsedFontStyle = FontStyle; -// 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. -#[deriving(Clone, PartialEq)] -pub struct FontDescriptor { - pub style: UsedFontStyle, - pub selector: FontSelector, -} - -impl FontDescriptor { - pub fn new(style: UsedFontStyle, selector: FontSelector) -> FontDescriptor { - FontDescriptor { - style: style, - selector: selector, - } - } -} - -// A FontSelector is a platform-specific strategy for serializing face names. -#[deriving(Clone, PartialEq)] -pub enum FontSelector { - SelectorPlatformIdentifier(String), -} - -// 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 { - pub families: Vec<String>, - // style of the first western font in group, which is - // used for purposes of calculating text run metrics. - pub style: UsedFontStyle, - pub fonts: Vec<Rc<RefCell<Font>>> -} - -impl FontGroup { - pub fn new(families: Vec<String>, style: &UsedFontStyle, fonts: Vec<Rc<RefCell<Font>>>) -> FontGroup { - FontGroup { - families: families, - style: (*style).clone(), - fonts: fonts, - } - } - - pub fn create_textrun(&self, text: String, decoration: text_decoration::T) -> TextRun { - assert!(self.fonts.len() > 0); - - // TODO(Issue #177): Actually fall back through the FontGroup when a font is unsuitable. - TextRun::new(&mut *self.fonts.get(0).borrow_mut(), text.clone(), decoration) - } -} - -pub struct RunMetrics { - // may be negative due to negative width (i.e., kerning of '.' in 'P.T.') - pub advance_width: Au, - pub ascent: Au, // nonzero - pub descent: Au, // nonzero - // this bounding box is relative to the left origin baseline. - // so, bounding_box.position.y = -ascent - pub bounding_box: Rect<Au> -} - -impl RunMetrics { - pub fn new(advance: Au, ascent: Au, descent: Au) -> RunMetrics { - let bounds = Rect(Point2D(Au(0), -ascent), - Size2D(advance, ascent + 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. - - RunMetrics { - advance_width: advance, - bounding_box: bounds, - ascent: ascent, - descent: descent, - } - } -} - -/** -A font instance. Layout can use this to calculate glyph metrics -and the renderer can use it to render text. -*/ pub struct Font { pub handle: FontHandle, - pub azure_font: Option<ScaledFont>, - pub shaper: Option<Shaper>, - pub style: UsedFontStyle, pub metrics: FontMetrics, - pub backend: BackendType, + pub descriptor: FontTemplateDescriptor, + pub pt_size: f64, + pub shaper: Option<Shaper>, pub shape_cache: HashCache<String, Arc<GlyphStore>>, pub glyph_advance_cache: HashCache<u32, FractionalPixel>, } -impl<'a> Font { - pub fn new_from_buffer(ctx: &FontContext, - buffer: Vec<u8>, - style: &SpecifiedFontStyle, - backend: BackendType) - -> Result<Rc<RefCell<Font>>, ()> { - let handle = FontHandleMethods::new_from_buffer(&ctx.handle, buffer, style); - let handle: FontHandle = match handle { - Ok(handle) => handle, - Err(()) => return Err(()), - }; - - let metrics = handle.get_metrics(); - - return Ok(Rc::new(RefCell::new(Font { - handle: handle, - azure_font: None, - shaper: None, - style: (*style).clone(), - metrics: metrics, - backend: backend, - shape_cache: HashCache::new(), - glyph_advance_cache: HashCache::new(), - }))); - } - - pub fn new_from_adopted_handle(_fctx: &FontContext, handle: FontHandle, - style: &SpecifiedFontStyle, backend: BackendType) - -> Font { - let metrics = handle.get_metrics(); +impl Font { + pub fn shape_text(&mut self, text: String, is_whitespace: bool) -> Arc<GlyphStore> { - Font { - handle: handle, - azure_font: None, - shaper: None, - style: (*style).clone(), - metrics: metrics, - backend: backend, - shape_cache: HashCache::new(), - glyph_advance_cache: HashCache::new(), - } + //FIXME (ksh8281) + self.make_shaper(); + let shaper = &self.shaper; + self.shape_cache.find_or_create(&text, |txt| { + let mut glyphs = GlyphStore::new(text.as_slice().char_len() as int, is_whitespace); + shaper.get_ref().shape_text(txt.as_slice(), &mut glyphs); + Arc::new(glyphs) + }) } - fn make_shaper(&'a mut self) -> &'a Shaper { + fn make_shaper<'a>(&'a mut self) -> &'a Shaper { // fast path: already created a shaper match self.shaper { Some(ref shaper) => { @@ -281,155 +146,64 @@ impl<'a> Font { return result; } - // TODO: this should return a borrowed pointer, but I can't figure - // out why borrowck doesn't like my implementation. - - fn get_azure_font(&mut self) -> AzScaledFontRef { - // fast path: we've already created the azure font resource - match self.azure_font { - Some(ref azfont) => return azfont.get_ref(), - None => {} - } - - let scaled_font = self.create_azure_font(); - self.azure_font = Some(scaled_font); - // try again. - return self.get_azure_font(); - } - - #[cfg(target_os="macos")] - fn create_azure_font(&mut self) -> ScaledFont { - let cg_font = self.handle.get_CGFont(); - let size = self.style.pt_size as AzFloat; - ScaledFont::new(self.backend, &cg_font, size) + pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { + self.handle.glyph_index(codepoint) } - #[cfg(target_os="linux")] - #[cfg(target_os="android")] - fn create_azure_font(&self) -> ScaledFont { - let freetype_font = self.handle.face; - let size = self.style.pt_size as AzFloat; - ScaledFont::new(self.backend, NativeFont(freetype_font), size) + pub fn glyph_h_advance(&mut self, glyph: GlyphId) -> FractionalPixel { + let handle = &self.handle; + self.glyph_advance_cache.find_or_create(&glyph, |glyph| { + match handle.glyph_h_advance(*glyph) { + Some(adv) => adv, + None => 10f64 as FractionalPixel // FIXME: Need fallback strategy + } + }) } } +pub struct FontGroup { + pub fonts: Vec<Rc<RefCell<Font>>>, +} -impl Font { - pub fn draw_text_into_context(&mut self, - rctx: &RenderContext, - run: &Box<TextRun>, - range: &Range<CharIndex>, - baseline_origin: Point2D<Au>, - color: Color) { - use libc::types::common::c99::{uint16_t, uint32_t}; - use azure::{struct__AzDrawOptions, - struct__AzGlyph, - struct__AzGlyphBuffer, - struct__AzPoint}; - use azure::azure::{AzDrawTargetFillGlyphs}; - - let target = rctx.get_draw_target(); - let azfontref = self.get_azure_font(); - let pattern = ColorPattern::new(color); - let azure_pattern = pattern.azure_color_pattern; - assert!(azure_pattern.is_not_null()); - - let options = struct__AzDrawOptions { - mAlpha: 1f64 as AzFloat, - fields: 0x0200 as uint16_t - }; - - let mut origin = baseline_origin.clone(); - let mut azglyphs = vec!(); - azglyphs.reserve(range.length().to_uint()); - - for (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) { - for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) { - let glyph_advance = glyph.advance(); - let glyph_offset = glyph.offset().unwrap_or(Zero::zero()); - - let azglyph = struct__AzGlyph { - mIndex: glyph.id() as uint32_t, - mPosition: struct__AzPoint { - x: (origin.x + glyph_offset.x).to_nearest_px() as AzFloat, - y: (origin.y + glyph_offset.y).to_nearest_px() as AzFloat - } - }; - origin = Point2D(origin.x + glyph_advance, origin.y); - azglyphs.push(azglyph) - }; - } - - let azglyph_buf_len = azglyphs.len(); - if azglyph_buf_len == 0 { return; } // Otherwise the Quartz backend will assert. - - let glyphbuf = struct__AzGlyphBuffer { - mGlyphs: azglyphs.as_ptr(), - mNumGlyphs: azglyph_buf_len as uint32_t - }; - - unsafe { - // TODO(Issue #64): this call needs to move into azure_hl.rs - AzDrawTargetFillGlyphs(target.azure_draw_target, - azfontref, - &glyphbuf, - azure_pattern, - &options, - ptr::null()); +impl FontGroup { + pub fn new(fonts: Vec<Rc<RefCell<Font>>>) -> FontGroup { + FontGroup { + fonts: fonts } } - pub fn measure_text(&self, run: &TextRun, range: &Range<CharIndex>) -> RunMetrics { - // TODO(Issue #199): 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 (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) { - for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) { - advance = advance + glyph.advance(); - } - } - RunMetrics::new(advance, self.metrics.ascent, self.metrics.descent) - } + pub fn create_textrun(&self, text: String, decoration: text_decoration::T) -> TextRun { + assert!(self.fonts.len() > 0); - pub fn measure_text_for_slice(&self, - glyphs: &GlyphStore, - slice_range: &Range<CharIndex>) - -> RunMetrics { - let mut advance = Au(0); - for (_i, glyph) in glyphs.iter_glyphs_for_char_range(slice_range) { - advance = advance + glyph.advance(); - } - RunMetrics::new(advance, self.metrics.ascent, self.metrics.descent) + // TODO(Issue #177): Actually fall back through the FontGroup when a font is unsuitable. + TextRun::new(&mut *self.fonts.get(0).borrow_mut(), text.clone(), decoration) } +} - pub fn shape_text(&mut self, text: String, is_whitespace: bool) -> Arc<GlyphStore> { - - //FIXME (ksh8281) - self.make_shaper(); - let shaper = &self.shaper; - self.shape_cache.find_or_create(&text, |txt| { - let mut glyphs = GlyphStore::new(text.as_slice().char_len() as int, is_whitespace); - shaper.get_ref().shape_text(txt.as_slice(), &mut glyphs); - Arc::new(glyphs) - }) - } +pub struct RunMetrics { + // may be negative due to negative width (i.e., kerning of '.' in 'P.T.') + pub advance_width: Au, + pub ascent: Au, // nonzero + pub descent: Au, // nonzero + // this bounding box is relative to the left origin baseline. + // so, bounding_box.position.y = -ascent + pub bounding_box: Rect<Au> +} - pub fn get_descriptor(&self) -> FontDescriptor { - FontDescriptor::new(self.style.clone(), SelectorPlatformIdentifier(self.handle.face_identifier())) - } +impl RunMetrics { + pub fn new(advance: Au, ascent: Au, descent: Au) -> RunMetrics { + let bounds = Rect(Point2D(Au(0), -ascent), + Size2D(advance, ascent + descent)); - pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { - self.handle.glyph_index(codepoint) - } + // 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. - pub fn glyph_h_advance(&mut self, glyph: GlyphId) -> FractionalPixel { - let handle = &self.handle; - self.glyph_advance_cache.find_or_create(&glyph, |glyph| { - match handle.glyph_h_advance(*glyph) { - Some(adv) => adv, - None => /* FIXME: Need fallback strategy */ 10f64 as FractionalPixel - } - }) + RunMetrics { + advance_width: advance, + bounding_box: bounds, + ascent: ascent, + descent: descent, + } } } - diff --git a/src/components/gfx/font_cache_task.rs b/src/components/gfx/font_cache_task.rs new file mode 100644 index 00000000000..55e2dad003e --- /dev/null +++ b/src/components/gfx/font_cache_task.rs @@ -0,0 +1,213 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use platform::font_list::get_available_families; +use platform::font_list::get_variations_for_family; +use platform::font_list::get_last_resort_font_families; +use platform::font_context::FontContextHandle; + +use std::collections::HashMap; +use sync::Arc; +use font_template::{FontTemplate, FontTemplateDescriptor}; +use platform::font_template::FontTemplateData; + +/// A list of font templates that make up a given font family. +struct FontFamily { + templates: Vec<FontTemplate>, +} + +impl FontFamily { + fn new() -> FontFamily { + FontFamily { + templates: vec!(), + } + } + + /// Find a font in this family that matches a given desriptor. + fn find_font_for_style<'a>(&'a mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle) + -> Option<Arc<FontTemplateData>> { + // 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 template in self.templates.mut_iter() { + let maybe_template = template.get_if_matches(fctx, desc); + if maybe_template.is_some() { + return maybe_template; + } + } + + None + } +} + +/// Commands that the FontContext sends to the font cache task. +pub enum Command { + GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>), + Exit(Sender<()>), +} + +/// Reply messages sent from the font cache task to the FontContext caller. +pub enum Reply { + GetFontTemplateReply(Arc<FontTemplateData>), +} + +/// The font cache task itself. It maintains a list of reference counted +/// font templates that are currently in use. +struct FontCache { + port: Receiver<Command>, + generic_fonts: HashMap<String, String>, + local_families: HashMap<String, FontFamily>, + font_context: FontContextHandle, +} + +impl FontCache { + fn run(&mut self) { + loop { + let msg = self.port.recv(); + + match msg { + GetFontTemplate(family, descriptor, result) => { + let maybe_font_template = self.get_font_template(&family, &descriptor); + + let font_template = match maybe_font_template { + Some(font_template) => font_template, + None => self.get_last_resort_template(&descriptor), + }; + + result.send(GetFontTemplateReply(font_template)); + } + Exit(result) => { + result.send(()); + break; + } + } + } + } + + fn refresh_local_families(&mut self) { + self.local_families.clear(); + get_available_families(|family_name| { + if !self.local_families.contains_key(&family_name) { + let family = FontFamily::new(); + self.local_families.insert(family_name, family); + } + }); + } + + fn transform_family(&self, family: &String) -> String { + match self.generic_fonts.find(family) { + None => family.to_string(), + Some(mapped_family) => (*mapped_family).clone() + } + } + + fn find_font_in_family<'a>(&'a mut self, + family_name: &String, + desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> { + // TODO(Issue #188): look up localized font family names if canonical name not found + // look up canonical name + if self.local_families.contains_key(family_name) { + debug!("FontList: Found font family with name={:s}", family_name.to_str()); + let s = self.local_families.get_mut(family_name); + + if s.templates.len() == 0 { + get_variations_for_family(family_name.as_slice(), |path| { + let template = FontTemplate::new(path.as_slice()); + s.templates.push(template); + }); + } + + // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'. + // if such family exists, try to match style to a font + let result = s.find_font_for_style(desc, &self.font_context); + if result.is_some() { + return result; + } + + None + } else { + debug!("FontList: Couldn't find font family with name={:s}", family_name.to_str()); + None + } + } + + fn get_font_template(&mut self, family: &String, desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> { + let transformed_family_name = self.transform_family(family); + self.find_font_in_family(&transformed_family_name, desc) + } + + fn get_last_resort_template(&mut self, desc: &FontTemplateDescriptor) -> Arc<FontTemplateData> { + let last_resort = get_last_resort_font_families(); + + for family in last_resort.iter() { + let maybe_font_in_family = self.find_font_in_family(family, desc); + if maybe_font_in_family.is_some() { + return maybe_font_in_family.unwrap(); + } + } + + fail!("Unable to find any fonts that match (do you have fallback fonts installed?)"); + } +} + +/// The public interface to the font cache task, used exclusively by +/// the per-thread/task FontContext structures. +#[deriving(Clone)] +pub struct FontCacheTask { + chan: Sender<Command>, +} + +impl FontCacheTask { + pub fn new() -> FontCacheTask { + let (chan, port) = channel(); + + spawn(proc() { + // TODO: Allow users to specify these. + let mut generic_fonts = HashMap::with_capacity(5); + generic_fonts.insert("serif".to_string(), "Times New Roman".to_string()); + generic_fonts.insert("sans-serif".to_string(), "Arial".to_string()); + generic_fonts.insert("cursive".to_string(), "Apple Chancery".to_string()); + generic_fonts.insert("fantasy".to_string(), "Papyrus".to_string()); + generic_fonts.insert("monospace".to_string(), "Menlo".to_string()); + + let mut cache = FontCache { + port: port, + generic_fonts: generic_fonts, + local_families: HashMap::new(), + font_context: FontContextHandle::new(), + }; + + cache.refresh_local_families(); + cache.run(); + }); + + FontCacheTask { + chan: chan, + } + } + + pub fn get_font_template(&mut self, family: String, desc: FontTemplateDescriptor) + -> Arc<FontTemplateData> { + + let (response_chan, response_port) = channel(); + self.chan.send(GetFontTemplate(family, desc, response_chan)); + + let reply = response_port.recv(); + + match reply { + GetFontTemplateReply(data) => { + data + } + } + } + + pub fn exit(&self) { + let (response_chan, response_port) = channel(); + self.chan.send(Exit(response_chan)); + response_port.recv(); + } +} diff --git a/src/components/gfx/font_context.rs b/src/components/gfx/font_context.rs index 9437e44299f..a9cccae61fb 100644 --- a/src/components/gfx/font_context.rs +++ b/src/components/gfx/font_context.rs @@ -2,223 +2,147 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, SelectorPlatformIdentifier}; -use font::{SpecifiedFontStyle, UsedFontStyle}; -use font_list::FontList; -use platform::font::FontHandle; +use font::{Font, FontGroup}; +use font::SpecifiedFontStyle; use platform::font_context::FontContextHandle; +use style::computed_values::font_style; -use azure::azure_hl::BackendType; -use std::collections::hashmap::HashMap; -use servo_util::cache::{Cache, LRUCache}; -use servo_util::time::TimeProfilerChan; +use font_cache_task::FontCacheTask; +use font_template::FontTemplateDescriptor; +use platform::font_template::FontTemplateData; +use font::FontHandleMethods; +use platform::font::FontHandle; +use servo_util::cache::HashCache; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use std::cell::RefCell; +use sync::Arc; -/// Information needed to create a font context. -#[deriving(Clone)] -pub struct FontContextInfo { - /// The painting backend we're using. - pub backend: BackendType, +use azure::AzFloat; +use azure::azure_hl::BackendType; +use azure::scaled_font::ScaledFont; + +#[cfg(target_os="linux")] +#[cfg(target_os="android")] +use azure::scaled_font::FontData; - /// Whether we need a font list. - pub needs_font_list: bool, +#[cfg(target_os="linux")] +#[cfg(target_os="android")] +fn create_scaled_font(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont { + ScaledFont::new(backend, FontData(&template.bytes), pt_size as AzFloat) +} - /// A channel up to the time profiler. - pub time_profiler_chan: TimeProfilerChan, +#[cfg(target_os="macos")] +fn create_scaled_font(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont { + let cgfont = template.ctfont.copy_to_CGFont(); + ScaledFont::new(backend, &cgfont, pt_size as AzFloat) } -pub trait FontContextHandleMethods { - fn create_font_from_identifier(&self, &str, Option<&UsedFontStyle>) -> Result<FontHandle, ()>; +/// A cached azure font (per render task) that +/// can be shared by multiple text runs. +struct RenderFontCacheEntry { + pt_size: f64, + identifier: String, + font: Rc<RefCell<ScaledFont>>, } +/// The FontContext represents the per-thread/task state necessary for +/// working with fonts. It is the public API used by the layout and +/// render code. It talks directly to the font cache task where +/// required. pub struct FontContext { - pub instance_cache: LRUCache<FontDescriptor, Rc<RefCell<Font>>>, - pub font_list: Option<FontList>, // only needed by layout - pub group_cache: LRUCache<SpecifiedFontStyle, Rc<RefCell<FontGroup>>>, - pub handle: FontContextHandle, - pub backend: BackendType, - pub generic_fonts: HashMap<String,String>, - pub time_profiler_chan: TimeProfilerChan, + platform_handle: FontContextHandle, + font_cache_task: FontCacheTask, + + /// Weak reference as the layout FontContext is persistent. + layout_font_cache: Vec<Weak<RefCell<Font>>>, + + /// Strong reference as the render FontContext is (for now) recycled + /// per frame. TODO: Make this weak when incremental redraw is done. + render_font_cache: Vec<RenderFontCacheEntry>, } impl FontContext { - pub fn new(info: FontContextInfo) -> FontContext { + pub fn new(font_cache_task: FontCacheTask) -> FontContext { let handle = FontContextHandle::new(); - let font_list = if info.needs_font_list { - Some(FontList::new(&handle, info.time_profiler_chan.clone())) - } else { - None - }; - - // TODO: Allow users to specify these. - let mut generic_fonts = HashMap::with_capacity(5); - generic_fonts.insert("serif".to_string(), "Times New Roman".to_string()); - generic_fonts.insert("sans-serif".to_string(), "Arial".to_string()); - generic_fonts.insert("cursive".to_string(), "Apple Chancery".to_string()); - generic_fonts.insert("fantasy".to_string(), "Papyrus".to_string()); - generic_fonts.insert("monospace".to_string(), "Menlo".to_string()); - FontContext { - instance_cache: LRUCache::new(10), - font_list: font_list, - group_cache: LRUCache::new(10), - handle: handle, - backend: info.backend, - generic_fonts: generic_fonts, - time_profiler_chan: info.time_profiler_chan.clone(), + platform_handle: handle, + font_cache_task: font_cache_task, + layout_font_cache: vec!(), + render_font_cache: vec!(), } } - pub fn get_resolved_font_for_style(&mut self, style: &SpecifiedFontStyle) - -> Rc<RefCell<FontGroup>> { - match self.group_cache.find(style) { - Some(fg) => { - debug!("font group cache hit"); - fg - }, - None => { - debug!("font group cache miss"); - let fg = self.create_font_group(style); - self.group_cache.insert(style.clone(), fg.clone()); - fg - } - } - } + /// Create a font for use in layout calculations. + fn create_layout_font(&self, template: Arc<FontTemplateData>, + descriptor: FontTemplateDescriptor, pt_size: f64) -> Font { - pub fn get_font_by_descriptor(&mut self, desc: &FontDescriptor) - -> Result<Rc<RefCell<Font>>, ()> { - match self.instance_cache.find(desc) { - Some(f) => { - debug!("font cache hit"); - Ok(f) - }, - None => { - debug!("font cache miss"); - let result = self.create_font_instance(desc); - match result.clone() { - Ok(ref font) => { - self.instance_cache.insert(desc.clone(), font.clone()); - }, _ => {} - }; - result - } - } - } + let handle: FontHandle = FontHandleMethods::new_from_template(&self.platform_handle, template, Some(pt_size)).unwrap(); + let metrics = handle.get_metrics(); - fn transform_family(&self, family: &String) -> String { - debug!("(transform family) searching for `{:s}`", family.as_slice()); - match self.generic_fonts.find(family) { - None => family.to_string(), - Some(mapped_family) => (*mapped_family).clone() + Font { + handle: handle, + shaper: None, + descriptor: descriptor, + pt_size: pt_size, + metrics: metrics, + shape_cache: HashCache::new(), + glyph_advance_cache: HashCache::new(), } } - fn create_font_group(&mut self, style: &SpecifiedFontStyle) -> Rc<RefCell<FontGroup>> { - let mut fonts = vec!(); + /// Create a group of fonts for use in layout calculations. May return + /// a cached font if this font instance has already been used by + /// this context. + pub fn get_layout_font_group_for_style(&mut self, style: &SpecifiedFontStyle) -> FontGroup { + // Remove all weak pointers that have been dropped. + self.layout_font_cache.retain(|maybe_font| { + maybe_font.upgrade().is_some() + }); - debug!("(create font group) --- starting ---"); + let mut fonts: Vec<Rc<RefCell<Font>>> = vec!(); - // TODO(Issue #193): make iteration over 'font-family' more robust. for family in style.families.iter() { - let transformed_family_name = self.transform_family(family); - debug!("(create font group) transformed family is `{:s}`", transformed_family_name); - let mut found = false; - - let result = match self.font_list { - Some(ref mut fl) => { - let font_in_family = fl.find_font_in_family(&self.handle, &transformed_family_name, style); - match font_in_family { - Some(font_entry) => { - let font_id = - SelectorPlatformIdentifier(font_entry.handle.face_identifier()); - let font_desc = FontDescriptor::new((*style).clone(), font_id); - Some(font_desc) - }, - None => { - None - } - } + let desc = FontTemplateDescriptor::new(style.weight, style.style == font_style::italic); + + // GWTODO: Check on real pages if this is faster as Vec() or HashMap(). + let mut cache_hit = false; + for maybe_cached_font in self.layout_font_cache.iter() { + let cached_font = maybe_cached_font.upgrade().unwrap(); + if cached_font.borrow().descriptor == desc { + fonts.push(cached_font.clone()); + cache_hit = true; + break; } - None => None, - }; - - match result { - Some(ref result) => { - found = true; - let instance = self.get_font_by_descriptor(result); - let _ = instance.map(|font| fonts.push(font.clone())); - }, - _ => {} } - if !found { - debug!("(create font group) didn't find `{:s}`", transformed_family_name); + if !cache_hit { + let font_template = self.font_cache_task.get_font_template(family.clone(), desc.clone()); + let layout_font = Rc::new(RefCell::new(self.create_layout_font(font_template, desc.clone(), style.pt_size))); + self.layout_font_cache.push(layout_font.downgrade()); + fonts.push(layout_font); } } - if fonts.len() == 0 { - let last_resort = FontList::get_last_resort_font_families(); - for family in last_resort.iter() { - let font_desc = match self.font_list { - Some(ref mut font_list) => { - let font_desc = { - let font_entry = font_list.find_font_in_family(&self.handle, family, style); - match font_entry { - Some(v) => { - let font_id = - SelectorPlatformIdentifier(v.handle.face_identifier()); - Some(FontDescriptor::new((*style).clone(), font_id)) - }, - None => { - None - } - } - }; - font_desc - }, - None => { - None - } - }; - - match font_desc { - Some(ref fd) => { - let instance = self.get_font_by_descriptor(fd); - let _ = instance.map(|font| fonts.push(font.clone())); - }, - None => { } - }; - } - } - assert!(fonts.len() > 0, "No matching font(s), are the appropriate fonts installed?"); - // TODO(Issue #179): Split FontStyle into specified and used styles - let used_style = (*style).clone(); - - debug!("(create font group) --- finished ---"); - - Rc::new( - RefCell::new( - FontGroup::new(style.families.clone(), &used_style, fonts))) + FontGroup::new(fonts) } - fn create_font_instance(&self, desc: &FontDescriptor) -> Result<Rc<RefCell<Font>>, ()> { - return match &desc.selector { - // TODO(Issue #174): implement by-platform-name font selectors. - &SelectorPlatformIdentifier(ref identifier) => { - let result_handle = self.handle.create_font_from_identifier(identifier.as_slice(), - Some(&desc.style)); - result_handle.and_then(|handle| { - Ok( - Rc::new( - RefCell::new( - Font::new_from_adopted_handle(self, - handle, - &desc.style, - self.backend)))) - }) + /// Create a render font for use with azure. May return a cached + /// reference if already used by this font context. + pub fn get_render_font_from_template(&mut self, template: &Arc<FontTemplateData>, pt_size: f64, backend: BackendType) -> Rc<RefCell<ScaledFont>> { + for cached_font in self.render_font_cache.iter() { + if cached_font.pt_size == pt_size && + cached_font.identifier == template.identifier { + return cached_font.font.clone(); } - }; + } + + let render_font = Rc::new(RefCell::new(create_scaled_font(backend, template, pt_size))); + self.render_font_cache.push(RenderFontCacheEntry{ + font: render_font.clone(), + pt_size: pt_size, + identifier: template.identifier.clone(), + }); + render_font } } diff --git a/src/components/gfx/font_list.rs b/src/components/gfx/font_list.rs deleted file mode 100644 index 3180e5e710c..00000000000 --- a/src/components/gfx/font_list.rs +++ /dev/null @@ -1,162 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use std::collections::hashmap::HashMap; -use font::SpecifiedFontStyle; -use font_context::FontContextHandleMethods; -use gfx_font::FontHandleMethods; -use platform::font::FontHandle; -use platform::font_context::FontContextHandle; -use platform::font_list; -use style::computed_values::{font_weight, font_style}; - -use servo_util::time::{TimeProfilerChan, profile}; -use servo_util::time; - -pub type FontFamilyMap = HashMap<String, FontFamily>; - -/// The platform-independent font list abstraction. -pub struct FontList { - family_map: FontFamilyMap, - time_profiler_chan: TimeProfilerChan, -} - -impl FontList { - pub fn new(fctx: &FontContextHandle, - time_profiler_chan: TimeProfilerChan) - -> FontList { - let mut list = FontList { - family_map: HashMap::new(), - time_profiler_chan: time_profiler_chan.clone(), - }; - list.refresh(fctx); - list - } - - fn refresh(&mut self, _: &FontContextHandle) { - // TODO(Issue #186): don't refresh unless something actually - // changed. Does OSX have a notification for this event? - // - // Should font families with entries be invalidated/refreshed too? - profile(time::GfxRegenAvailableFontsCategory, self.time_profiler_chan.clone(), || { - self.family_map.clear(); - font_list::get_available_families(|family_name| { - debug!("Creating new FontFamily for family: {:s}", family_name); - let new_family = FontFamily::new(family_name.as_slice()); - self.family_map.insert(family_name, new_family); - }); - }); - } - - pub fn find_font_in_family<'a>(&'a mut self, fctx: &FontContextHandle, - family_name: &String, - style: &SpecifiedFontStyle) -> Option<&'a FontEntry> { - // TODO(Issue #188): look up localized font family names if canonical name not found - // look up canonical name - if self.family_map.contains_key(family_name) { - //FIXME call twice!(ksh8281) - debug!("FontList: Found font family with name={:s}", family_name.to_str()); - let s: &'a mut FontFamily = self.family_map.get_mut(family_name); - // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'. - // if such family exists, try to match style to a font - let result = s.find_font_for_style(fctx, style); - if result.is_some() { - return result; - } - - None - } else { - debug!("FontList: Couldn't find font family with name={:s}", family_name.to_str()); - None - } - } - - pub fn get_last_resort_font_families() -> Vec<String> { - font_list::get_last_resort_font_families() - } -} - -// Holds a specific font family, and the various -pub struct FontFamily { - pub family_name: String, - pub entries: Vec<FontEntry>, -} - -impl FontFamily { - pub fn new(family_name: &str) -> FontFamily { - FontFamily { - family_name: family_name.to_str(), - entries: vec!(), - } - } - - fn load_family_variations(&mut self, fctx: &FontContextHandle) { - if self.entries.len() > 0 { - return - } - let mut entries = vec!(); - font_list::load_variations_for_family(self.family_name.as_slice(), |file_path| { - let font_handle = fctx.create_font_from_identifier(file_path.as_slice(), None).unwrap(); - debug!("Creating new FontEntry for face: {:s}", font_handle.face_name()); - let entry = FontEntry::new(font_handle); - entries.push(entry); - }); - self.entries = entries; - assert!(self.entries.len() > 0) - } - - pub fn find_font_for_style<'a>(&'a mut self, fctx: &FontContextHandle, style: &SpecifiedFontStyle) - -> Option<&'a FontEntry> { - self.load_family_variations(fctx); - - // 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 entry in self.entries.iter() { - if (style.weight.is_bold() == entry.is_bold()) && - ((style.style == font_style::italic) == entry.is_italic()) { - - return Some(entry); - } - } - - 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. -pub struct FontEntry { - pub face_name: String, - weight: font_weight::T, - italic: bool, - pub handle: FontHandle, - // TODO: array of OpenType features, etc. -} - -impl FontEntry { - pub fn new(handle: FontHandle) -> FontEntry { - FontEntry { - face_name: handle.face_name(), - weight: handle.boldness(), - italic: handle.is_italic(), - handle: handle - } - } - - pub fn is_bold(&self) -> bool { - self.weight.is_bold() - } - - pub fn is_italic(&self) -> bool { - self.italic - } -} - diff --git a/src/components/gfx/font_template.rs b/src/components/gfx/font_template.rs new file mode 100644 index 00000000000..3ca9aff4e45 --- /dev/null +++ b/src/components/gfx/font_template.rs @@ -0,0 +1,115 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use style::computed_values::font_weight; +use platform::font_context::FontContextHandle; +use platform::font::FontHandle; +use platform::font_template::FontTemplateData; + +use sync::{Arc, Weak}; +use font::FontHandleMethods; + +/// Describes how to select a font from a given family. +/// This is very basic at the moment and needs to be +/// expanded or refactored when we support more of the +/// font styling parameters. +#[deriving(Clone)] +pub struct FontTemplateDescriptor { + pub weight: font_weight::T, + pub italic: bool, +} + +impl FontTemplateDescriptor { + pub fn new(weight: font_weight::T, italic: bool) -> FontTemplateDescriptor { + FontTemplateDescriptor { + weight: weight, + italic: italic, + } + } +} + +impl PartialEq for FontTemplateDescriptor { + fn eq(&self, other: &FontTemplateDescriptor) -> bool { + self.weight.is_bold() == other.weight.is_bold() && + self.italic == other.italic + } +} + +/// This describes all the information needed to create +/// font instance handles. It contains a unique that is +/// platform specific. +pub struct FontTemplate { + identifier: String, + descriptor: Option<FontTemplateDescriptor>, + data: Option<Weak<FontTemplateData>>, +} + +/// Holds all of the template information for a font that +/// is common, regardless of the number of instances of +/// this font handle per thread. +impl FontTemplate { + pub fn new(identifier: &str) -> FontTemplate { + FontTemplate { + identifier: identifier.to_string(), + descriptor: None, + data: None, + } + } + + /// Get the data for creating a font if it matches a given descriptor. + pub fn get_if_matches(&mut self, fctx: &FontContextHandle, + requested_desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> { + // The font template data can be unloaded when nothing is referencing + // it (via the Weak reference to the Arc above). However, if we have + // already loaded a font, store the style information about it separately, + // so that we can do font matching against it again in the future + // without having to reload the font (unless it is an actual match). + match self.descriptor { + Some(actual_desc) => { + if *requested_desc == actual_desc { + Some(self.get_data()) + } else { + None + } + }, + None => { + let data = self.get_data(); + let handle = FontHandleMethods::new_from_template(fctx, data.clone(), None); + let handle: FontHandle = match handle { + Ok(handle) => handle, + Err(()) => fail!("TODO - Handle failure to create a font from template."), + }; + let actual_desc = FontTemplateDescriptor::new(handle.boldness(), + handle.is_italic()); + let desc_match = actual_desc == *requested_desc; + + self.descriptor = Some(actual_desc); + if desc_match { + Some(data) + } else { + None + } + } + } + } + + /// Get the font template data. If any strong references still + /// exist, it will return a clone, otherwise it will load the + /// font data and store a weak reference to it internally. + pub fn get_data(&mut self) -> Arc<FontTemplateData> { + let maybe_data = match self.data { + Some(ref data) => data.upgrade(), + None => None, + }; + + match maybe_data { + Some(data) => data, + None => { + let template_data = Arc::new(FontTemplateData::new(self.identifier.as_slice())); + self.data = Some(template_data.downgrade()); + template_data + } + } + } +}
\ No newline at end of file diff --git a/src/components/gfx/gfx.rs b/src/components/gfx/gfx.rs index dd7d58c7616..4a7c4e217b2 100644 --- a/src/components/gfx/gfx.rs +++ b/src/components/gfx/gfx.rs @@ -45,12 +45,6 @@ extern crate harfbuzz; #[cfg(target_os="macos")] extern crate core_graphics; #[cfg(target_os="macos")] extern crate core_text; -pub use gfx_font = font; -pub use gfx_font_context = font_context; -pub use gfx_font_list = font_list; -pub use servo_gfx_font = font; -pub use servo_gfx_font_list = font_list; - pub use render_context::RenderContext; // Private rendering modules @@ -65,7 +59,8 @@ pub mod render_task; // Fonts pub mod font; pub mod font_context; -pub mod font_list; +pub mod font_cache_task; +pub mod font_template; // Misc. mod buffer_map; diff --git a/src/components/gfx/platform/android/font.rs b/src/components/gfx/platform/android/font.rs index e61d092ae07..3e060c0fb58 100644 --- a/src/components/gfx/platform/android/font.rs +++ b/src/components/gfx/platform/android/font.rs @@ -5,17 +5,18 @@ extern crate freetype; use font::{FontHandleMethods, FontMetrics, FontTableMethods}; -use font::{FontTableTag, FractionalPixel, SpecifiedFontStyle}; +use font::{FontTableTag, FractionalPixel}; use servo_util::geometry::Au; use servo_util::geometry; use platform::font_context::FontContextHandle; use text::glyph::GlyphId; use text::util::{float_to_fixed, fixed_to_float}; use style::computed_values::font_weight; +use platform::font_template::FontTemplateData; use freetype::freetype::{FT_Get_Char_Index, FT_Get_Postscript_Name}; use freetype::freetype::{FT_Load_Glyph, FT_Set_Char_Size}; -use freetype::freetype::{FT_New_Face, FT_Get_Sfnt_Table}; +use freetype::freetype::{FT_Get_Sfnt_Table}; use freetype::freetype::{FT_New_Memory_Face, FT_Done_Face}; use freetype::freetype::{FTErrorMethods, FT_F26Dot6, FT_Face, FT_FaceRec}; use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong}; @@ -28,6 +29,8 @@ use std::mem; use std::ptr; use std::str; +use sync::Arc; + fn float_to_fixed_ft(f: f64) -> i32 { float_to_fixed(6, f) } @@ -37,7 +40,7 @@ fn fixed_to_float_ft(f: i32) -> f64 { } pub struct FontTable { - bogus: () + _bogus: () } impl FontTableMethods for FontTable { @@ -46,15 +49,10 @@ impl FontTableMethods for FontTable { } } -pub enum FontSource { - FontSourceMem(Vec<u8>), - FontSourceFile(String) -} - pub struct FontHandle { // The font binary. This must stay valid for the lifetime of the font, // if the font is created using FT_Memory_Face. - pub source: FontSource, + pub font_data: Arc<FontTemplateData>, pub face: FT_Face, pub handle: FontContextHandle } @@ -72,14 +70,15 @@ impl Drop for FontHandle { } impl FontHandleMethods for FontHandle { - fn new_from_buffer(fctx: &FontContextHandle, - buf: Vec<u8>, - style: &SpecifiedFontStyle) + fn new_from_template(fctx: &FontContextHandle, + template: Arc<FontTemplateData>, + pt_size: Option<f64>) -> Result<FontHandle, ()> { let ft_ctx: FT_Library = fctx.ctx.ctx; if ft_ctx.is_null() { return Err(()); } - let face_result = create_face_from_buffer(ft_ctx, buf.as_ptr(), buf.len(), style.pt_size); + let bytes = &template.deref().bytes; + let face_result = create_face_from_buffer(ft_ctx, bytes.as_ptr(), bytes.len(), pt_size); // TODO: this could be more simply written as result::chain // and moving buf into the struct ctor, but cant' move out of @@ -88,7 +87,7 @@ impl FontHandleMethods for FontHandle { Ok(face) => { let handle = FontHandle { face: face, - source: FontSourceMem(buf), + font_data: template.clone(), handle: fctx.clone() }; Ok(handle) @@ -96,7 +95,7 @@ impl FontHandleMethods for FontHandle { Err(()) => Err(()) }; - fn create_face_from_buffer(lib: FT_Library, cbuf: *u8, cbuflen: uint, pt_size: f64) + fn create_face_from_buffer(lib: FT_Library, cbuf: *u8, cbuflen: uint, pt_size: Option<f64>) -> Result<FT_Face, ()> { unsafe { let mut face: FT_Face = ptr::null(); @@ -107,7 +106,11 @@ impl FontHandleMethods for FontHandle { if !result.succeeded() || face.is_null() { return Err(()); } - if FontHandle::set_char_size(face, pt_size).is_ok() { + let is_ok = match pt_size { + Some(s) => FontHandle::set_char_size(face, s).is_ok(), + None => true, + }; + if is_ok { Ok(face) } else { Err(()) @@ -115,14 +118,8 @@ impl FontHandleMethods for FontHandle { } } } - - // an identifier usable by FontContextHandle to recreate this FontHandle. - fn face_identifier(&self) -> String { - match self.source { - FontSourceFile(ref path) => path.clone(), - _ => unreachable!(), // This will be handled when the rest of the font - // refactor is complete. For now, it can never be hit. - } + fn get_template(&self) -> Arc<FontTemplateData> { + self.font_data.clone() } fn family_name(&self) -> String { unsafe { str::raw::from_c_str((*self.face).family_name) } @@ -265,39 +262,6 @@ impl<'a> FontHandle { } } - pub fn new_from_file(fctx: &FontContextHandle, file: &str, - maybe_style: Option<&SpecifiedFontStyle>) -> Result<FontHandle, ()> { - unsafe { - let ft_ctx: FT_Library = fctx.ctx.ctx; - if ft_ctx.is_null() { return Err(()); } - - let mut face: FT_Face = ptr::null(); - let face_index = 0 as FT_Long; - file.to_c_str().with_ref(|file_str| { - FT_New_Face(ft_ctx, file_str, - face_index, &mut face); - }); - if face.is_null() { - return Err(()); - } - - let ok = match maybe_style { - Some(style) => FontHandle::set_char_size(face, style.pt_size).is_ok(), - None => true, - }; - - if ok { - Ok(FontHandle { - source: FontSourceFile(file.to_str()), - face: face, - handle: fctx.clone() - }) - } else { - Err(()) - } - } - } - fn get_face_rec(&'a self) -> &'a FT_FaceRec { unsafe { &(*self.face) diff --git a/src/components/gfx/platform/android/font_context.rs b/src/components/gfx/platform/android/font_context.rs index 3196f25120e..97c1fdf698e 100644 --- a/src/components/gfx/platform/android/font_context.rs +++ b/src/components/gfx/platform/android/font_context.rs @@ -2,10 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use font::UsedFontStyle; -use platform::font::FontHandle; -use font_context::FontContextHandleMethods; - use freetype::freetype::FTErrorMethods; use freetype::freetype::FT_Add_Default_Modules; use freetype::freetype::FT_Done_FreeType; @@ -84,12 +80,3 @@ impl FontContextHandle { } } } - -impl FontContextHandleMethods for FontContextHandle { - fn create_font_from_identifier(&self, name: &str, style: Option<&UsedFontStyle>) - -> Result<FontHandle, ()> { - debug!("Creating font handle for {:s}", name); - FontHandle::new_from_file(self, name.as_slice(), style) - } -} - diff --git a/src/components/gfx/platform/android/font_list.rs b/src/components/gfx/platform/android/font_list.rs index d96cd8cdd0f..1f53e1b93d2 100644 --- a/src/components/gfx/platform/android/font_list.rs +++ b/src/components/gfx/platform/android/font_list.rs @@ -40,7 +40,7 @@ pub fn get_available_families(callback: |String|) { } } -pub fn load_variations_for_family(family_name: &str, callback: |String|) { +pub fn get_variations_for_family(family_name: &str, callback: |String|) { debug!("getting variations for {}", family_name); unsafe { let config = FcConfigGetCurrent(); diff --git a/src/components/gfx/platform/android/font_template.rs b/src/components/gfx/platform/android/font_template.rs new file mode 100644 index 00000000000..d1a821a0bf3 --- /dev/null +++ b/src/components/gfx/platform/android/font_template.rs @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::io; +use std::io::File; + +/// Platform specific font representation for android. +/// The identifier is an absolute path, and the bytes +/// field is the loaded data that can be passed to +/// freetype and azure directly. +pub struct FontTemplateData { + pub bytes: Vec<u8>, + pub identifier: String, +} + +impl FontTemplateData { + pub fn new(identifier: &str) -> FontTemplateData { + // TODO: Handle file load failure! + let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap(); + let bytes = file.read_to_end().unwrap(); + + FontTemplateData { + bytes: bytes, + identifier: identifier.to_string(), + } + } +} diff --git a/src/components/gfx/platform/linux/font.rs b/src/components/gfx/platform/linux/font.rs index 1299315ade2..3e060c0fb58 100644 --- a/src/components/gfx/platform/linux/font.rs +++ b/src/components/gfx/platform/linux/font.rs @@ -5,17 +5,18 @@ extern crate freetype; use font::{FontHandleMethods, FontMetrics, FontTableMethods}; -use font::{FontTableTag, FractionalPixel, SpecifiedFontStyle}; +use font::{FontTableTag, FractionalPixel}; use servo_util::geometry::Au; use servo_util::geometry; use platform::font_context::FontContextHandle; use text::glyph::GlyphId; use text::util::{float_to_fixed, fixed_to_float}; use style::computed_values::font_weight; +use platform::font_template::FontTemplateData; use freetype::freetype::{FT_Get_Char_Index, FT_Get_Postscript_Name}; use freetype::freetype::{FT_Load_Glyph, FT_Set_Char_Size}; -use freetype::freetype::{FT_New_Face, FT_Get_Sfnt_Table}; +use freetype::freetype::{FT_Get_Sfnt_Table}; use freetype::freetype::{FT_New_Memory_Face, FT_Done_Face}; use freetype::freetype::{FTErrorMethods, FT_F26Dot6, FT_Face, FT_FaceRec}; use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong}; @@ -28,6 +29,8 @@ use std::mem; use std::ptr; use std::str; +use sync::Arc; + fn float_to_fixed_ft(f: f64) -> i32 { float_to_fixed(6, f) } @@ -46,15 +49,10 @@ impl FontTableMethods for FontTable { } } -pub enum FontSource { - FontSourceMem(Vec<u8>), - FontSourceFile(String) -} - pub struct FontHandle { // The font binary. This must stay valid for the lifetime of the font, // if the font is created using FT_Memory_Face. - pub source: FontSource, + pub font_data: Arc<FontTemplateData>, pub face: FT_Face, pub handle: FontContextHandle } @@ -72,14 +70,15 @@ impl Drop for FontHandle { } impl FontHandleMethods for FontHandle { - fn new_from_buffer(fctx: &FontContextHandle, - buf: Vec<u8>, - style: &SpecifiedFontStyle) + fn new_from_template(fctx: &FontContextHandle, + template: Arc<FontTemplateData>, + pt_size: Option<f64>) -> Result<FontHandle, ()> { let ft_ctx: FT_Library = fctx.ctx.ctx; if ft_ctx.is_null() { return Err(()); } - let face_result = create_face_from_buffer(ft_ctx, buf.as_ptr(), buf.len(), style.pt_size); + let bytes = &template.deref().bytes; + let face_result = create_face_from_buffer(ft_ctx, bytes.as_ptr(), bytes.len(), pt_size); // TODO: this could be more simply written as result::chain // and moving buf into the struct ctor, but cant' move out of @@ -88,7 +87,7 @@ impl FontHandleMethods for FontHandle { Ok(face) => { let handle = FontHandle { face: face, - source: FontSourceMem(buf), + font_data: template.clone(), handle: fctx.clone() }; Ok(handle) @@ -96,7 +95,7 @@ impl FontHandleMethods for FontHandle { Err(()) => Err(()) }; - fn create_face_from_buffer(lib: FT_Library, cbuf: *u8, cbuflen: uint, pt_size: f64) + fn create_face_from_buffer(lib: FT_Library, cbuf: *u8, cbuflen: uint, pt_size: Option<f64>) -> Result<FT_Face, ()> { unsafe { let mut face: FT_Face = ptr::null(); @@ -107,7 +106,11 @@ impl FontHandleMethods for FontHandle { if !result.succeeded() || face.is_null() { return Err(()); } - if FontHandle::set_char_size(face, pt_size).is_ok() { + let is_ok = match pt_size { + Some(s) => FontHandle::set_char_size(face, s).is_ok(), + None => true, + }; + if is_ok { Ok(face) } else { Err(()) @@ -115,14 +118,8 @@ impl FontHandleMethods for FontHandle { } } } - - // an identifier usable by FontContextHandle to recreate this FontHandle. - fn face_identifier(&self) -> String { - match self.source { - FontSourceFile(ref path) => path.clone(), - _ => unreachable!(), // This will be handled when the rest of the font - // refactor is complete. For now, it can never be hit. - } + fn get_template(&self) -> Arc<FontTemplateData> { + self.font_data.clone() } fn family_name(&self) -> String { unsafe { str::raw::from_c_str((*self.face).family_name) } @@ -265,39 +262,6 @@ impl<'a> FontHandle { } } - pub fn new_from_file(fctx: &FontContextHandle, file: &str, - maybe_style: Option<&SpecifiedFontStyle>) -> Result<FontHandle, ()> { - unsafe { - let ft_ctx: FT_Library = fctx.ctx.ctx; - if ft_ctx.is_null() { return Err(()); } - - let mut face: FT_Face = ptr::null(); - let face_index = 0 as FT_Long; - file.to_c_str().with_ref(|file_str| { - FT_New_Face(ft_ctx, file_str, - face_index, &mut face); - }); - if face.is_null() { - return Err(()); - } - - let ok = match maybe_style { - Some(style) => FontHandle::set_char_size(face, style.pt_size).is_ok(), - None => true, - }; - - if ok { - Ok(FontHandle { - source: FontSourceFile(file.to_str()), - face: face, - handle: fctx.clone() - }) - } else { - Err(()) - } - } - } - fn get_face_rec(&'a self) -> &'a FT_FaceRec { unsafe { &(*self.face) diff --git a/src/components/gfx/platform/linux/font_context.rs b/src/components/gfx/platform/linux/font_context.rs index 3196f25120e..97c1fdf698e 100644 --- a/src/components/gfx/platform/linux/font_context.rs +++ b/src/components/gfx/platform/linux/font_context.rs @@ -2,10 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use font::UsedFontStyle; -use platform::font::FontHandle; -use font_context::FontContextHandleMethods; - use freetype::freetype::FTErrorMethods; use freetype::freetype::FT_Add_Default_Modules; use freetype::freetype::FT_Done_FreeType; @@ -84,12 +80,3 @@ impl FontContextHandle { } } } - -impl FontContextHandleMethods for FontContextHandle { - fn create_font_from_identifier(&self, name: &str, style: Option<&UsedFontStyle>) - -> Result<FontHandle, ()> { - debug!("Creating font handle for {:s}", name); - FontHandle::new_from_file(self, name.as_slice(), style) - } -} - diff --git a/src/components/gfx/platform/linux/font_list.rs b/src/components/gfx/platform/linux/font_list.rs index f76cde75c69..b401bb2b94a 100644 --- a/src/components/gfx/platform/linux/font_list.rs +++ b/src/components/gfx/platform/linux/font_list.rs @@ -40,7 +40,7 @@ pub fn get_available_families(callback: |String|) { } } -pub fn load_variations_for_family(family_name: &str, callback: |String|) { +pub fn get_variations_for_family(family_name: &str, callback: |String|) { debug!("getting variations for {}", family_name); unsafe { let config = FcConfigGetCurrent(); diff --git a/src/components/gfx/platform/linux/font_template.rs b/src/components/gfx/platform/linux/font_template.rs new file mode 100644 index 00000000000..e00a1018317 --- /dev/null +++ b/src/components/gfx/platform/linux/font_template.rs @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::io; +use std::io::File; + +/// Platform specific font representation for Linux. +/// The identifier is an absolute path, and the bytes +/// field is the loaded data that can be passed to +/// freetype and azure directly. +pub struct FontTemplateData { + pub bytes: Vec<u8>, + pub identifier: String, +} + +impl FontTemplateData { + pub fn new(identifier: &str) -> FontTemplateData { + // TODO: Handle file load failure! + let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap(); + let bytes = file.read_to_end().unwrap(); + + FontTemplateData { + bytes: bytes, + identifier: identifier.to_string(), + } + } +} diff --git a/src/components/gfx/platform/macos/font.rs b/src/components/gfx/platform/macos/font.rs index 2edb233a527..26209abb9d9 100644 --- a/src/components/gfx/platform/macos/font.rs +++ b/src/components/gfx/platform/macos/font.rs @@ -10,25 +10,25 @@ extern crate core_text; use font::{FontHandleMethods, FontMetrics, FontTableMethods}; use font::FontTableTag; -use font::{FractionalPixel, SpecifiedFontStyle}; +use font::FractionalPixel; use servo_util::geometry::{Au, px_to_pt}; use servo_util::geometry; use platform::macos::font_context::FontContextHandle; use text::glyph::GlyphId; use style::computed_values::font_weight; +use platform::font_template::FontTemplateData; use core_foundation::base::CFIndex; use core_foundation::data::CFData; use core_foundation::string::UniChar; -use core_graphics::data_provider::CGDataProvider; -use core_graphics::font::{CGFont, CGGlyph}; +use core_graphics::font::CGGlyph; use core_graphics::geometry::CGRect; use core_text::font::CTFont; use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors}; use core_text::font_descriptor::{kCTFontDefaultOrientation}; -use core_text; use std::ptr; +use sync::Arc; pub struct FontTable { data: CFData, @@ -52,43 +52,30 @@ impl FontTableMethods for FontTable { } pub struct FontHandle { - cgfont: Option<CGFont>, + pub font_data: Arc<FontTemplateData>, pub ctfont: CTFont, } -impl FontHandle { - pub fn new_from_CTFont(_: &FontContextHandle, ctfont: CTFont) -> Result<FontHandle, ()> { - Ok(FontHandle { - cgfont: None, - ctfont: ctfont, +impl FontHandleMethods for FontHandle { + fn new_from_template(_fctx: &FontContextHandle, + template: Arc<FontTemplateData>, + pt_size: Option<f64>) + -> Result<FontHandle, ()> { + let size = match pt_size { + Some(s) => s, + None => 0.0 + }; + let ct_result = core_text::font::new_from_name(template.identifier.as_slice(), size); + ct_result.and_then(|ctfont| { + Ok(FontHandle { + font_data: template.clone(), + ctfont: ctfont, + }) }) } - pub fn get_CGFont(&mut self) -> CGFont { - match self.cgfont { - Some(ref font) => font.clone(), - None => { - let cgfont = self.ctfont.copy_to_CGFont(); - self.cgfont = Some(cgfont.clone()); - cgfont - } - } - } -} - -impl FontHandleMethods for FontHandle { - fn new_from_buffer(_: &FontContextHandle, buf: Vec<u8>, style: &SpecifiedFontStyle) - -> Result<FontHandle, ()> { - let fontprov = CGDataProvider::from_buffer(buf.as_slice()); - let cgfont = CGFont::from_data_provider(fontprov); - let ctfont = core_text::font::new_from_CGFont(&cgfont, style.pt_size); - - let result = Ok(FontHandle { - cgfont: Some(cgfont), - ctfont: ctfont, - }); - - return result; + fn get_template(&self) -> Arc<FontTemplateData> { + self.font_data.clone() } fn family_name(&self) -> String { @@ -182,9 +169,5 @@ impl FontHandleMethods for FontHandle { Some(FontTable::wrap(data)) }) } - - fn face_identifier(&self) -> String { - self.ctfont.postscript_name() - } } diff --git a/src/components/gfx/platform/macos/font_context.rs b/src/components/gfx/platform/macos/font_context.rs index 4b66aa6bbf8..94730641c3d 100644 --- a/src/components/gfx/platform/macos/font_context.rs +++ b/src/components/gfx/platform/macos/font_context.rs @@ -2,12 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use font::UsedFontStyle; -use font_context::FontContextHandleMethods; -use platform::macos::font::FontHandle; - -use core_text; - #[deriving(Clone)] pub struct FontContextHandle { ctx: () @@ -20,19 +14,3 @@ impl FontContextHandle { FontContextHandle { ctx: () } } } - -impl FontContextHandleMethods for FontContextHandle { - fn create_font_from_identifier(&self, - name: &str, - style: Option<&UsedFontStyle>) - -> Result<FontHandle, ()> { - let pt_size = match style { - Some(style) => style.pt_size, - None => 0.0, - }; - let ctfont_result = core_text::font::new_from_name(name.as_slice(), pt_size); - ctfont_result.and_then(|ctfont| { - FontHandle::new_from_CTFont(self, ctfont) - }) - } -} diff --git a/src/components/gfx/platform/macos/font_list.rs b/src/components/gfx/platform/macos/font_list.rs index 65d375c5845..dd0611a0356 100644 --- a/src/components/gfx/platform/macos/font_list.rs +++ b/src/components/gfx/platform/macos/font_list.rs @@ -18,7 +18,7 @@ pub fn get_available_families(callback: |String|) { } } -pub fn load_variations_for_family(family_name: &str, callback: |String|) { +pub fn get_variations_for_family(family_name: &str, callback: |String|) { debug!("Looking for faces of family: {:s}", family_name); let family_collection = diff --git a/src/components/gfx/platform/macos/font_template.rs b/src/components/gfx/platform/macos/font_template.rs new file mode 100644 index 00000000000..bda41186da3 --- /dev/null +++ b/src/components/gfx/platform/macos/font_template.rs @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use core_text::font::CTFont; +use core_text; + +/// Platform specific font representation for mac. +/// The identifier is a PostScript font name. The +/// CTFont object is cached here for use by the +/// render functions that create CGFont references. +pub struct FontTemplateData { + pub ctfont: CTFont, + pub identifier: String, +} + +impl FontTemplateData { + pub fn new(identifier: &str) -> FontTemplateData { + let ctfont_result = core_text::font::new_from_name(identifier.as_slice(), 0.0); + FontTemplateData { + ctfont: ctfont_result.unwrap(), + identifier: identifier.to_string(), + } + } +} diff --git a/src/components/gfx/platform/mod.rs b/src/components/gfx/platform/mod.rs index 34870cc2f2d..eada62fc637 100644 --- a/src/components/gfx/platform/mod.rs +++ b/src/components/gfx/platform/mod.rs @@ -2,15 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#[cfg(target_os="linux")] pub use platform::linux::{font, font_context, font_list}; -#[cfg(target_os="macos")] pub use platform::macos::{font, font_context, font_list}; -#[cfg(target_os="android")] pub use platform::android::{font, font_context, font_list}; +#[cfg(target_os="linux")] pub use platform::linux::{font, font_context, font_list, font_template}; +#[cfg(target_os="macos")] pub use platform::macos::{font, font_context, font_list, font_template}; +#[cfg(target_os="android")] pub use platform::android::{font, font_context, font_list, font_template}; #[cfg(target_os="linux")] pub mod linux { pub mod font; pub mod font_context; pub mod font_list; + pub mod font_template; } #[cfg(target_os="macos")] @@ -18,6 +19,7 @@ pub mod macos { pub mod font; pub mod font_context; pub mod font_list; + pub mod font_template; } #[cfg(target_os="android")] @@ -25,4 +27,5 @@ pub mod android { pub mod font; pub mod font_context; pub mod font_list; + pub mod font_template; } diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 8048db3b70d..d6c31bce048 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -7,7 +7,7 @@ use buffer_map::BufferMap; use display_list::optimizer::DisplayListOptimizer; use display_list::DisplayList; -use font_context::{FontContext, FontContextInfo}; +use font_context::FontContext; use render_context::RenderContext; use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources}; @@ -34,6 +34,7 @@ use servo_util::time::{TimeProfilerChan, profile}; use servo_util::time; use std::comm::{Receiver, Sender, channel}; use sync::Arc; +use font_cache_task::FontCacheTask; /// Information about a layer that layout sends to the painting task. pub struct RenderLayer { @@ -144,12 +145,15 @@ impl<C:RenderListener + Send> RenderTask<C> { port: Receiver<Msg>, compositor: C, constellation_chan: ConstellationChan, + font_cache_task: FontCacheTask, failure_msg: Failure, opts: Opts, time_profiler_chan: TimeProfilerChan, shutdown_chan: Sender<()>) { let ConstellationChan(c) = constellation_chan.clone(); + let fc = font_cache_task.clone(); + let mut task_opts = TaskOpts::new(); task_opts.name = Some("RenderTask".into_maybe_owned()); task_opts.on_exit = Some(proc(result: task::Result) { @@ -172,11 +176,7 @@ impl<C:RenderListener + Send> RenderTask<C> { port: port, compositor: compositor, constellation_chan: constellation_chan, - font_ctx: box FontContext::new(FontContextInfo { - backend: opts.render_backend.clone(), - needs_font_list: false, - time_profiler_chan: time_profiler_chan.clone(), - }), + font_ctx: box FontContext::new(fc.clone()), opts: opts, time_profiler_chan: time_profiler_chan, diff --git a/src/components/gfx/text/shaping/harfbuzz.rs b/src/components/gfx/text/shaping/harfbuzz.rs index b6646db771a..e028ae8dac2 100644 --- a/src/components/gfx/text/shaping/harfbuzz.rs +++ b/src/components/gfx/text/shaping/harfbuzz.rs @@ -163,7 +163,7 @@ impl Shaper { 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; + let pt_size = font.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. diff --git a/src/components/gfx/text/text_run.rs b/src/components/gfx/text/text_run.rs index 1f182572ac1..719660c676d 100644 --- a/src/components/gfx/text/text_run.rs +++ b/src/components/gfx/text/text_run.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use font::{Font, FontDescriptor, RunMetrics, FontStyle, FontMetrics}; +use font::{Font, RunMetrics, FontMetrics}; use servo_util::geometry::Au; use servo_util::range::Range; use servo_util::vec::{Comparator, FullBinarySearchMethods}; @@ -10,14 +10,16 @@ use std::slice::Items; use style::computed_values::text_decoration; use sync::Arc; use text::glyph::{CharIndex, GlyphStore}; +use font::FontHandleMethods; +use platform::font_template::FontTemplateData; /// A single "paragraph" of text in one font size and style. #[deriving(Clone)] pub struct TextRun { pub text: Arc<String>, - pub font_descriptor: FontDescriptor, + pub font_template: Arc<FontTemplateData>, + pub pt_size: f64, pub font_metrics: FontMetrics, - pub font_style: FontStyle, pub decoration: text_decoration::T, /// The glyph runs that make up this text run. pub glyphs: Arc<Vec<GlyphRun>>, @@ -119,12 +121,11 @@ impl<'a> Iterator<Range<CharIndex>> for LineIterator<'a> { impl<'a> TextRun { pub fn new(font: &mut Font, text: String, decoration: text_decoration::T) -> TextRun { let glyphs = TextRun::break_and_shape(font, text.as_slice()); - let run = TextRun { text: Arc::new(text), - font_style: font.style.clone(), font_metrics: font.metrics.clone(), - font_descriptor: font.get_descriptor(), + font_template: font.handle.get_template(), + pt_size: font.pt_size, decoration: decoration, glyphs: Arc::new(glyphs), }; @@ -133,7 +134,6 @@ impl<'a> TextRun { pub fn break_and_shape(font: &mut Font, text: &str) -> Vec<GlyphRun> { // TODO(Issue #230): do a better job. See Gecko's LineBreaker. - let mut glyphs = vec!(); let (mut byte_i, mut char_i) = (0u, CharIndex(0)); let mut cur_slice_is_whitespace = false; diff --git a/src/components/layout/context.rs b/src/components/layout/context.rs index 4163de71c6c..fdd6dec0254 100644 --- a/src/components/layout/context.rs +++ b/src/components/layout/context.rs @@ -9,7 +9,8 @@ use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; use geom::rect::Rect; use geom::size::Size2D; use gfx::display_list::OpaqueNode; -use gfx::font_context::{FontContext, FontContextInfo}; +use gfx::font_context::FontContext; +use gfx::font_cache_task::FontCacheTask; #[cfg(not(target_os="android"))] use green::task::GreenTask; use script::layout_interface::LayoutChan; @@ -67,8 +68,8 @@ pub struct LayoutContext { /// A channel up to the layout task. pub layout_chan: LayoutChan, - /// Information needed to construct a font context. - pub font_context_info: FontContextInfo, + /// Interface to the font cache task. + pub font_cache_task: FontCacheTask, /// The CSS selector stylist. /// @@ -105,7 +106,7 @@ impl LayoutContext { unsafe { if FONT_CONTEXT == ptr::mut_null() { - let context = box FontContext::new(self.font_context_info.clone()); + let context = box FontContext::new(self.font_cache_task.clone()); FONT_CONTEXT = mem::transmute(context) } mem::transmute(FONT_CONTEXT) @@ -172,7 +173,7 @@ impl LayoutContext { match opt { Some(c) => context = mem::transmute(c), None => { - context = mem::transmute(box FontContext::new(self.font_context_info.clone())) + context = mem::transmute(box FontContext::new(self.font_cache_task.clone())) } } font_context.replace(Some(context)); diff --git a/src/components/layout/layout_task.rs b/src/components/layout/layout_task.rs index f7fd408fd6e..e97057c605c 100644 --- a/src/components/layout/layout_task.rs +++ b/src/components/layout/layout_task.rs @@ -27,7 +27,7 @@ use geom::rect::Rect; use geom::size::Size2D; use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem}; use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode}; -use gfx::font_context::{FontContext, FontContextInfo}; +use gfx::font_context::FontContext; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer}; use gfx::{render_task, color}; use script::dom::bindings::js::JS; @@ -44,6 +44,7 @@ use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg}; use servo_msg::compositor_msg::Scrollable; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; +use gfx::font_cache_task::{FontCacheTask}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_util::geometry::Au; use servo_util::geometry; @@ -84,6 +85,9 @@ pub struct LayoutTask { /// The channel on which messages can be sent to the image cache. pub image_cache_task: ImageCacheTask, + /// Public interface to the font cache task. + pub font_cache_task: FontCacheTask, + /// The local image cache. pub local_image_cache: Arc<Mutex<LocalImageCache>>, @@ -278,6 +282,7 @@ impl LayoutTask { script_chan: ScriptChan, render_chan: RenderChan, img_cache_task: ImageCacheTask, + font_cache_task: FontCacheTask, opts: Opts, time_profiler_chan: TimeProfilerChan, shutdown_chan: Sender<()>) { @@ -293,6 +298,7 @@ impl LayoutTask { script_chan, render_chan, img_cache_task, + font_cache_task, &opts, time_profiler_chan); layout.start(); @@ -309,6 +315,7 @@ impl LayoutTask { script_chan: ScriptChan, render_chan: RenderChan, image_cache_task: ImageCacheTask, + font_cache_task: FontCacheTask, opts: &Opts, time_profiler_chan: TimeProfilerChan) -> LayoutTask { @@ -328,6 +335,7 @@ impl LayoutTask { script_chan: script_chan, render_chan: render_chan, image_cache_task: image_cache_task.clone(), + font_cache_task: font_cache_task, local_image_cache: local_image_cache, screen_size: screen_size, @@ -349,18 +357,12 @@ impl LayoutTask { // Create a layout context for use in building display lists, hit testing, &c. fn build_layout_context(&self, reflow_root: &LayoutNode, url: &Url) -> LayoutContext { - let font_context_info = FontContextInfo { - backend: self.opts.render_backend, - needs_font_list: true, - time_profiler_chan: self.time_profiler_chan.clone(), - }; - LayoutContext { image_cache: self.local_image_cache.clone(), screen_size: self.screen_size.clone(), constellation_chan: self.constellation_chan.clone(), layout_chan: self.chan.clone(), - font_context_info: font_context_info, + font_cache_task: self.font_cache_task.clone(), stylist: &*self.stylist, url: (*url).clone(), reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root), @@ -594,7 +596,7 @@ impl LayoutTask { // FIXME(pcwalton): This is a pretty bogus thing to do. Essentially this is a workaround // for libgreen having slow TLS. let mut font_context_opt = if self.parallel_traversal.is_none() { - Some(box FontContext::new(layout_ctx.font_context_info.clone())) + Some(box FontContext::new(layout_ctx.font_cache_task.clone())) } else { None }; diff --git a/src/components/layout/text.rs b/src/components/layout/text.rs index 4f354c96703..5991df7db44 100644 --- a/src/components/layout/text.rs +++ b/src/components/layout/text.rs @@ -142,8 +142,8 @@ impl TextRunScanner { // TODO(#177): Text run creation must account for the renderability of text by // font group fonts. This is probably achieved by creating the font group above // and then letting `FontGroup` decide which `Font` to stick into the text run. - let fontgroup = font_context.get_resolved_font_for_style(&font_style); - let run = box fontgroup.borrow().create_textrun( + let fontgroup = font_context.get_layout_font_group_for_style(&font_style); + let run = box fontgroup.create_textrun( transformed_text.clone(), decoration); debug!("TextRunScanner: pushing single text fragment in range: {} ({})", @@ -164,7 +164,7 @@ impl TextRunScanner { // and then letting `FontGroup` decide which `Font` to stick into the text run. let in_fragment = &in_fragments[self.clump.begin().to_uint()]; let font_style = in_fragment.font_style(); - let fontgroup = font_context.get_resolved_font_for_style(&font_style); + let fontgroup = font_context.get_layout_font_group_for_style(&font_style); let decoration = in_fragment.text_decoration(); // TODO(#115): Use the actual CSS `white-space` property of the relevant style. @@ -218,7 +218,7 @@ impl TextRunScanner { let clump = self.clump; let run = if clump.length() != CharIndex(0) && run_str.len() > 0 { Some(Arc::new(box TextRun::new( - &mut *fontgroup.borrow().fonts.get(0).borrow_mut(), + &mut *fontgroup.fonts.get(0).borrow_mut(), run_str.to_string(), decoration))) } else { None @@ -258,8 +258,8 @@ impl TextRunScanner { #[inline] pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: &FontStyle) -> FontMetrics { - let fontgroup = font_context.get_resolved_font_for_style(font_style); - fontgroup.borrow().fonts.get(0).borrow().metrics.clone() + let fontgroup = font_context.get_layout_font_group_for_style(font_style); + fontgroup.fonts.get(0).borrow().metrics.clone() } /// Converts a computed style to a font style used for rendering. diff --git a/src/components/main/servo.rs b/src/components/main/servo.rs index 49f68a2bf1b..f845846280e 100644 --- a/src/components/main/servo.rs +++ b/src/components/main/servo.rs @@ -20,6 +20,7 @@ extern crate servo_msg = "msg"; #[phase(plugin, link)] extern crate servo_util = "util"; extern crate green; +extern crate gfx; extern crate libc; extern crate native; extern crate rustrt; @@ -35,6 +36,8 @@ use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask}; #[cfg(not(test))] use servo_net::resource_task::ResourceTask; #[cfg(not(test))] +use gfx::font_cache_task::FontCacheTask; +#[cfg(not(test))] use servo_util::time::TimeProfiler; #[cfg(not(test))] use servo_util::memory::MemoryProfiler; @@ -115,10 +118,12 @@ pub fn run(opts: opts::Opts) { } else { ImageCacheTask(resource_task.clone()) }; + let font_cache_task = FontCacheTask::new(); let constellation_chan = Constellation::start(compositor_chan, opts, resource_task, image_cache_task, + font_cache_task, time_profiler_chan_clone); // Send the URL command to the constellation. |