From 960cde1fbe0e956020fccf09a8177b6628446ea4 Mon Sep 17 00:00:00 2001 From: "Brian J. Burg" Date: Mon, 29 Oct 2012 12:05:58 -0700 Subject: [PATCH] Move around native font implementations and shapers. --- src/servo/servo.rc | 22 ++- .../native_font.rs} | 3 +- src/servo/text/harfbuzz/shaper.rs | 168 ++++++++++++++++++ src/servo/text/native_font.rs | 9 +- .../native_font.rs} | 5 +- src/servo/text/shaper.rs | 162 +---------------- src/servo/text/text_run.rs | 8 +- 7 files changed, 202 insertions(+), 175 deletions(-) rename src/servo/text/{native_font/ft_native_font.rs => freetype/native_font.rs} (99%) create mode 100644 src/servo/text/harfbuzz/shaper.rs rename src/servo/text/{native_font/quartz_native_font.rs => quartz/native_font.rs} (98%) diff --git a/src/servo/servo.rc b/src/servo/servo.rc index 0248aeb1571..b20c993816e 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -106,15 +106,25 @@ pub mod text { pub mod font; pub mod font_cache; pub mod glyph; - pub mod native_font { - #[cfg(target_os = "macos")] - pub mod quartz_native_font; - #[cfg(target_os = "linux")] - pub mod ft_native_font; - } + pub mod native_font; pub mod shaper; pub mod text_run; pub mod util; + + // platform and library-specific implementations. + pub mod harfbuzz { + pub mod shaper; + } + + #[cfg(target_os = "macos")] + pub mod quartz { + pub mod native_font; + } + + #[cfg(target_os = "linux")] + pub mod freetype { + pub mod native_font; + } } pub mod resource { diff --git a/src/servo/text/native_font/ft_native_font.rs b/src/servo/text/freetype/native_font.rs similarity index 99% rename from src/servo/text/native_font/ft_native_font.rs rename to src/servo/text/freetype/native_font.rs index b8e3e25b3b4..c07afb2972e 100644 --- a/src/servo/text/native_font/ft_native_font.rs +++ b/src/servo/text/freetype/native_font.rs @@ -1,4 +1,3 @@ - extern mod freetype; use font::{FontMetrics, FractionalPixel}; @@ -10,7 +9,7 @@ use vec_as_buf = vec::as_imm_buf; use ptr::{addr_of, null}; use cast::reinterpret_cast; use glyph::GlyphIndex; -use font::FontMetrics; + use freetype::{ FT_Error, FT_Library, FT_Face, FT_Long, FT_ULong, FT_Size, FT_SizeRec, FT_UInt, FT_GlyphSlot, FT_Size_Metrics, FT_FaceRec }; use freetype::bindgen::{ diff --git a/src/servo/text/harfbuzz/shaper.rs b/src/servo/text/harfbuzz/shaper.rs new file mode 100644 index 00000000000..61e1ca53345 --- /dev/null +++ b/src/servo/text/harfbuzz/shaper.rs @@ -0,0 +1,168 @@ +extern mod harfbuzz; + +use font::Font; +use font_cache::FontCache; + +use au = gfx::geometry; +use au::Au; +use geom::point::Point2D; +use glyph::{GlyphStore, GlyphIndex, GlyphData}; +use libc::types::common::c99::int32_t; +use libc::{c_uint, c_int, c_void, c_char}; +use ptr::{null, to_unsafe_ptr, offset}; +use std::arc; +use text_run::TextRun; +use util::*; + +use harfbuzz::{HB_MEMORY_MODE_READONLY, + HB_DIRECTION_LTR}; +use harfbuzz::{hb_blob_t, hb_face_t, hb_font_t, hb_font_funcs_t, hb_buffer_t, + hb_codepoint_t, hb_bool_t, hb_glyph_position_t, + hb_glyph_info_t, hb_var_int_t, hb_position_t}; +use harfbuzz::bindgen::{hb_blob_create, hb_blob_destroy, + hb_face_create, hb_face_destroy, + hb_font_create, hb_font_destroy, + hb_buffer_create, hb_buffer_destroy, + hb_buffer_add_utf8, hb_shape, + hb_buffer_get_glyph_infos, + hb_buffer_get_glyph_positions, + hb_font_set_ppem, hb_font_set_scale, + hb_buffer_set_direction, + hb_font_funcs_create, hb_font_funcs_destroy, + hb_font_set_funcs, + hb_font_funcs_set_glyph_h_advance_func, + hb_font_funcs_set_glyph_func, + hb_font_funcs_set_glyph_h_kerning_func}; + +fn float_to_fixed_hb(f: float) -> i32 { + util::float_to_fixed(16, f) +} + +fn fixed_to_float_hb(i: hb_position_t) -> float { + util::fixed_to_float(16, i) +} + +fn fixed_to_rounded_int_hb(f: hb_position_t) -> int { + util::fixed_to_rounded_int(16, f) +} + +pub struct HarfbuzzShaper { + dummy: int +} + +pub impl HarfbuzzShaper { + static pub fn new() -> HarfbuzzShaper { + HarfbuzzShaper { dummy: 42 } + } + + /** + Calculate the layout metrics associated with a some given text + when rendered in a specific font. + */ + pub fn shape_textrun(run: &TextRun) { + debug!("shaping text '%s'", run.text); + + // TODO: harfbuzz fonts and faces should be cached on the Font object. + // TODO: font tables should be stored in Font object and cached by FontCache (Issue #92) + let face_blob: *hb_blob_t = vec::as_imm_buf(*(run.font).fontbuf, |buf: *u8, len: uint| { + hb_blob_create(buf as *c_char, + len as c_uint, + HB_MEMORY_MODE_READONLY, + null(), + null()) + }); + + let hb_face: *hb_face_t = hb_face_create(face_blob, 0 as c_uint); + let hb_font: *hb_font_t = hb_font_create(hb_face); + + // TODO: set font size here, based on Font's size + // Set points-per-em. if zero, performs no hinting in that direction. + hb_font_set_ppem(hb_font, 21 as c_uint, 21 as c_uint); + // Set scaling. Note that this takes 16.16 fixed point. + hb_font_set_scale(hb_font, float_to_fixed_hb(21f) as c_int, float_to_fixed_hb(21f) as c_int); + + let funcs: *hb_font_funcs_t = hb_font_funcs_create(); + hb_font_funcs_set_glyph_func(funcs, glyph_func, null(), null()); + hb_font_funcs_set_glyph_h_advance_func(funcs, glyph_h_advance_func, null(), null()); + + unsafe { + let font_data: *c_void = core::ptr::addr_of(run.font) as *c_void; + hb_font_set_funcs(hb_font, funcs, font_data, null()); + }; + + let hb_buffer: *hb_buffer_t = hb_buffer_create(); + hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR); + + // Using as_buf because it never does a copy - we don't need the trailing null + str::as_buf(run.text, |ctext: *u8, _l: uint| { + hb_buffer_add_utf8(hb_buffer, + ctext as *c_char, + run.text.len() as c_int, + 0 as c_uint, + run.text.len() as c_int); + }); + + hb_shape(hb_font, hb_buffer, null(), 0 as c_uint); + + let info_buf_len = 0 as c_uint; + let info_buf = hb_buffer_get_glyph_infos(hb_buffer, to_unsafe_ptr(&info_buf_len)); + assert info_buf.is_not_null(); + let pos_buf_len = 0 as c_uint; + let pos_buf = hb_buffer_get_glyph_positions(hb_buffer, to_unsafe_ptr(&pos_buf_len)); + assert pos_buf.is_not_null(); + + assert info_buf_len == pos_buf_len; + + for uint::range(0u, info_buf_len as uint) |i| { unsafe { + let hb_info: hb_glyph_info_t = *offset(info_buf, i); + let hb_pos: hb_glyph_position_t = *offset(pos_buf, i); + let codepoint = hb_info.codepoint as GlyphIndex; + let advance: Au = au::from_frac_px(fixed_to_float_hb(hb_pos.x_advance)); + let offset = match (hb_pos.x_offset, hb_pos.y_offset) { + (0, 0) => None, + (x, y) => Some(Point2D(au::from_frac_px(fixed_to_float_hb(x)), + au::from_frac_px(fixed_to_float_hb(y)))) + }; + // TODO: convert pos.y_advance into offset adjustment + // TODO: handle multiple glyphs per char, ligatures, etc. + // See Issue # + debug!("glyph %?: index %?, advance %?, offset %?", + i, codepoint, advance, offset); + + let data = GlyphData(codepoint, advance, offset, false, false, false); + run.glyphs.add_glyph_for_index(i, &data); + } /* unsafe */ } + + hb_buffer_destroy(hb_buffer); + hb_font_funcs_destroy(funcs); + hb_font_destroy(hb_font); + hb_face_destroy(hb_face); + hb_blob_destroy(face_blob); + } +} + +extern fn glyph_func(_font: *hb_font_t, + font_data: *c_void, + unicode: hb_codepoint_t, + _variant_selector: hb_codepoint_t, + glyph: *mut hb_codepoint_t, + _user_data: *c_void) -> hb_bool_t unsafe { + + let font: *Font = font_data as *Font; + assert font.is_not_null(); + return match (*font).glyph_index(unicode as char) { + Some(g) => { *glyph = g as hb_codepoint_t; true }, + None => false + } as hb_bool_t; +} + +extern fn glyph_h_advance_func(_font: *hb_font_t, + font_data: *c_void, + glyph: hb_codepoint_t, + _user_data: *c_void) -> hb_position_t unsafe { + let font: *Font = font_data as *Font; + assert font.is_not_null(); + + let advance = (*font).glyph_h_advance(glyph as GlyphIndex); + float_to_fixed_hb(advance) +} diff --git a/src/servo/text/native_font.rs b/src/servo/text/native_font.rs index d59dbdf5e31..fe3d18356c6 100644 --- a/src/servo/text/native_font.rs +++ b/src/servo/text/native_font.rs @@ -5,22 +5,21 @@ needed by the text shaper as well as access to the underlying font resources needed by the graphics layer to draw glyphs. */ -use glyph::GlyphIndex; use font_cache::native::NativeFontCache; #[cfg(target_os = "macos")] -pub type NativeFont/& = quartz_native_font::QuartzNativeFont; +pub type NativeFont/& = quartz::native_font::QuartzNativeFont; #[cfg(target_os = "linux")] -pub type NativeFont/& = ft_native_font::FreeTypeNativeFont; +pub type NativeFont/& = freetype::native_font::FreeTypeNativeFont; // TODO: this should be part of trait NativeFont #[cfg(target_os = "macos")] pub fn create(native_lib: &NativeFontCache, buf: @~[u8], pt_size: float) -> Result { - quartz_native_font::create(native_lib, buf, pt_size) + quartz::native_font::create(native_lib, buf, pt_size) } #[cfg(target_os = "linux")] pub fn create(native_lib: &NativeFontCache, buf: @~[u8], pt_size: float) -> Result { - ft_native_font::create(native_lib, buf, pt_size) + freetype::native_font::create(native_lib, buf, pt_size) } diff --git a/src/servo/text/native_font/quartz_native_font.rs b/src/servo/text/quartz/native_font.rs similarity index 98% rename from src/servo/text/native_font/quartz_native_font.rs rename to src/servo/text/quartz/native_font.rs index bffd52b2291..d50133c703e 100644 --- a/src/servo/text/native_font/quartz_native_font.rs +++ b/src/servo/text/quartz/native_font.rs @@ -2,15 +2,14 @@ extern mod core_foundation; extern mod core_graphics; extern mod core_text; -export QuartzNativeFont, with_test_native_font, create; - use font::{FontMetrics, FractionalPixel}; +use font_cache::native::NativeFontCache; use au = gfx::geometry; use cast::transmute; +use glyph::GlyphIndex; use libc::size_t; use ptr::null; -use glyph::GlyphIndex; use cf = core_foundation; use cf::base::{ diff --git a/src/servo/text/shaper.rs b/src/servo/text/shaper.rs index 7e57b6e41ca..28587f3021e 100644 --- a/src/servo/text/shaper.rs +++ b/src/servo/text/shaper.rs @@ -1,160 +1,8 @@ -extern mod harfbuzz; - -use au = gfx::geometry; -use au::Au; -use core::num::from_int; -use font::Font; -use font_cache::FontCache; -use geom::point::Point2D; -use glyph::{GlyphStore, GlyphIndex, GlyphData}; -use libc::types::common::c99::int32_t; -use libc::{c_uint, c_int, c_void, c_char}; -use ptr::{null, to_unsafe_ptr, offset}; -use std::arc; -use text_run::TextRun; -use util::*; - - -use cast::reinterpret_cast; -use harfbuzz::{HB_MEMORY_MODE_READONLY, - HB_DIRECTION_LTR}; -use harfbuzz::{hb_blob_t, hb_face_t, hb_font_t, hb_font_funcs_t, hb_buffer_t, - hb_codepoint_t, hb_bool_t, hb_glyph_position_t, - hb_glyph_info_t, hb_var_int_t, hb_position_t}; -use harfbuzz::bindgen::{hb_blob_create, hb_blob_destroy, - hb_face_create, hb_face_destroy, - hb_font_create, hb_font_destroy, - hb_buffer_create, hb_buffer_destroy, - hb_buffer_add_utf8, hb_shape, - hb_buffer_get_glyph_infos, - hb_buffer_get_glyph_positions, - hb_font_set_ppem, hb_font_set_scale, - hb_buffer_set_direction, - hb_font_funcs_create, hb_font_funcs_destroy, - hb_font_set_funcs, - hb_font_funcs_set_glyph_h_advance_func, - hb_font_funcs_set_glyph_func, - hb_font_funcs_set_glyph_h_kerning_func}; - -fn float_to_fixed_hb(f: float) -> i32 { - util::float_to_fixed(16, f) -} - -fn fixed_to_float_hb(i: hb_position_t) -> float { - util::fixed_to_float(16, i) -} - -fn fixed_to_rounded_int_hb(f: hb_position_t) -> int { - util::fixed_to_rounded_int(16, f) -} - /** -Calculate the layout metrics associated with a some given text -when rendered in a specific font. +Shaper encapsulates a specific shaper, such as Harfbuzz, +Uniscribe, Pango, or Coretext. + +Currently, only harfbuzz bindings are implemented. */ -pub fn shape_textrun(run: &TextRun) { - debug!("shaping text '%s'", run.text); - // TODO: harfbuzz fonts and faces should be cached on the Font object. - // TODO: font tables should be stored in Font object and cached by FontCache (Issue #92) - let face_blob: *hb_blob_t = vec::as_imm_buf(*(run.font).fontbuf, |buf: *u8, len: uint| { - hb_blob_create(buf as *c_char, - len as c_uint, - HB_MEMORY_MODE_READONLY, - null(), - null()) - }); - - let hb_face: *hb_face_t = hb_face_create(face_blob, 0 as c_uint); - let hb_font: *hb_font_t = hb_font_create(hb_face); - - // TODO: set font size here, based on Font's size - // Set points-per-em. if zero, performs no hinting in that direction. - hb_font_set_ppem(hb_font, 21 as c_uint, 21 as c_uint); - // Set scaling. Note that this takes 16.16 fixed point. - hb_font_set_scale(hb_font, float_to_fixed_hb(21f) as c_int, float_to_fixed_hb(21f) as c_int); - - let funcs: *hb_font_funcs_t = hb_font_funcs_create(); - hb_font_funcs_set_glyph_func(funcs, glyph_func, null(), null()); - hb_font_funcs_set_glyph_h_advance_func(funcs, glyph_h_advance_func, null(), null()); - - unsafe { - let font_data: *c_void = core::ptr::addr_of(run.font) as *c_void; - hb_font_set_funcs(hb_font, funcs, font_data, null()); - }; - - let hb_buffer: *hb_buffer_t = hb_buffer_create(); - hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR); - - // Using as_buf because it never does a copy - we don't need the trailing null - str::as_buf(run.text, |ctext: *u8, _l: uint| { - hb_buffer_add_utf8(hb_buffer, - ctext as *c_char, - run.text.len() as c_int, - 0 as c_uint, - run.text.len() as c_int); - }); - - hb_shape(hb_font, hb_buffer, null(), 0 as c_uint); - - let info_buf_len = 0 as c_uint; - let info_buf = hb_buffer_get_glyph_infos(hb_buffer, to_unsafe_ptr(&info_buf_len)); - assert info_buf.is_not_null(); - let pos_buf_len = 0 as c_uint; - let pos_buf = hb_buffer_get_glyph_positions(hb_buffer, to_unsafe_ptr(&pos_buf_len)); - assert pos_buf.is_not_null(); - - assert info_buf_len == pos_buf_len; - - for uint::range(0u, info_buf_len as uint) |i| { unsafe { - let hb_info: hb_glyph_info_t = *offset(info_buf, i); - let hb_pos: hb_glyph_position_t = *offset(pos_buf, i); - let codepoint = hb_info.codepoint as GlyphIndex; - let advance: Au = au::from_frac_px(fixed_to_float_hb(hb_pos.x_advance)); - let offset = match (hb_pos.x_offset, hb_pos.y_offset) { - (0, 0) => None, - (x, y) => Some(Point2D(au::from_frac_px(fixed_to_float_hb(x)), - au::from_frac_px(fixed_to_float_hb(y)))) - }; - // TODO: convert pos.y_advance into offset adjustment - // TODO: handle multiple glyphs per char, ligatures, etc. - // See Issue # - debug!("glyph %?: index %?, advance %?, offset %?", - i, codepoint, advance, offset); - - let data = GlyphData(codepoint, advance, offset, false, false, false); - run.glyphs.add_glyph_for_index(i, &data); - } /* unsafe */ } - - hb_buffer_destroy(hb_buffer); - hb_font_funcs_destroy(funcs); - hb_font_destroy(hb_font); - hb_face_destroy(hb_face); - hb_blob_destroy(face_blob); -} - -extern fn glyph_func(_font: *hb_font_t, - font_data: *c_void, - unicode: hb_codepoint_t, - _variant_selector: hb_codepoint_t, - glyph: *mut hb_codepoint_t, - _user_data: *c_void) -> hb_bool_t unsafe { - - let font: *Font = font_data as *Font; - assert font.is_not_null(); - return match (*font).glyph_index(unicode as char) { - Some(g) => { *glyph = g as hb_codepoint_t; true }, - None => false - } as hb_bool_t; -} - -extern fn glyph_h_advance_func(_font: *hb_font_t, - font_data: *c_void, - glyph: hb_codepoint_t, - _user_data: *c_void) -> hb_position_t unsafe { - let font: *Font = font_data as *Font; - assert font.is_not_null(); - - let advance = (*font).glyph_h_advance(glyph as GlyphIndex); - float_to_fixed_hb(advance) -} +pub type Shaper/& = harfbuzz::shaper::HarfbuzzShaper; diff --git a/src/servo/text/text_run.rs b/src/servo/text/text_run.rs index 1ff24ca9c7b..a64501fad97 100644 --- a/src/servo/text/text_run.rs +++ b/src/servo/text/text_run.rs @@ -10,7 +10,6 @@ use glyph::GlyphStore; use layout::context::LayoutContext; use libc::{c_void}; use servo_util::color; -use shaper::shape_textrun; use std::arc; use servo_util::range::{Range, MutableRange}; @@ -41,6 +40,8 @@ impl SendableTextRun { impl TextRun { static fn new(font: @Font, text: ~str) -> TextRun { + use shaper::Shaper; + let glyph_store = GlyphStore(text.len()); let run = TextRun { text: move text, @@ -48,7 +49,10 @@ impl TextRun { glyphs: move glyph_store, }; - shape_textrun(&run); + // XXX wrong! use typedef + let shaper = harfbuzz::shaper::HarfbuzzShaper::new(); + // let shaper = Shaper::new(); + shaper.shape_textrun(&run); return move run; }