Restructure text module

This commit is contained in:
Brian Anderson 2012-08-09 18:00:20 -07:00
parent 2b29512ef5
commit d7eb2ab8ac
7 changed files with 115 additions and 270 deletions

View file

@ -6,6 +6,7 @@ import dl = layout::display_list;
import azure::*;
import azure::bindgen::*;
import libc::size_t;
import text::font::Font;
import text::text_run::TextRun;
import dom::event::{Event, ResizeEvent};
import geom::size::Size2D;
@ -14,6 +15,7 @@ import geom::point::Point2D;
import azure_hl::AsAzureRect;
import ptr::addr_of;
import arc::arc;
import azure::cairo::{cairo_font_face_t, cairo_scaled_font_t};
import pipes::{port, chan};
@ -177,6 +179,7 @@ fn draw_text(draw_target: AzDrawTargetRef, item: dl::display_item, text_run: Tex
AzReleaseScaledFont,
AzCreateColorPattern,
AzReleaseColorPattern};
import azure::cairo::bindgen::cairo_scaled_font_destroy;
let bounds = copy (*item).bounds;
// FIXME: The font library should not be created here
@ -188,8 +191,10 @@ fn draw_text(draw_target: AzDrawTargetRef, item: dl::display_item, text_run: Tex
mFont: null()
};
let azfont = AzCreateScaledFontWithCairo(addr_of(nfont), 1f as AzFloat, font.cairo_font);
let cfont = get_cairo_font(font);
let azfont = AzCreateScaledFontWithCairo(addr_of(nfont), 1f as AzFloat, cfont);
assert azfont.is_not_null();
cairo_scaled_font_destroy(cfont);
let color = {
r: 0f as AzFloat,
@ -231,6 +236,66 @@ fn draw_text(draw_target: AzDrawTargetRef, item: dl::display_item, text_run: Tex
AzReleaseScaledFont(azfont);
}
#[cfg(target_os = "linux")]
fn get_cairo_face(font: &Font) -> *cairo_font_face_t {
import libc::c_int;
import azure::cairo_ft::bindgen::{cairo_ft_font_face_create_for_ft_face};
let ftface = font.native_font.face;
let cface = cairo_ft_font_face_create_for_ft_face(ftface, 0 as c_int);
// FIXME: error handling
return cface;
}
#[cfg(target_os = "macos")]
fn get_cairo_face(font: &Font) -> *cairo_font_face_t {
import azure::cairo_quartz::bindgen::cairo_quartz_font_face_create_for_cgfont;
let cgfont = font.native_font.cgfont;
let face = cairo_quartz_font_face_create_for_cgfont(cgfont);
// FIXME: error handling
return face;
}
fn get_cairo_font(font: &Font) -> *cairo_scaled_font_t {
import libc::c_double;
import azure::cairo;
import cairo::cairo_matrix_t;
import cairo::bindgen::{cairo_matrix_init_identity,
cairo_matrix_scale,
cairo_font_options_create,
cairo_scaled_font_create,
cairo_font_options_destroy,
cairo_font_face_destroy};
// FIXME: error handling
let face = get_cairo_face(font);
let idmatrix: cairo_matrix_t = {
xx: 0 as c_double,
yx: 0 as c_double,
xy: 0 as c_double,
yy: 0 as c_double,
x0: 0 as c_double,
y0: 0 as c_double
};
cairo_matrix_init_identity(addr_of(idmatrix));
let fontmatrix = idmatrix;
cairo_matrix_scale(addr_of(fontmatrix),
20f as c_double, 20f as c_double);
let options = cairo_font_options_create();
let cfont = cairo_scaled_font_create(face, addr_of(fontmatrix),
addr_of(idmatrix), options);
cairo_font_options_destroy(options);
cairo_font_face_destroy(face);
return cfont;
}
fn clear(draw_target: AzDrawTargetRef) {
let black_color = {

View file

@ -4,22 +4,7 @@ import glyph::GlyphIndex;
import vec_to_ptr = vec::unsafe::to_ptr;
import libc::{ c_int, c_double, c_ulong };
import ptr::{ null, addr_of };
import azure::cairo::{
cairo_font_face_t,
cairo_scaled_font_t,
cairo_glyph_t,
cairo_text_extents_t,
CAIRO_STATUS_SUCCESS,
};
import azure::cairo::bindgen::{
cairo_font_face_destroy,
cairo_scaled_font_destroy,
cairo_scaled_font_status,
cairo_scaled_font_text_to_glyphs,
cairo_scaled_font_glyph_extents,
cairo_glyph_free,
cairo_status_to_string
};
import native_font::NativeFont;
// FIXME (rust 2708): convert this to a class
@ -29,239 +14,29 @@ and the renderer can use it to render text.
"]
class Font {
let fontbuf: @~[u8];
let cairo_font: *cairo_scaled_font_t;
let font_dtor: fn@();
new(-fontbuf: ~[u8]) {
let (cairo_font, font_dtor) = get_cairo_font(&copy fontbuf);
assert cairo_font.is_not_null();
let native_font: NativeFont;
new(-fontbuf: ~[u8], -native_font: NativeFont) {
self.fontbuf = @fontbuf;
self.cairo_font = cairo_font;
self.font_dtor = font_dtor;
self.native_font = native_font;
}
fn buf() -> @~[u8] {
self.fontbuf
}
fn glyph_idx(codepoint: char) -> option<GlyphIndex> {
#debug("getting glyph for codepoint %u", codepoint as uint);
let codepoint_str = str::from_char(codepoint);
let mut glyphs: *cairo_glyph_t = null();
let mut num_glyphs = 0 as c_int;
let status = str::as_c_str(codepoint_str, |codepoint_buf| {
cairo_scaled_font_text_to_glyphs(
self.cairo_font,
0.0 as c_double, 0.0 as c_double,
codepoint_buf, codepoint_str.len() as c_int,
addr_of(glyphs), addr_of(num_glyphs),
null(), null(), null()
)
});
return if status == CAIRO_STATUS_SUCCESS {
// This might not be true, but at least we'll know if it isn't
assert num_glyphs == 1 as c_int;
let glyph_index = unsafe { *glyphs }.index as GlyphIndex;
#debug("glyph index is %?", glyph_index);
cairo_glyph_free(glyphs);
some(glyph_index)
} else {
#error("cairo did not give me a glyph for %u", codepoint as uint);
none
}
fn glyph_index(codepoint: char) -> option<GlyphIndex> {
self.native_font.glyph_index(codepoint)
}
fn glyph_h_advance(glyph: GlyphIndex) -> int {
#debug("getting h advance for glyph %?", glyph);
let glyphs: ~[cairo_glyph_t] = ~[{
index: glyph as c_ulong,
x: 0 as c_double,
y: 0 as c_double,
}];
let extents: cairo_text_extents_t = {
x_bearing: 0 as c_double,
y_bearing: 0 as c_double,
width: 0 as c_double,
height: 0 as c_double,
x_advance: 0 as c_double,
y_advance: 0 as c_double,
};
assert self.cairo_font.is_not_null();
cairo_scaled_font_glyph_extents(
self.cairo_font, unsafe { vec_to_ptr(glyphs) },
1 as c_int, addr_of(extents));
match cairo_scaled_font_status(self.cairo_font) {
status if status == CAIRO_STATUS_SUCCESS => {
#debug("x_advance: %?", extents.x_advance);
#debug("y_advance: %?", extents.y_advance);
return extents.x_advance as int;
}
status => {
import str::unsafe::from_c_str;
let status_cstr = cairo_status_to_string(status);
let status_str = unsafe { from_c_str(status_cstr) };
#error("cairo_scaled_font_glyph_extents status: %s", status_str);
fail ~"failed to get glyph extents from cairo"
}
match self.native_font.glyph_h_advance(glyph) {
some(adv) => adv,
none => /* FIXME: Need fallback strategy */ 10
}
}
}
fn get_cairo_font(buf: &~[u8]) -> (*cairo_scaled_font_t, fn@()) {
import libc::c_double;
import azure::cairo;
import cairo::{ cairo_matrix_t };
import cairo::bindgen::{
cairo_matrix_init_identity,
cairo_matrix_scale,
cairo_font_options_create,
cairo_font_options_destroy,
cairo_scaled_font_create,
cairo_scaled_font_destroy
};
import cairo::bindgen::cairo_scaled_font_create;
let mut (face, dtor) = get_cairo_face(buf);
let idmatrix: cairo_matrix_t = {
xx: 0 as c_double,
yx: 0 as c_double,
xy: 0 as c_double,
yy: 0 as c_double,
x0: 0 as c_double,
y0: 0 as c_double
};
cairo_matrix_init_identity(addr_of(idmatrix));
let fontmatrix = idmatrix;
cairo_matrix_scale(addr_of(fontmatrix),
20f as c_double, 20f as c_double);
let options = cairo_font_options_create();
let cfont = cairo_scaled_font_create(face, addr_of(fontmatrix),
addr_of(idmatrix), options);
cairo_font_options_destroy(options);
// FIXME: Need negative tests
if cfont.is_null() {
dtor();
fail ~"unable to create cairo scaled font";
}
dtor = fn@(move dtor) { cairo_scaled_font_destroy(cfont); dtor() };
(cfont, dtor)
}
#[cfg(target_os = "linux")]
fn get_cairo_face(buf: &~[u8]) -> (*cairo_font_face_t, fn@()) {
import freetype = azure::freetype;
import freetype::{ FT_Error, FT_Library, FT_Face, FT_Long };
import freetype::bindgen::{
FT_Init_FreeType,
FT_Done_FreeType,
FT_New_Memory_Face,
FT_Done_Face
};
import azure::cairo_ft;
import cairo_ft::bindgen::cairo_ft_font_face_create_for_ft_face;
trait FTErrorMethods {
fn for_sure();
fn failed() -> bool;
}
impl FT_Error : FTErrorMethods {
fn for_sure() { assert !self.failed() }
fn failed() -> bool { self != 0 as FT_Error }
}
let mut dtor = fn@() { };
let library: FT_Library = null();
// FIXME: Need tests for failure case
FT_Init_FreeType(addr_of(library)).for_sure();
dtor = fn@(move dtor) { FT_Done_FreeType(library).for_sure(); dtor() };
let face: FT_Face = null();
vec::as_buf(*buf, |cbuf, len| {
if FT_New_Memory_Face(library, cbuf, len as FT_Long,
0 as FT_Long, addr_of(face)).failed() {
dtor();
fail ~"unable to create FreeType face";
}
});
dtor = fn@(move dtor) { FT_Done_Face(face).for_sure(); dtor() };
let cface = cairo_ft_font_face_create_for_ft_face(face, 0 as c_int);
if cface.is_null() {
// FIXME: Need tests for failure case
dtor();
fail ~"unable to create cairo font face";
}
dtor = fn@(move dtor) { cairo_font_face_destroy(cface); dtor() };
(cface, dtor)
}
#[cfg(target_os = "macos")]
mod cocoa {
use cocoa;
export cocoa;
}
#[cfg(target_os = "macos")]
fn get_cairo_face(buf: &~[u8]) -> (*cairo_font_face_t, fn@()) {
import unsafe::reinterpret_cast;
import libc::size_t;
import cocoa::cocoa;
import cocoa::cg::cg::{
CGDataProviderCreateWithData,
CGDataProviderRelease,
CGFontCreateWithDataProvider,
CGFontRelease
};
import azure::cairo_quartz::bindgen::cairo_quartz_font_face_create_for_cgfont;
let mut dtor = fn@() { };
let fontprov = vec::as_buf(*buf, |cbuf, len| {
CGDataProviderCreateWithData(
null(),
unsafe { reinterpret_cast(cbuf) },
len as size_t,
null()
)
});
dtor = fn@(move dtor) { CGDataProviderRelease(fontprov); dtor() };
let cgfont = CGFontCreateWithDataProvider(fontprov);
if cgfont.is_null() { fail ~"could not create quartz font" }
dtor = fn@(move dtor) { CGFontRelease(cgfont); dtor() };
let cface = cairo_quartz_font_face_create_for_cgfont(cgfont);
assert cface.is_not_null(); // FIXME: error handling
dtor = fn@(move dtor) { cairo_font_face_destroy(cface); dtor() };
(cface, dtor)
}
fn create_test_font() -> @Font {
import font_library::FontLibrary;
@ -285,7 +60,7 @@ fn should_get_glyph_indexes() {
#[ignore(reason = "random failures")];
let font = create_test_font();
let glyph_idx = font.glyph_idx('w');
let glyph_idx = font.glyph_index('w');
assert glyph_idx == some(40u);
}
@ -300,31 +75,8 @@ fn should_get_glyph_advance() {
fn should_be_able_to_create_instances_in_multiple_threads() {
#[test];
#[ignore];
for iter::repeat(10u) {do task::spawn {create_test_font();}}
}
fn get_cairo_face_should_fail_and_not_leak_if_font_cant_be_created() {
#[test];
#[should_fail];
get_cairo_face(&~[0u8, 1u8, 2u8, 3u8]);
}
fn get_cairo_face_should_return_a_new_face_and_dtor() {
#[test];
let buf = test_font_bin();
let (face, dtor) = get_cairo_face(&buf);
assert face.is_not_null();
dtor();
}
fn get_cairo_font_should_return_a_new_font_and_dtor() {
#[test];
let buf = test_font_bin();
let (font, dtor) = get_cairo_font(&buf);
assert font.is_not_null();
dtor();
}

View file

@ -1,6 +1,7 @@
export FontLibrary, native;
import font::Font;
import font::{Font, test_font_bin};
import result::{result, ok, err};
class FontLibrary {
let native_lib: native::NativeFontLibrary;
@ -14,8 +15,10 @@ class FontLibrary {
}
fn get_font() -> @Font {
let f = Font(font::test_font_bin());
return @f;
match create_font(&self.native_lib) {
ok(font) => font,
err(*) => /* FIXME */ fail
}
}
fn get_test_font() -> @Font {
@ -23,6 +26,18 @@ class FontLibrary {
}
}
fn create_font(native_lib: &native::NativeFontLibrary) -> result<@Font, ()> {
let font_bin = test_font_bin();
let native_font = native_font::create(native_lib, &font_bin);
let native_font = if native_font.is_ok() {
result::unwrap(native_font)
} else {
return err(native_font.get_err());
};
return ok(@Font(font_bin, native_font));
}
#[cfg(target_os = "linux")]
mod native {
import ptr::{null, addr_of};

View file

@ -7,7 +7,10 @@ font resources needed by the graphics layer to draw glyphs.
"];
export NativeFont;
export NativeFont, create;
import result::result;
import font_library::native::NativeFontLibrary;
#[cfg(target_os = "macos")]
type NativeFont/& = quartz_native_font::QuartzNativeFont;
@ -15,6 +18,16 @@ type NativeFont/& = quartz_native_font::QuartzNativeFont;
#[cfg(target_os = "linux")]
type NativeFont/& = ft_native_font::FreeTypeNativeFont;
#[cfg(target_os = "macos")]
fn create(_native_lib: &NativeFontLibrary, buf: &~[u8]) -> result<NativeFont, ()> {
quartz_native_font::create(buf)
}
#[cfg(target_os = "linux")]
fn create(native_lib: &NativeFontLibrary, buf: &~[u8]) -> result<NativeFont, ()> {
ft_native_font::create(*native_lib, buf)
}
#[cfg(target_os = "macos")]
fn with_test_native_font(f: fn@(nf: &NativeFont)) {
quartz_native_font::with_test_native_font(f);

View file

@ -1,4 +1,4 @@
export FreeTypeNativeFont, with_test_native_font;
export FreeTypeNativeFont, with_test_native_font, create;
import vec_as_buf = vec::as_buf;
import result::{result, ok, err};

View file

@ -1,6 +1,6 @@
use cocoa;
export QuartzNativeFont, with_test_native_font;
export QuartzNativeFont, with_test_native_font, create;
import libc::size_t;
import ptr::null;
@ -50,8 +50,8 @@ class QuartzNativeFont/& {
}
}
fn create(buf: ~[u8]) -> result<QuartzNativeFont, ()> {
let fontprov = vec::as_buf(buf, |cbuf, len| {
fn create(buf: &~[u8]) -> result<QuartzNativeFont, ()> {
let fontprov = vec::as_buf(*buf, |cbuf, len| {
CGDataProviderCreateWithData(
null(),
unsafe { reinterpret_cast(cbuf) },
@ -72,7 +72,7 @@ fn with_test_native_font(f: fn@(nf: &NativeFont)) {
import unwrap_result = result::unwrap;
let buf = test_font_bin();
let res = create(buf);
let res = create(&buf);
let font = unwrap_result(res);
f(&font);
}

View file

@ -111,7 +111,7 @@ extern fn glyph_func(_font: *hb_font_t,
let font: *Font = reinterpret_cast(font_data);
assert font.is_not_null();
return match (*font).glyph_idx(unicode as char) {
return match (*font).glyph_index(unicode as char) {
some(g) => {
*glyph = g as hb_codepoint_t;
true