Move around native font implementations and shapers.

This commit is contained in:
Brian J. Burg 2012-10-29 12:05:58 -07:00
parent dbfe0869e2
commit 960cde1fbe
7 changed files with 202 additions and 175 deletions

View file

@ -106,15 +106,25 @@ pub mod text {
pub mod font; pub mod font;
pub mod font_cache; pub mod font_cache;
pub mod glyph; pub mod glyph;
pub mod native_font { pub mod native_font;
#[cfg(target_os = "macos")]
pub mod quartz_native_font;
#[cfg(target_os = "linux")]
pub mod ft_native_font;
}
pub mod shaper; pub mod shaper;
pub mod text_run; pub mod text_run;
pub mod util; 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 { pub mod resource {

View file

@ -1,4 +1,3 @@
extern mod freetype; extern mod freetype;
use font::{FontMetrics, FractionalPixel}; use font::{FontMetrics, FractionalPixel};
@ -10,7 +9,7 @@ use vec_as_buf = vec::as_imm_buf;
use ptr::{addr_of, null}; use ptr::{addr_of, null};
use cast::reinterpret_cast; use cast::reinterpret_cast;
use glyph::GlyphIndex; use glyph::GlyphIndex;
use font::FontMetrics;
use freetype::{ FT_Error, FT_Library, FT_Face, FT_Long, FT_ULong, FT_Size, FT_SizeRec, use freetype::{ FT_Error, FT_Library, FT_Face, FT_Long, FT_ULong, FT_Size, FT_SizeRec,
FT_UInt, FT_GlyphSlot, FT_Size_Metrics, FT_FaceRec }; FT_UInt, FT_GlyphSlot, FT_Size_Metrics, FT_FaceRec };
use freetype::bindgen::{ use freetype::bindgen::{

View file

@ -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)
}

View file

@ -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. font resources needed by the graphics layer to draw glyphs.
*/ */
use glyph::GlyphIndex;
use font_cache::native::NativeFontCache; use font_cache::native::NativeFontCache;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub type NativeFont/& = quartz_native_font::QuartzNativeFont; pub type NativeFont/& = quartz::native_font::QuartzNativeFont;
#[cfg(target_os = "linux")] #[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 // TODO: this should be part of trait NativeFont
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn create(native_lib: &NativeFontCache, buf: @~[u8], pt_size: float) -> Result<NativeFont, ()> { pub fn create(native_lib: &NativeFontCache, buf: @~[u8], pt_size: float) -> Result<NativeFont, ()> {
quartz_native_font::create(native_lib, buf, pt_size) quartz::native_font::create(native_lib, buf, pt_size)
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn create(native_lib: &NativeFontCache, buf: @~[u8], pt_size: float) -> Result<NativeFont, ()> { pub fn create(native_lib: &NativeFontCache, buf: @~[u8], pt_size: float) -> Result<NativeFont, ()> {
ft_native_font::create(native_lib, buf, pt_size) freetype::native_font::create(native_lib, buf, pt_size)
} }

View file

@ -2,15 +2,14 @@ extern mod core_foundation;
extern mod core_graphics; extern mod core_graphics;
extern mod core_text; extern mod core_text;
export QuartzNativeFont, with_test_native_font, create;
use font::{FontMetrics, FractionalPixel}; use font::{FontMetrics, FractionalPixel};
use font_cache::native::NativeFontCache;
use au = gfx::geometry; use au = gfx::geometry;
use cast::transmute; use cast::transmute;
use glyph::GlyphIndex;
use libc::size_t; use libc::size_t;
use ptr::null; use ptr::null;
use glyph::GlyphIndex;
use cf = core_foundation; use cf = core_foundation;
use cf::base::{ use cf::base::{

View file

@ -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 Shaper encapsulates a specific shaper, such as Harfbuzz,
when rendered in a specific font. 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. pub type Shaper/& = harfbuzz::shaper::HarfbuzzShaper;
// 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)
}

View file

@ -10,7 +10,6 @@ use glyph::GlyphStore;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use libc::{c_void}; use libc::{c_void};
use servo_util::color; use servo_util::color;
use shaper::shape_textrun;
use std::arc; use std::arc;
use servo_util::range::{Range, MutableRange}; use servo_util::range::{Range, MutableRange};
@ -41,6 +40,8 @@ impl SendableTextRun {
impl TextRun { impl TextRun {
static fn new(font: @Font, text: ~str) -> TextRun { static fn new(font: @Font, text: ~str) -> TextRun {
use shaper::Shaper;
let glyph_store = GlyphStore(text.len()); let glyph_store = GlyphStore(text.len());
let run = TextRun { let run = TextRun {
text: move text, text: move text,
@ -48,7 +49,10 @@ impl TextRun {
glyphs: move glyph_store, 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; return move run;
} }