mirror of
https://github.com/servo/servo.git
synced 2025-06-18 13:24:29 +00:00
Next stage of refactoring font system. This commit introduces
the font cache task, and adapts client code to use it. It also cleans up some existing code paths. - Fonts are only read once from disk while in use (they are discarded if the reference count reaches zero, however). This saves memory and prevents unnecessary reading from disk. - It will be easier to add web font support, as all fonts are created and managed in a single place and the entire pipeline ensures that only one in-memory copy of font data is required. An overview of how the pieces fit together: FontTemplate - A structure containing everything that is required to create (and select) font handles. This structure is shared among all matching font handles (via Arc). FontTemplateData - A platform specific structure that contains the actual font data inside a template (this is a byte array on Linux/Android, CTFont on Mac). FontHandle - An opaque, platform specific handle to a font instance. Each FontHandle contains an Arc<> reference to the FontTemplate it was created from. FontCache - This is a separate task, that is responsible for loading and caching FontTemplate structures. There is one FontCache per constellation. It is only ever accessed via the FontContext described below. FontContext - This is the public interface to the FontCache and is used by the layout and render code to create font handles. These must *not* be shared between threads. There is typically one FontContext per thread/task.
This commit is contained in:
parent
e62637fee2
commit
12978eeb50
29 changed files with 812 additions and 899 deletions
|
@ -22,6 +22,7 @@ use servo_msg::constellation_msg::{NavigationType, PipelineId, RendererReadyMsg,
|
||||||
use servo_msg::constellation_msg::{SubpageId, WindowSizeData};
|
use servo_msg::constellation_msg::{SubpageId, WindowSizeData};
|
||||||
use servo_msg::constellation_msg;
|
use servo_msg::constellation_msg;
|
||||||
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
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::ResourceTask;
|
||||||
use servo_net::resource_task;
|
use servo_net::resource_task;
|
||||||
use servo_util::geometry::PagePx;
|
use servo_util::geometry::PagePx;
|
||||||
|
@ -43,6 +44,7 @@ pub struct Constellation {
|
||||||
pub resource_task: ResourceTask,
|
pub resource_task: ResourceTask,
|
||||||
pub image_cache_task: ImageCacheTask,
|
pub image_cache_task: ImageCacheTask,
|
||||||
pipelines: HashMap<PipelineId, Rc<Pipeline>>,
|
pipelines: HashMap<PipelineId, Rc<Pipeline>>,
|
||||||
|
font_cache_task: FontCacheTask,
|
||||||
navigation_context: NavigationContext,
|
navigation_context: NavigationContext,
|
||||||
next_pipeline_id: PipelineId,
|
next_pipeline_id: PipelineId,
|
||||||
pending_frames: Vec<FrameChange>,
|
pending_frames: Vec<FrameChange>,
|
||||||
|
@ -243,6 +245,7 @@ impl Constellation {
|
||||||
opts: &Opts,
|
opts: &Opts,
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
|
font_cache_task: FontCacheTask,
|
||||||
time_profiler_chan: TimeProfilerChan)
|
time_profiler_chan: TimeProfilerChan)
|
||||||
-> ConstellationChan {
|
-> ConstellationChan {
|
||||||
let (constellation_port, constellation_chan) = ConstellationChan::new();
|
let (constellation_port, constellation_chan) = ConstellationChan::new();
|
||||||
|
@ -255,6 +258,7 @@ impl Constellation {
|
||||||
compositor_chan: compositor_chan,
|
compositor_chan: compositor_chan,
|
||||||
resource_task: resource_task,
|
resource_task: resource_task,
|
||||||
image_cache_task: image_cache_task,
|
image_cache_task: image_cache_task,
|
||||||
|
font_cache_task: font_cache_task,
|
||||||
pipelines: HashMap::new(),
|
pipelines: HashMap::new(),
|
||||||
navigation_context: NavigationContext::new(),
|
navigation_context: NavigationContext::new(),
|
||||||
next_pipeline_id: PipelineId(0),
|
next_pipeline_id: PipelineId(0),
|
||||||
|
@ -368,6 +372,7 @@ impl Constellation {
|
||||||
}
|
}
|
||||||
self.image_cache_task.exit();
|
self.image_cache_task.exit();
|
||||||
self.resource_task.send(resource_task::Exit);
|
self.resource_task.send(resource_task::Exit);
|
||||||
|
self.font_cache_task.exit();
|
||||||
self.compositor_chan.send(ShutdownComplete);
|
self.compositor_chan.send(ShutdownComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,6 +427,7 @@ impl Constellation {
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.compositor_chan.clone(),
|
self.compositor_chan.clone(),
|
||||||
self.image_cache_task.clone(),
|
self.image_cache_task.clone(),
|
||||||
|
self.font_cache_task.clone(),
|
||||||
self.resource_task.clone(),
|
self.resource_task.clone(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
self.window_size,
|
self.window_size,
|
||||||
|
@ -449,6 +455,7 @@ impl Constellation {
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.compositor_chan.clone(),
|
self.compositor_chan.clone(),
|
||||||
self.image_cache_task.clone(),
|
self.image_cache_task.clone(),
|
||||||
|
self.font_cache_task.clone(),
|
||||||
self.resource_task.clone(),
|
self.resource_task.clone(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
self.window_size,
|
self.window_size,
|
||||||
|
@ -575,6 +582,7 @@ impl Constellation {
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.compositor_chan.clone(),
|
self.compositor_chan.clone(),
|
||||||
self.image_cache_task.clone(),
|
self.image_cache_task.clone(),
|
||||||
|
self.font_cache_task.clone(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
self.opts.clone(),
|
self.opts.clone(),
|
||||||
source_pipeline.clone(),
|
source_pipeline.clone(),
|
||||||
|
@ -587,6 +595,7 @@ impl Constellation {
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.compositor_chan.clone(),
|
self.compositor_chan.clone(),
|
||||||
self.image_cache_task.clone(),
|
self.image_cache_task.clone(),
|
||||||
|
self.font_cache_task.clone(),
|
||||||
self.resource_task.clone(),
|
self.resource_task.clone(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
self.window_size,
|
self.window_size,
|
||||||
|
@ -643,6 +652,7 @@ impl Constellation {
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.compositor_chan.clone(),
|
self.compositor_chan.clone(),
|
||||||
self.image_cache_task.clone(),
|
self.image_cache_task.clone(),
|
||||||
|
self.font_cache_task.clone(),
|
||||||
self.resource_task.clone(),
|
self.resource_task.clone(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
self.window_size,
|
self.window_size,
|
||||||
|
|
|
@ -14,6 +14,7 @@ use script::script_task;
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId};
|
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId};
|
||||||
use servo_msg::constellation_msg::WindowSizeData;
|
use servo_msg::constellation_msg::WindowSizeData;
|
||||||
use servo_net::image_cache_task::ImageCacheTask;
|
use servo_net::image_cache_task::ImageCacheTask;
|
||||||
|
use gfx::font_cache_task::FontCacheTask;
|
||||||
use servo_net::resource_task::ResourceTask;
|
use servo_net::resource_task::ResourceTask;
|
||||||
use servo_util::opts::Opts;
|
use servo_util::opts::Opts;
|
||||||
use servo_util::time::TimeProfilerChan;
|
use servo_util::time::TimeProfilerChan;
|
||||||
|
@ -49,6 +50,7 @@ impl Pipeline {
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
compositor_chan: CompositorChan,
|
compositor_chan: CompositorChan,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
|
font_cache_task: FontCacheTask,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
script_pipeline: Rc<Pipeline>,
|
script_pipeline: Rc<Pipeline>,
|
||||||
|
@ -68,6 +70,7 @@ impl Pipeline {
|
||||||
render_port,
|
render_port,
|
||||||
compositor_chan.clone(),
|
compositor_chan.clone(),
|
||||||
constellation_chan.clone(),
|
constellation_chan.clone(),
|
||||||
|
font_cache_task.clone(),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
opts.clone(),
|
opts.clone(),
|
||||||
time_profiler_chan.clone(),
|
time_profiler_chan.clone(),
|
||||||
|
@ -81,6 +84,7 @@ impl Pipeline {
|
||||||
script_pipeline.script_chan.clone(),
|
script_pipeline.script_chan.clone(),
|
||||||
render_chan.clone(),
|
render_chan.clone(),
|
||||||
image_cache_task.clone(),
|
image_cache_task.clone(),
|
||||||
|
font_cache_task.clone(),
|
||||||
opts.clone(),
|
opts.clone(),
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
layout_shutdown_chan);
|
layout_shutdown_chan);
|
||||||
|
@ -110,6 +114,7 @@ impl Pipeline {
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
compositor_chan: CompositorChan,
|
compositor_chan: CompositorChan,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
|
font_cache_task: FontCacheTask,
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
window_size: WindowSizeData,
|
window_size: WindowSizeData,
|
||||||
|
@ -150,6 +155,7 @@ impl Pipeline {
|
||||||
render_port,
|
render_port,
|
||||||
compositor_chan.clone(),
|
compositor_chan.clone(),
|
||||||
constellation_chan.clone(),
|
constellation_chan.clone(),
|
||||||
|
font_cache_task.clone(),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
opts.clone(),
|
opts.clone(),
|
||||||
time_profiler_chan.clone(),
|
time_profiler_chan.clone(),
|
||||||
|
@ -163,6 +169,7 @@ impl Pipeline {
|
||||||
script_chan.clone(),
|
script_chan.clone(),
|
||||||
render_chan.clone(),
|
render_chan.clone(),
|
||||||
image_cache_task,
|
image_cache_task,
|
||||||
|
font_cache_task,
|
||||||
opts.clone(),
|
opts.clone(),
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
layout_shutdown_chan);
|
layout_shutdown_chan);
|
||||||
|
|
|
@ -32,6 +32,12 @@ use std::mem;
|
||||||
use std::slice::Items;
|
use std::slice::Items;
|
||||||
use style::computed_values::border_style;
|
use style::computed_values::border_style;
|
||||||
use sync::Arc;
|
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;
|
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.
|
/// "Steps" as defined by CSS 2.1 § E.2.
|
||||||
#[deriving(Clone, PartialEq)]
|
#[deriving(Clone, PartialEq)]
|
||||||
pub enum StackingLevel {
|
pub enum StackingLevel {
|
||||||
|
@ -510,25 +590,27 @@ impl DisplayItem {
|
||||||
|
|
||||||
// FIXME(pcwalton): Allocating? Why?
|
// FIXME(pcwalton): Allocating? Why?
|
||||||
let text_run = text.text_run.clone();
|
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 = {
|
let font = render_context.font_ctx.get_render_font_from_template(
|
||||||
font.borrow().metrics.clone()
|
&text_run.font_template,
|
||||||
};
|
text_run.pt_size,
|
||||||
|
render_context.opts.render_backend);
|
||||||
|
let font = font.borrow();
|
||||||
|
|
||||||
let origin = text.base.bounds.origin;
|
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,
|
font.draw_text_into_context(render_context,
|
||||||
&*text.text_run,
|
&*text.text_run,
|
||||||
&text.range,
|
&text.range,
|
||||||
baseline_origin,
|
baseline_origin,
|
||||||
text.text_color);
|
text.text_color);
|
||||||
}
|
}
|
||||||
let width = text.base.bounds.size.width;
|
let width = text.base.bounds.size.width;
|
||||||
let underline_size = font_metrics.underline_size;
|
let underline_size = text_run.font_metrics.underline_size;
|
||||||
let underline_offset = font_metrics.underline_offset;
|
let underline_offset = text_run.font_metrics.underline_offset;
|
||||||
let strikeout_size = font_metrics.strikeout_size;
|
let strikeout_size = text_run.font_metrics.strikeout_size;
|
||||||
let strikeout_offset = font_metrics.strikeout_offset;
|
let strikeout_offset = text_run.font_metrics.strikeout_offset;
|
||||||
|
|
||||||
for underline_color in text.text_decorations.underline.iter() {
|
for underline_color in text.text_decorations.underline.iter() {
|
||||||
let underline_y = baseline_origin.y - underline_offset;
|
let underline_y = baseline_origin.y - underline_offset;
|
||||||
|
|
|
@ -2,34 +2,23 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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 geom::{Point2D, Rect, Size2D};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::num::Zero;
|
|
||||||
use std::ptr;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use servo_util::cache::{Cache, HashCache};
|
use servo_util::cache::{Cache, HashCache};
|
||||||
use servo_util::range::Range;
|
|
||||||
use style::computed_values::{text_decoration, font_weight, font_style};
|
use style::computed_values::{text_decoration, font_weight, font_style};
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
|
|
||||||
use color::Color;
|
|
||||||
use font_context::FontContext;
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use platform::font_context::FontContextHandle;
|
use platform::font_context::FontContextHandle;
|
||||||
use platform::font::{FontHandle, FontTable};
|
use platform::font::{FontHandle, FontTable};
|
||||||
use render_context::RenderContext;
|
use text::glyph::{GlyphStore, GlyphId};
|
||||||
use text::glyph::{CharIndex, GlyphStore, GlyphId};
|
|
||||||
use text::shaping::ShaperMethods;
|
use text::shaping::ShaperMethods;
|
||||||
use text::{Shaper, TextRun};
|
use text::{Shaper, TextRun};
|
||||||
|
use font_template::FontTemplateDescriptor;
|
||||||
#[cfg(target_os="linux")]
|
use platform::font_template::FontTemplateData;
|
||||||
#[cfg(target_os="android")]
|
|
||||||
use azure::scaled_font::NativeFont;
|
|
||||||
|
|
||||||
// FontHandle encapsulates access to the platform's font API,
|
// FontHandle encapsulates access to the platform's font API,
|
||||||
// e.g. quartz, FreeType. It provides access to metrics and tables
|
// 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.
|
// resources needed by the graphics layer to draw glyphs.
|
||||||
|
|
||||||
pub trait FontHandleMethods {
|
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,()>;
|
-> Result<Self,()>;
|
||||||
|
fn get_template(&self) -> Arc<FontTemplateData>;
|
||||||
// an identifier usable by FontContextHandle to recreate this FontHandle.
|
|
||||||
fn face_identifier(&self) -> String;
|
|
||||||
fn family_name(&self) -> String;
|
fn family_name(&self) -> String;
|
||||||
fn face_name(&self) -> String;
|
fn face_name(&self) -> String;
|
||||||
fn is_italic(&self) -> bool;
|
fn is_italic(&self) -> bool;
|
||||||
|
@ -110,54 +97,78 @@ pub struct FontStyle {
|
||||||
pub type SpecifiedFontStyle = FontStyle;
|
pub type SpecifiedFontStyle = FontStyle;
|
||||||
pub type UsedFontStyle = FontStyle;
|
pub type UsedFontStyle = FontStyle;
|
||||||
|
|
||||||
// FontDescriptor serializes a specific font and used font style
|
pub struct Font {
|
||||||
// options, such as point size.
|
pub handle: FontHandle,
|
||||||
|
pub metrics: FontMetrics,
|
||||||
// It's used to swizzle/unswizzle gfx::Font instances when
|
pub descriptor: FontTemplateDescriptor,
|
||||||
// communicating across tasks, such as the display list between layout
|
pub pt_size: f64,
|
||||||
// and render tasks.
|
pub shaper: Option<Shaper>,
|
||||||
#[deriving(Clone, PartialEq)]
|
pub shape_cache: HashCache<String, Arc<GlyphStore>>,
|
||||||
pub struct FontDescriptor {
|
pub glyph_advance_cache: HashCache<u32, FractionalPixel>,
|
||||||
pub style: UsedFontStyle,
|
|
||||||
pub selector: FontSelector,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontDescriptor {
|
impl Font {
|
||||||
pub fn new(style: UsedFontStyle, selector: FontSelector) -> FontDescriptor {
|
pub fn shape_text(&mut self, text: String, is_whitespace: bool) -> Arc<GlyphStore> {
|
||||||
FontDescriptor {
|
|
||||||
style: style,
|
//FIXME (ksh8281)
|
||||||
selector: selector,
|
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>(&'a mut self) -> &'a Shaper {
|
||||||
|
// fast path: already created a shaper
|
||||||
|
match self.shaper {
|
||||||
|
Some(ref shaper) => {
|
||||||
|
let s: &'a Shaper = shaper;
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let shaper = Shaper::new(self);
|
||||||
|
self.shaper = Some(shaper);
|
||||||
|
self.shaper.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
|
||||||
|
let result = self.handle.get_table_for_tag(tag);
|
||||||
|
let status = if result.is_some() { "Found" } else { "Didn't find" };
|
||||||
|
|
||||||
|
debug!("{:s} font table[{:s}] with family={}, face={}",
|
||||||
|
status, tag.tag_to_str(),
|
||||||
|
self.handle.family_name(), self.handle.face_name());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||||
|
self.handle.glyph_index(codepoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 struct FontGroup {
|
||||||
pub families: Vec<String>,
|
pub fonts: Vec<Rc<RefCell<Font>>>,
|
||||||
// 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 {
|
impl FontGroup {
|
||||||
pub fn new(families: Vec<String>, style: &UsedFontStyle, fonts: Vec<Rc<RefCell<Font>>>) -> FontGroup {
|
pub fn new(fonts: Vec<Rc<RefCell<Font>>>) -> FontGroup {
|
||||||
FontGroup {
|
FontGroup {
|
||||||
families: families,
|
fonts: fonts
|
||||||
style: (*style).clone(),
|
|
||||||
fonts: fonts,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,240 +207,3 @@ impl RunMetrics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
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 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();
|
|
||||||
|
|
||||||
Font {
|
|
||||||
handle: handle,
|
|
||||||
azure_font: None,
|
|
||||||
shaper: None,
|
|
||||||
style: (*style).clone(),
|
|
||||||
metrics: metrics,
|
|
||||||
backend: backend,
|
|
||||||
shape_cache: HashCache::new(),
|
|
||||||
glyph_advance_cache: HashCache::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_shaper(&'a mut self) -> &'a Shaper {
|
|
||||||
// fast path: already created a shaper
|
|
||||||
match self.shaper {
|
|
||||||
Some(ref shaper) => {
|
|
||||||
let s: &'a Shaper = shaper;
|
|
||||||
return s;
|
|
||||||
},
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let shaper = Shaper::new(self);
|
|
||||||
self.shaper = Some(shaper);
|
|
||||||
self.shaper.get_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
|
|
||||||
let result = self.handle.get_table_for_tag(tag);
|
|
||||||
let status = if result.is_some() { "Found" } else { "Didn't find" };
|
|
||||||
|
|
||||||
debug!("{:s} font table[{:s}] with family={}, face={}",
|
|
||||||
status, tag.tag_to_str(),
|
|
||||||
self.handle.family_name(), self.handle.face_name());
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 fn get_descriptor(&self) -> FontDescriptor {
|
|
||||||
FontDescriptor::new(self.style.clone(), SelectorPlatformIdentifier(self.handle.face_identifier()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
|
||||||
self.handle.glyph_index(codepoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
213
src/components/gfx/font_cache_task.rs
Normal file
213
src/components/gfx/font_cache_task.rs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,223 +2,147 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, SelectorPlatformIdentifier};
|
use font::{Font, FontGroup};
|
||||||
use font::{SpecifiedFontStyle, UsedFontStyle};
|
use font::SpecifiedFontStyle;
|
||||||
use font_list::FontList;
|
|
||||||
use platform::font::FontHandle;
|
|
||||||
use platform::font_context::FontContextHandle;
|
use platform::font_context::FontContextHandle;
|
||||||
|
use style::computed_values::font_style;
|
||||||
|
|
||||||
use azure::azure_hl::BackendType;
|
use font_cache_task::FontCacheTask;
|
||||||
use std::collections::hashmap::HashMap;
|
use font_template::FontTemplateDescriptor;
|
||||||
use servo_util::cache::{Cache, LRUCache};
|
use platform::font_template::FontTemplateData;
|
||||||
use servo_util::time::TimeProfilerChan;
|
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 std::cell::RefCell;
|
||||||
|
use sync::Arc;
|
||||||
|
|
||||||
/// Information needed to create a font context.
|
use azure::AzFloat;
|
||||||
#[deriving(Clone)]
|
use azure::azure_hl::BackendType;
|
||||||
pub struct FontContextInfo {
|
use azure::scaled_font::ScaledFont;
|
||||||
/// The painting backend we're using.
|
|
||||||
pub backend: BackendType,
|
|
||||||
|
|
||||||
/// Whether we need a font list.
|
#[cfg(target_os="linux")]
|
||||||
pub needs_font_list: bool,
|
#[cfg(target_os="android")]
|
||||||
|
use azure::scaled_font::FontData;
|
||||||
|
|
||||||
/// A channel up to the time profiler.
|
#[cfg(target_os="linux")]
|
||||||
pub time_profiler_chan: TimeProfilerChan,
|
#[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)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FontContextHandleMethods {
|
#[cfg(target_os="macos")]
|
||||||
fn create_font_from_identifier(&self, &str, Option<&UsedFontStyle>) -> Result<FontHandle, ()>;
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 struct FontContext {
|
||||||
pub instance_cache: LRUCache<FontDescriptor, Rc<RefCell<Font>>>,
|
platform_handle: FontContextHandle,
|
||||||
pub font_list: Option<FontList>, // only needed by layout
|
font_cache_task: FontCacheTask,
|
||||||
pub group_cache: LRUCache<SpecifiedFontStyle, Rc<RefCell<FontGroup>>>,
|
|
||||||
pub handle: FontContextHandle,
|
/// Weak reference as the layout FontContext is persistent.
|
||||||
pub backend: BackendType,
|
layout_font_cache: Vec<Weak<RefCell<Font>>>,
|
||||||
pub generic_fonts: HashMap<String,String>,
|
|
||||||
pub time_profiler_chan: TimeProfilerChan,
|
/// 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 {
|
impl FontContext {
|
||||||
pub fn new(info: FontContextInfo) -> FontContext {
|
pub fn new(font_cache_task: FontCacheTask) -> FontContext {
|
||||||
let handle = FontContextHandle::new();
|
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 {
|
FontContext {
|
||||||
instance_cache: LRUCache::new(10),
|
platform_handle: handle,
|
||||||
font_list: font_list,
|
font_cache_task: font_cache_task,
|
||||||
group_cache: LRUCache::new(10),
|
layout_font_cache: vec!(),
|
||||||
|
render_font_cache: vec!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a font for use in layout calculations.
|
||||||
|
fn create_layout_font(&self, template: Arc<FontTemplateData>,
|
||||||
|
descriptor: FontTemplateDescriptor, pt_size: f64) -> Font {
|
||||||
|
|
||||||
|
let handle: FontHandle = FontHandleMethods::new_from_template(&self.platform_handle, template, Some(pt_size)).unwrap();
|
||||||
|
let metrics = handle.get_metrics();
|
||||||
|
|
||||||
|
Font {
|
||||||
handle: handle,
|
handle: handle,
|
||||||
backend: info.backend,
|
shaper: None,
|
||||||
generic_fonts: generic_fonts,
|
descriptor: descriptor,
|
||||||
time_profiler_chan: info.time_profiler_chan.clone(),
|
pt_size: pt_size,
|
||||||
|
metrics: metrics,
|
||||||
|
shape_cache: HashCache::new(),
|
||||||
|
glyph_advance_cache: HashCache::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_resolved_font_for_style(&mut self, style: &SpecifiedFontStyle)
|
/// Create a group of fonts for use in layout calculations. May return
|
||||||
-> Rc<RefCell<FontGroup>> {
|
/// a cached font if this font instance has already been used by
|
||||||
match self.group_cache.find(style) {
|
/// this context.
|
||||||
Some(fg) => {
|
pub fn get_layout_font_group_for_style(&mut self, style: &SpecifiedFontStyle) -> FontGroup {
|
||||||
debug!("font group cache hit");
|
// Remove all weak pointers that have been dropped.
|
||||||
fg
|
self.layout_font_cache.retain(|maybe_font| {
|
||||||
},
|
maybe_font.upgrade().is_some()
|
||||||
None => {
|
});
|
||||||
debug!("font group cache miss");
|
|
||||||
let fg = self.create_font_group(style);
|
|
||||||
self.group_cache.insert(style.clone(), fg.clone());
|
|
||||||
fg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_font_by_descriptor(&mut self, desc: &FontDescriptor)
|
let mut fonts: Vec<Rc<RefCell<Font>>> = vec!();
|
||||||
-> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_font_group(&mut self, style: &SpecifiedFontStyle) -> Rc<RefCell<FontGroup>> {
|
|
||||||
let mut fonts = vec!();
|
|
||||||
|
|
||||||
debug!("(create font group) --- starting ---");
|
|
||||||
|
|
||||||
// TODO(Issue #193): make iteration over 'font-family' more robust.
|
|
||||||
for family in style.families.iter() {
|
for family in style.families.iter() {
|
||||||
let transformed_family_name = self.transform_family(family);
|
let desc = FontTemplateDescriptor::new(style.weight, style.style == font_style::italic);
|
||||||
debug!("(create font group) transformed family is `{:s}`", transformed_family_name);
|
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
let result = match self.font_list {
|
// GWTODO: Check on real pages if this is faster as Vec() or HashMap().
|
||||||
Some(ref mut fl) => {
|
let mut cache_hit = false;
|
||||||
let font_in_family = fl.find_font_in_family(&self.handle, &transformed_family_name, style);
|
for maybe_cached_font in self.layout_font_cache.iter() {
|
||||||
match font_in_family {
|
let cached_font = maybe_cached_font.upgrade().unwrap();
|
||||||
Some(font_entry) => {
|
if cached_font.borrow().descriptor == desc {
|
||||||
let font_id =
|
fonts.push(cached_font.clone());
|
||||||
SelectorPlatformIdentifier(font_entry.handle.face_identifier());
|
cache_hit = true;
|
||||||
let font_desc = FontDescriptor::new((*style).clone(), font_id);
|
break;
|
||||||
Some(font_desc)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 {
|
if !cache_hit {
|
||||||
debug!("(create font group) didn't find `{:s}`", transformed_family_name);
|
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 {
|
FontGroup::new(fonts)
|
||||||
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)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_font_instance(&self, desc: &FontDescriptor) -> Result<Rc<RefCell<Font>>, ()> {
|
/// Create a render font for use with azure. May return a cached
|
||||||
return match &desc.selector {
|
/// reference if already used by this font context.
|
||||||
// TODO(Issue #174): implement by-platform-name font selectors.
|
pub fn get_render_font_from_template(&mut self, template: &Arc<FontTemplateData>, pt_size: f64, backend: BackendType) -> Rc<RefCell<ScaledFont>> {
|
||||||
&SelectorPlatformIdentifier(ref identifier) => {
|
for cached_font in self.render_font_cache.iter() {
|
||||||
let result_handle = self.handle.create_font_from_identifier(identifier.as_slice(),
|
if cached_font.pt_size == pt_size &&
|
||||||
Some(&desc.style));
|
cached_font.identifier == template.identifier {
|
||||||
result_handle.and_then(|handle| {
|
return cached_font.font.clone();
|
||||||
Ok(
|
|
||||||
Rc::new(
|
|
||||||
RefCell::new(
|
|
||||||
Font::new_from_adopted_handle(self,
|
|
||||||
handle,
|
|
||||||
&desc.style,
|
|
||||||
self.backend))))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
115
src/components/gfx/font_template.rs
Normal file
115
src/components/gfx/font_template.rs
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,12 +45,6 @@ extern crate harfbuzz;
|
||||||
#[cfg(target_os="macos")] extern crate core_graphics;
|
#[cfg(target_os="macos")] extern crate core_graphics;
|
||||||
#[cfg(target_os="macos")] extern crate core_text;
|
#[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;
|
pub use render_context::RenderContext;
|
||||||
|
|
||||||
// Private rendering modules
|
// Private rendering modules
|
||||||
|
@ -65,7 +59,8 @@ pub mod render_task;
|
||||||
// Fonts
|
// Fonts
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod font_context;
|
pub mod font_context;
|
||||||
pub mod font_list;
|
pub mod font_cache_task;
|
||||||
|
pub mod font_template;
|
||||||
|
|
||||||
// Misc.
|
// Misc.
|
||||||
mod buffer_map;
|
mod buffer_map;
|
||||||
|
|
|
@ -5,17 +5,18 @@
|
||||||
extern crate freetype;
|
extern crate freetype;
|
||||||
|
|
||||||
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
|
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
|
||||||
use font::{FontTableTag, FractionalPixel, SpecifiedFontStyle};
|
use font::{FontTableTag, FractionalPixel};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::geometry;
|
use servo_util::geometry;
|
||||||
use platform::font_context::FontContextHandle;
|
use platform::font_context::FontContextHandle;
|
||||||
use text::glyph::GlyphId;
|
use text::glyph::GlyphId;
|
||||||
use text::util::{float_to_fixed, fixed_to_float};
|
use text::util::{float_to_fixed, fixed_to_float};
|
||||||
use style::computed_values::font_weight;
|
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_Get_Char_Index, FT_Get_Postscript_Name};
|
||||||
use freetype::freetype::{FT_Load_Glyph, FT_Set_Char_Size};
|
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::{FT_New_Memory_Face, FT_Done_Face};
|
||||||
use freetype::freetype::{FTErrorMethods, FT_F26Dot6, FT_Face, FT_FaceRec};
|
use freetype::freetype::{FTErrorMethods, FT_F26Dot6, FT_Face, FT_FaceRec};
|
||||||
use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong};
|
use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong};
|
||||||
|
@ -28,6 +29,8 @@ use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use sync::Arc;
|
||||||
|
|
||||||
fn float_to_fixed_ft(f: f64) -> i32 {
|
fn float_to_fixed_ft(f: f64) -> i32 {
|
||||||
float_to_fixed(6, f)
|
float_to_fixed(6, f)
|
||||||
}
|
}
|
||||||
|
@ -37,7 +40,7 @@ fn fixed_to_float_ft(f: i32) -> f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FontTable {
|
pub struct FontTable {
|
||||||
bogus: ()
|
_bogus: ()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontTableMethods for FontTable {
|
impl FontTableMethods for FontTable {
|
||||||
|
@ -46,15 +49,10 @@ impl FontTableMethods for FontTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FontSource {
|
|
||||||
FontSourceMem(Vec<u8>),
|
|
||||||
FontSourceFile(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FontHandle {
|
pub struct FontHandle {
|
||||||
// The font binary. This must stay valid for the lifetime of the font,
|
// The font binary. This must stay valid for the lifetime of the font,
|
||||||
// if the font is created using FT_Memory_Face.
|
// if the font is created using FT_Memory_Face.
|
||||||
pub source: FontSource,
|
pub font_data: Arc<FontTemplateData>,
|
||||||
pub face: FT_Face,
|
pub face: FT_Face,
|
||||||
pub handle: FontContextHandle
|
pub handle: FontContextHandle
|
||||||
}
|
}
|
||||||
|
@ -72,14 +70,15 @@ impl Drop for FontHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontHandleMethods for FontHandle {
|
impl FontHandleMethods for FontHandle {
|
||||||
fn new_from_buffer(fctx: &FontContextHandle,
|
fn new_from_template(fctx: &FontContextHandle,
|
||||||
buf: Vec<u8>,
|
template: Arc<FontTemplateData>,
|
||||||
style: &SpecifiedFontStyle)
|
pt_size: Option<f64>)
|
||||||
-> Result<FontHandle, ()> {
|
-> Result<FontHandle, ()> {
|
||||||
let ft_ctx: FT_Library = fctx.ctx.ctx;
|
let ft_ctx: FT_Library = fctx.ctx.ctx;
|
||||||
if ft_ctx.is_null() { return Err(()); }
|
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
|
// TODO: this could be more simply written as result::chain
|
||||||
// and moving buf into the struct ctor, but cant' move out of
|
// and moving buf into the struct ctor, but cant' move out of
|
||||||
|
@ -88,7 +87,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
Ok(face) => {
|
Ok(face) => {
|
||||||
let handle = FontHandle {
|
let handle = FontHandle {
|
||||||
face: face,
|
face: face,
|
||||||
source: FontSourceMem(buf),
|
font_data: template.clone(),
|
||||||
handle: fctx.clone()
|
handle: fctx.clone()
|
||||||
};
|
};
|
||||||
Ok(handle)
|
Ok(handle)
|
||||||
|
@ -96,7 +95,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
Err(()) => Err(())
|
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, ()> {
|
-> Result<FT_Face, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut face: FT_Face = ptr::null();
|
let mut face: FT_Face = ptr::null();
|
||||||
|
@ -107,7 +106,11 @@ impl FontHandleMethods for FontHandle {
|
||||||
if !result.succeeded() || face.is_null() {
|
if !result.succeeded() || face.is_null() {
|
||||||
return Err(());
|
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)
|
Ok(face)
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
|
@ -115,14 +118,8 @@ impl FontHandleMethods for FontHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn get_template(&self) -> Arc<FontTemplateData> {
|
||||||
// an identifier usable by FontContextHandle to recreate this FontHandle.
|
self.font_data.clone()
|
||||||
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 family_name(&self) -> String {
|
fn family_name(&self) -> String {
|
||||||
unsafe { str::raw::from_c_str((*self.face).family_name) }
|
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 {
|
fn get_face_rec(&'a self) -> &'a FT_FaceRec {
|
||||||
unsafe {
|
unsafe {
|
||||||
&(*self.face)
|
&(*self.face)
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::FTErrorMethods;
|
||||||
use freetype::freetype::FT_Add_Default_Modules;
|
use freetype::freetype::FT_Add_Default_Modules;
|
||||||
use freetype::freetype::FT_Done_FreeType;
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
debug!("getting variations for {}", family_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let config = FcConfigGetCurrent();
|
let config = FcConfigGetCurrent();
|
||||||
|
|
28
src/components/gfx/platform/android/font_template.rs
Normal file
28
src/components/gfx/platform/android/font_template.rs
Normal file
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,17 +5,18 @@
|
||||||
extern crate freetype;
|
extern crate freetype;
|
||||||
|
|
||||||
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
|
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
|
||||||
use font::{FontTableTag, FractionalPixel, SpecifiedFontStyle};
|
use font::{FontTableTag, FractionalPixel};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::geometry;
|
use servo_util::geometry;
|
||||||
use platform::font_context::FontContextHandle;
|
use platform::font_context::FontContextHandle;
|
||||||
use text::glyph::GlyphId;
|
use text::glyph::GlyphId;
|
||||||
use text::util::{float_to_fixed, fixed_to_float};
|
use text::util::{float_to_fixed, fixed_to_float};
|
||||||
use style::computed_values::font_weight;
|
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_Get_Char_Index, FT_Get_Postscript_Name};
|
||||||
use freetype::freetype::{FT_Load_Glyph, FT_Set_Char_Size};
|
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::{FT_New_Memory_Face, FT_Done_Face};
|
||||||
use freetype::freetype::{FTErrorMethods, FT_F26Dot6, FT_Face, FT_FaceRec};
|
use freetype::freetype::{FTErrorMethods, FT_F26Dot6, FT_Face, FT_FaceRec};
|
||||||
use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong};
|
use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong};
|
||||||
|
@ -28,6 +29,8 @@ use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use sync::Arc;
|
||||||
|
|
||||||
fn float_to_fixed_ft(f: f64) -> i32 {
|
fn float_to_fixed_ft(f: f64) -> i32 {
|
||||||
float_to_fixed(6, f)
|
float_to_fixed(6, f)
|
||||||
}
|
}
|
||||||
|
@ -46,15 +49,10 @@ impl FontTableMethods for FontTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FontSource {
|
|
||||||
FontSourceMem(Vec<u8>),
|
|
||||||
FontSourceFile(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FontHandle {
|
pub struct FontHandle {
|
||||||
// The font binary. This must stay valid for the lifetime of the font,
|
// The font binary. This must stay valid for the lifetime of the font,
|
||||||
// if the font is created using FT_Memory_Face.
|
// if the font is created using FT_Memory_Face.
|
||||||
pub source: FontSource,
|
pub font_data: Arc<FontTemplateData>,
|
||||||
pub face: FT_Face,
|
pub face: FT_Face,
|
||||||
pub handle: FontContextHandle
|
pub handle: FontContextHandle
|
||||||
}
|
}
|
||||||
|
@ -72,14 +70,15 @@ impl Drop for FontHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontHandleMethods for FontHandle {
|
impl FontHandleMethods for FontHandle {
|
||||||
fn new_from_buffer(fctx: &FontContextHandle,
|
fn new_from_template(fctx: &FontContextHandle,
|
||||||
buf: Vec<u8>,
|
template: Arc<FontTemplateData>,
|
||||||
style: &SpecifiedFontStyle)
|
pt_size: Option<f64>)
|
||||||
-> Result<FontHandle, ()> {
|
-> Result<FontHandle, ()> {
|
||||||
let ft_ctx: FT_Library = fctx.ctx.ctx;
|
let ft_ctx: FT_Library = fctx.ctx.ctx;
|
||||||
if ft_ctx.is_null() { return Err(()); }
|
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
|
// TODO: this could be more simply written as result::chain
|
||||||
// and moving buf into the struct ctor, but cant' move out of
|
// and moving buf into the struct ctor, but cant' move out of
|
||||||
|
@ -88,7 +87,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
Ok(face) => {
|
Ok(face) => {
|
||||||
let handle = FontHandle {
|
let handle = FontHandle {
|
||||||
face: face,
|
face: face,
|
||||||
source: FontSourceMem(buf),
|
font_data: template.clone(),
|
||||||
handle: fctx.clone()
|
handle: fctx.clone()
|
||||||
};
|
};
|
||||||
Ok(handle)
|
Ok(handle)
|
||||||
|
@ -96,7 +95,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
Err(()) => Err(())
|
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, ()> {
|
-> Result<FT_Face, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut face: FT_Face = ptr::null();
|
let mut face: FT_Face = ptr::null();
|
||||||
|
@ -107,7 +106,11 @@ impl FontHandleMethods for FontHandle {
|
||||||
if !result.succeeded() || face.is_null() {
|
if !result.succeeded() || face.is_null() {
|
||||||
return Err(());
|
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)
|
Ok(face)
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
|
@ -115,14 +118,8 @@ impl FontHandleMethods for FontHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn get_template(&self) -> Arc<FontTemplateData> {
|
||||||
// an identifier usable by FontContextHandle to recreate this FontHandle.
|
self.font_data.clone()
|
||||||
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 family_name(&self) -> String {
|
fn family_name(&self) -> String {
|
||||||
unsafe { str::raw::from_c_str((*self.face).family_name) }
|
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 {
|
fn get_face_rec(&'a self) -> &'a FT_FaceRec {
|
||||||
unsafe {
|
unsafe {
|
||||||
&(*self.face)
|
&(*self.face)
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::FTErrorMethods;
|
||||||
use freetype::freetype::FT_Add_Default_Modules;
|
use freetype::freetype::FT_Add_Default_Modules;
|
||||||
use freetype::freetype::FT_Done_FreeType;
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
debug!("getting variations for {}", family_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let config = FcConfigGetCurrent();
|
let config = FcConfigGetCurrent();
|
||||||
|
|
28
src/components/gfx/platform/linux/font_template.rs
Normal file
28
src/components/gfx/platform/linux/font_template.rs
Normal file
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,25 +10,25 @@ extern crate core_text;
|
||||||
|
|
||||||
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
|
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
|
||||||
use font::FontTableTag;
|
use font::FontTableTag;
|
||||||
use font::{FractionalPixel, SpecifiedFontStyle};
|
use font::FractionalPixel;
|
||||||
use servo_util::geometry::{Au, px_to_pt};
|
use servo_util::geometry::{Au, px_to_pt};
|
||||||
use servo_util::geometry;
|
use servo_util::geometry;
|
||||||
use platform::macos::font_context::FontContextHandle;
|
use platform::macos::font_context::FontContextHandle;
|
||||||
use text::glyph::GlyphId;
|
use text::glyph::GlyphId;
|
||||||
use style::computed_values::font_weight;
|
use style::computed_values::font_weight;
|
||||||
|
use platform::font_template::FontTemplateData;
|
||||||
|
|
||||||
use core_foundation::base::CFIndex;
|
use core_foundation::base::CFIndex;
|
||||||
use core_foundation::data::CFData;
|
use core_foundation::data::CFData;
|
||||||
use core_foundation::string::UniChar;
|
use core_foundation::string::UniChar;
|
||||||
use core_graphics::data_provider::CGDataProvider;
|
use core_graphics::font::CGGlyph;
|
||||||
use core_graphics::font::{CGFont, CGGlyph};
|
|
||||||
use core_graphics::geometry::CGRect;
|
use core_graphics::geometry::CGRect;
|
||||||
use core_text::font::CTFont;
|
use core_text::font::CTFont;
|
||||||
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
|
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
|
||||||
use core_text::font_descriptor::{kCTFontDefaultOrientation};
|
use core_text::font_descriptor::{kCTFontDefaultOrientation};
|
||||||
use core_text;
|
|
||||||
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use sync::Arc;
|
||||||
|
|
||||||
pub struct FontTable {
|
pub struct FontTable {
|
||||||
data: CFData,
|
data: CFData,
|
||||||
|
@ -52,43 +52,30 @@ impl FontTableMethods for FontTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FontHandle {
|
pub struct FontHandle {
|
||||||
cgfont: Option<CGFont>,
|
pub font_data: Arc<FontTemplateData>,
|
||||||
pub ctfont: CTFont,
|
pub ctfont: CTFont,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontHandle {
|
impl FontHandleMethods for FontHandle {
|
||||||
pub fn new_from_CTFont(_: &FontContextHandle, ctfont: CTFont) -> Result<FontHandle, ()> {
|
fn new_from_template(_fctx: &FontContextHandle,
|
||||||
Ok(FontHandle {
|
template: Arc<FontTemplateData>,
|
||||||
cgfont: None,
|
pt_size: Option<f64>)
|
||||||
ctfont: ctfont,
|
-> 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 {
|
fn get_template(&self) -> Arc<FontTemplateData> {
|
||||||
match self.cgfont {
|
self.font_data.clone()
|
||||||
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 family_name(&self) -> String {
|
fn family_name(&self) -> String {
|
||||||
|
@ -182,9 +169,5 @@ impl FontHandleMethods for FontHandle {
|
||||||
Some(FontTable::wrap(data))
|
Some(FontTable::wrap(data))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn face_identifier(&self) -> String {
|
|
||||||
self.ctfont.postscript_name()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,6 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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)]
|
#[deriving(Clone)]
|
||||||
pub struct FontContextHandle {
|
pub struct FontContextHandle {
|
||||||
ctx: ()
|
ctx: ()
|
||||||
|
@ -20,19 +14,3 @@ impl FontContextHandle {
|
||||||
FontContextHandle { ctx: () }
|
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
debug!("Looking for faces of family: {:s}", family_name);
|
||||||
|
|
||||||
let family_collection =
|
let family_collection =
|
||||||
|
|
25
src/components/gfx/platform/macos/font_template.rs
Normal file
25
src/components/gfx/platform/macos/font_template.rs
Normal file
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,16 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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="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};
|
#[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};
|
#[cfg(target_os="android")] pub use platform::android::{font, font_context, font_list, font_template};
|
||||||
|
|
||||||
#[cfg(target_os="linux")]
|
#[cfg(target_os="linux")]
|
||||||
pub mod linux {
|
pub mod linux {
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod font_context;
|
pub mod font_context;
|
||||||
pub mod font_list;
|
pub mod font_list;
|
||||||
|
pub mod font_template;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os="macos")]
|
#[cfg(target_os="macos")]
|
||||||
|
@ -18,6 +19,7 @@ pub mod macos {
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod font_context;
|
pub mod font_context;
|
||||||
pub mod font_list;
|
pub mod font_list;
|
||||||
|
pub mod font_template;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os="android")]
|
#[cfg(target_os="android")]
|
||||||
|
@ -25,4 +27,5 @@ pub mod android {
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod font_context;
|
pub mod font_context;
|
||||||
pub mod font_list;
|
pub mod font_list;
|
||||||
|
pub mod font_template;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
use buffer_map::BufferMap;
|
use buffer_map::BufferMap;
|
||||||
use display_list::optimizer::DisplayListOptimizer;
|
use display_list::optimizer::DisplayListOptimizer;
|
||||||
use display_list::DisplayList;
|
use display_list::DisplayList;
|
||||||
use font_context::{FontContext, FontContextInfo};
|
use font_context::FontContext;
|
||||||
use render_context::RenderContext;
|
use render_context::RenderContext;
|
||||||
|
|
||||||
use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
|
use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
|
||||||
|
@ -34,6 +34,7 @@ use servo_util::time::{TimeProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
use std::comm::{Receiver, Sender, channel};
|
use std::comm::{Receiver, Sender, channel};
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
|
use font_cache_task::FontCacheTask;
|
||||||
|
|
||||||
/// Information about a layer that layout sends to the painting task.
|
/// Information about a layer that layout sends to the painting task.
|
||||||
pub struct RenderLayer {
|
pub struct RenderLayer {
|
||||||
|
@ -144,12 +145,15 @@ impl<C:RenderListener + Send> RenderTask<C> {
|
||||||
port: Receiver<Msg>,
|
port: Receiver<Msg>,
|
||||||
compositor: C,
|
compositor: C,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
|
font_cache_task: FontCacheTask,
|
||||||
failure_msg: Failure,
|
failure_msg: Failure,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
shutdown_chan: Sender<()>) {
|
shutdown_chan: Sender<()>) {
|
||||||
|
|
||||||
let ConstellationChan(c) = constellation_chan.clone();
|
let ConstellationChan(c) = constellation_chan.clone();
|
||||||
|
let fc = font_cache_task.clone();
|
||||||
|
|
||||||
let mut task_opts = TaskOpts::new();
|
let mut task_opts = TaskOpts::new();
|
||||||
task_opts.name = Some("RenderTask".into_maybe_owned());
|
task_opts.name = Some("RenderTask".into_maybe_owned());
|
||||||
task_opts.on_exit = Some(proc(result: task::Result) {
|
task_opts.on_exit = Some(proc(result: task::Result) {
|
||||||
|
@ -172,11 +176,7 @@ impl<C:RenderListener + Send> RenderTask<C> {
|
||||||
port: port,
|
port: port,
|
||||||
compositor: compositor,
|
compositor: compositor,
|
||||||
constellation_chan: constellation_chan,
|
constellation_chan: constellation_chan,
|
||||||
font_ctx: box FontContext::new(FontContextInfo {
|
font_ctx: box FontContext::new(fc.clone()),
|
||||||
backend: opts.render_backend.clone(),
|
|
||||||
needs_font_list: false,
|
|
||||||
time_profiler_chan: time_profiler_chan.clone(),
|
|
||||||
}),
|
|
||||||
opts: opts,
|
opts: opts,
|
||||||
time_profiler_chan: time_profiler_chan,
|
time_profiler_chan: time_profiler_chan,
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@ impl Shaper {
|
||||||
let hb_font: *hb_font_t = hb_font_create(hb_face);
|
let hb_font: *hb_font_t = hb_font_create(hb_face);
|
||||||
|
|
||||||
// Set points-per-em. if zero, performs no hinting in that direction.
|
// 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);
|
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.
|
// Set scaling. Note that this takes 16.16 fixed point.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::geometry::Au;
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
use servo_util::vec::{Comparator, FullBinarySearchMethods};
|
use servo_util::vec::{Comparator, FullBinarySearchMethods};
|
||||||
|
@ -10,14 +10,16 @@ use std::slice::Items;
|
||||||
use style::computed_values::text_decoration;
|
use style::computed_values::text_decoration;
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
use text::glyph::{CharIndex, GlyphStore};
|
use text::glyph::{CharIndex, GlyphStore};
|
||||||
|
use font::FontHandleMethods;
|
||||||
|
use platform::font_template::FontTemplateData;
|
||||||
|
|
||||||
/// A single "paragraph" of text in one font size and style.
|
/// A single "paragraph" of text in one font size and style.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct TextRun {
|
pub struct TextRun {
|
||||||
pub text: Arc<String>,
|
pub text: Arc<String>,
|
||||||
pub font_descriptor: FontDescriptor,
|
pub font_template: Arc<FontTemplateData>,
|
||||||
|
pub pt_size: f64,
|
||||||
pub font_metrics: FontMetrics,
|
pub font_metrics: FontMetrics,
|
||||||
pub font_style: FontStyle,
|
|
||||||
pub decoration: text_decoration::T,
|
pub decoration: text_decoration::T,
|
||||||
/// The glyph runs that make up this text run.
|
/// The glyph runs that make up this text run.
|
||||||
pub glyphs: Arc<Vec<GlyphRun>>,
|
pub glyphs: Arc<Vec<GlyphRun>>,
|
||||||
|
@ -119,12 +121,11 @@ impl<'a> Iterator<Range<CharIndex>> for LineIterator<'a> {
|
||||||
impl<'a> TextRun {
|
impl<'a> TextRun {
|
||||||
pub fn new(font: &mut Font, text: String, decoration: text_decoration::T) -> 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 glyphs = TextRun::break_and_shape(font, text.as_slice());
|
||||||
|
|
||||||
let run = TextRun {
|
let run = TextRun {
|
||||||
text: Arc::new(text),
|
text: Arc::new(text),
|
||||||
font_style: font.style.clone(),
|
|
||||||
font_metrics: font.metrics.clone(),
|
font_metrics: font.metrics.clone(),
|
||||||
font_descriptor: font.get_descriptor(),
|
font_template: font.handle.get_template(),
|
||||||
|
pt_size: font.pt_size,
|
||||||
decoration: decoration,
|
decoration: decoration,
|
||||||
glyphs: Arc::new(glyphs),
|
glyphs: Arc::new(glyphs),
|
||||||
};
|
};
|
||||||
|
@ -133,7 +134,6 @@ impl<'a> TextRun {
|
||||||
|
|
||||||
pub fn break_and_shape(font: &mut Font, text: &str) -> Vec<GlyphRun> {
|
pub fn break_and_shape(font: &mut Font, text: &str) -> Vec<GlyphRun> {
|
||||||
// TODO(Issue #230): do a better job. See Gecko's LineBreaker.
|
// TODO(Issue #230): do a better job. See Gecko's LineBreaker.
|
||||||
|
|
||||||
let mut glyphs = vec!();
|
let mut glyphs = vec!();
|
||||||
let (mut byte_i, mut char_i) = (0u, CharIndex(0));
|
let (mut byte_i, mut char_i) = (0u, CharIndex(0));
|
||||||
let mut cur_slice_is_whitespace = false;
|
let mut cur_slice_is_whitespace = false;
|
||||||
|
|
|
@ -9,7 +9,8 @@ use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::display_list::OpaqueNode;
|
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"))]
|
#[cfg(not(target_os="android"))]
|
||||||
use green::task::GreenTask;
|
use green::task::GreenTask;
|
||||||
use script::layout_interface::LayoutChan;
|
use script::layout_interface::LayoutChan;
|
||||||
|
@ -67,8 +68,8 @@ pub struct LayoutContext {
|
||||||
/// A channel up to the layout task.
|
/// A channel up to the layout task.
|
||||||
pub layout_chan: LayoutChan,
|
pub layout_chan: LayoutChan,
|
||||||
|
|
||||||
/// Information needed to construct a font context.
|
/// Interface to the font cache task.
|
||||||
pub font_context_info: FontContextInfo,
|
pub font_cache_task: FontCacheTask,
|
||||||
|
|
||||||
/// The CSS selector stylist.
|
/// The CSS selector stylist.
|
||||||
///
|
///
|
||||||
|
@ -105,7 +106,7 @@ impl LayoutContext {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if FONT_CONTEXT == ptr::mut_null() {
|
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)
|
FONT_CONTEXT = mem::transmute(context)
|
||||||
}
|
}
|
||||||
mem::transmute(FONT_CONTEXT)
|
mem::transmute(FONT_CONTEXT)
|
||||||
|
@ -172,7 +173,7 @@ impl LayoutContext {
|
||||||
match opt {
|
match opt {
|
||||||
Some(c) => context = mem::transmute(c),
|
Some(c) => context = mem::transmute(c),
|
||||||
None => {
|
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));
|
font_context.replace(Some(context));
|
||||||
|
|
|
@ -27,7 +27,7 @@ use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem};
|
use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem};
|
||||||
use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode};
|
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::{RenderMsg, RenderChan, RenderLayer};
|
||||||
use gfx::{render_task, color};
|
use gfx::{render_task, color};
|
||||||
use script::dom::bindings::js::JS;
|
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::compositor_msg::Scrollable;
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
|
||||||
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
||||||
|
use gfx::font_cache_task::{FontCacheTask};
|
||||||
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
|
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::geometry;
|
use servo_util::geometry;
|
||||||
|
@ -84,6 +85,9 @@ pub struct LayoutTask {
|
||||||
/// The channel on which messages can be sent to the image cache.
|
/// The channel on which messages can be sent to the image cache.
|
||||||
pub image_cache_task: ImageCacheTask,
|
pub image_cache_task: ImageCacheTask,
|
||||||
|
|
||||||
|
/// Public interface to the font cache task.
|
||||||
|
pub font_cache_task: FontCacheTask,
|
||||||
|
|
||||||
/// The local image cache.
|
/// The local image cache.
|
||||||
pub local_image_cache: Arc<Mutex<LocalImageCache>>,
|
pub local_image_cache: Arc<Mutex<LocalImageCache>>,
|
||||||
|
|
||||||
|
@ -278,6 +282,7 @@ impl LayoutTask {
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
render_chan: RenderChan,
|
render_chan: RenderChan,
|
||||||
img_cache_task: ImageCacheTask,
|
img_cache_task: ImageCacheTask,
|
||||||
|
font_cache_task: FontCacheTask,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
shutdown_chan: Sender<()>) {
|
shutdown_chan: Sender<()>) {
|
||||||
|
@ -293,6 +298,7 @@ impl LayoutTask {
|
||||||
script_chan,
|
script_chan,
|
||||||
render_chan,
|
render_chan,
|
||||||
img_cache_task,
|
img_cache_task,
|
||||||
|
font_cache_task,
|
||||||
&opts,
|
&opts,
|
||||||
time_profiler_chan);
|
time_profiler_chan);
|
||||||
layout.start();
|
layout.start();
|
||||||
|
@ -309,6 +315,7 @@ impl LayoutTask {
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
render_chan: RenderChan,
|
render_chan: RenderChan,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
|
font_cache_task: FontCacheTask,
|
||||||
opts: &Opts,
|
opts: &Opts,
|
||||||
time_profiler_chan: TimeProfilerChan)
|
time_profiler_chan: TimeProfilerChan)
|
||||||
-> LayoutTask {
|
-> LayoutTask {
|
||||||
|
@ -328,6 +335,7 @@ impl LayoutTask {
|
||||||
script_chan: script_chan,
|
script_chan: script_chan,
|
||||||
render_chan: render_chan,
|
render_chan: render_chan,
|
||||||
image_cache_task: image_cache_task.clone(),
|
image_cache_task: image_cache_task.clone(),
|
||||||
|
font_cache_task: font_cache_task,
|
||||||
local_image_cache: local_image_cache,
|
local_image_cache: local_image_cache,
|
||||||
screen_size: screen_size,
|
screen_size: screen_size,
|
||||||
|
|
||||||
|
@ -349,18 +357,12 @@ impl LayoutTask {
|
||||||
|
|
||||||
// Create a layout context for use in building display lists, hit testing, &c.
|
// Create a layout context for use in building display lists, hit testing, &c.
|
||||||
fn build_layout_context(&self, reflow_root: &LayoutNode, url: &Url) -> LayoutContext {
|
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 {
|
LayoutContext {
|
||||||
image_cache: self.local_image_cache.clone(),
|
image_cache: self.local_image_cache.clone(),
|
||||||
screen_size: self.screen_size.clone(),
|
screen_size: self.screen_size.clone(),
|
||||||
constellation_chan: self.constellation_chan.clone(),
|
constellation_chan: self.constellation_chan.clone(),
|
||||||
layout_chan: self.chan.clone(),
|
layout_chan: self.chan.clone(),
|
||||||
font_context_info: font_context_info,
|
font_cache_task: self.font_cache_task.clone(),
|
||||||
stylist: &*self.stylist,
|
stylist: &*self.stylist,
|
||||||
url: (*url).clone(),
|
url: (*url).clone(),
|
||||||
reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root),
|
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
|
// FIXME(pcwalton): This is a pretty bogus thing to do. Essentially this is a workaround
|
||||||
// for libgreen having slow TLS.
|
// for libgreen having slow TLS.
|
||||||
let mut font_context_opt = if self.parallel_traversal.is_none() {
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
@ -142,8 +142,8 @@ impl TextRunScanner {
|
||||||
// TODO(#177): Text run creation must account for the renderability of text by
|
// 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
|
// 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.
|
// 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 fontgroup = font_context.get_layout_font_group_for_style(&font_style);
|
||||||
let run = box fontgroup.borrow().create_textrun(
|
let run = box fontgroup.create_textrun(
|
||||||
transformed_text.clone(), decoration);
|
transformed_text.clone(), decoration);
|
||||||
|
|
||||||
debug!("TextRunScanner: pushing single text fragment in range: {} ({})",
|
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.
|
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
||||||
let in_fragment = &in_fragments[self.clump.begin().to_uint()];
|
let in_fragment = &in_fragments[self.clump.begin().to_uint()];
|
||||||
let font_style = in_fragment.font_style();
|
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();
|
let decoration = in_fragment.text_decoration();
|
||||||
|
|
||||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||||
|
@ -218,7 +218,7 @@ impl TextRunScanner {
|
||||||
let clump = self.clump;
|
let clump = self.clump;
|
||||||
let run = if clump.length() != CharIndex(0) && run_str.len() > 0 {
|
let run = if clump.length() != CharIndex(0) && run_str.len() > 0 {
|
||||||
Some(Arc::new(box TextRun::new(
|
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)))
|
run_str.to_string(), decoration)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -258,8 +258,8 @@ impl TextRunScanner {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: &FontStyle)
|
pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: &FontStyle)
|
||||||
-> FontMetrics {
|
-> FontMetrics {
|
||||||
let fontgroup = font_context.get_resolved_font_for_style(font_style);
|
let fontgroup = font_context.get_layout_font_group_for_style(font_style);
|
||||||
fontgroup.borrow().fonts.get(0).borrow().metrics.clone()
|
fontgroup.fonts.get(0).borrow().metrics.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a computed style to a font style used for rendering.
|
/// Converts a computed style to a font style used for rendering.
|
||||||
|
|
|
@ -20,6 +20,7 @@ extern crate servo_msg = "msg";
|
||||||
#[phase(plugin, link)]
|
#[phase(plugin, link)]
|
||||||
extern crate servo_util = "util";
|
extern crate servo_util = "util";
|
||||||
extern crate green;
|
extern crate green;
|
||||||
|
extern crate gfx;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate native;
|
extern crate native;
|
||||||
extern crate rustrt;
|
extern crate rustrt;
|
||||||
|
@ -35,6 +36,8 @@ use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask};
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use servo_net::resource_task::ResourceTask;
|
use servo_net::resource_task::ResourceTask;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
use gfx::font_cache_task::FontCacheTask;
|
||||||
|
#[cfg(not(test))]
|
||||||
use servo_util::time::TimeProfiler;
|
use servo_util::time::TimeProfiler;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use servo_util::memory::MemoryProfiler;
|
use servo_util::memory::MemoryProfiler;
|
||||||
|
@ -115,10 +118,12 @@ pub fn run(opts: opts::Opts) {
|
||||||
} else {
|
} else {
|
||||||
ImageCacheTask(resource_task.clone())
|
ImageCacheTask(resource_task.clone())
|
||||||
};
|
};
|
||||||
|
let font_cache_task = FontCacheTask::new();
|
||||||
let constellation_chan = Constellation::start(compositor_chan,
|
let constellation_chan = Constellation::start(compositor_chan,
|
||||||
opts,
|
opts,
|
||||||
resource_task,
|
resource_task,
|
||||||
image_cache_task,
|
image_cache_task,
|
||||||
|
font_cache_task,
|
||||||
time_profiler_chan_clone);
|
time_profiler_chan_clone);
|
||||||
|
|
||||||
// Send the URL command to the constellation.
|
// Send the URL command to the constellation.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue