From d7eb2ab8ac6e78e78ad1209f41f2607a1e4fbde1 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 9 Aug 2012 18:00:20 -0700 Subject: [PATCH] Restructure text module --- src/servo/gfx/renderer.rs | 67 ++++- src/servo/text/font.rs | 270 +----------------- src/servo/text/font_library.rs | 21 +- src/servo/text/native_font.rs | 15 +- src/servo/text/native_font/ft_native_font.rs | 2 +- .../text/native_font/quartz_native_font.rs | 8 +- src/servo/text/shaper.rs | 2 +- 7 files changed, 115 insertions(+), 270 deletions(-) diff --git a/src/servo/gfx/renderer.rs b/src/servo/gfx/renderer.rs index d25171e7e25..c111dc8ab34 100644 --- a/src/servo/gfx/renderer.rs +++ b/src/servo/gfx/renderer.rs @@ -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 = { diff --git a/src/servo/text/font.rs b/src/servo/text/font.rs index a58dc06ac88..ed32f13a4c4 100644 --- a/src/servo/text/font.rs +++ b/src/servo/text/font.rs @@ -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(© 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 { - #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 { + 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(); -} diff --git a/src/servo/text/font_library.rs b/src/servo/text/font_library.rs index fba25ee5c0e..5986b846584 100644 --- a/src/servo/text/font_library.rs +++ b/src/servo/text/font_library.rs @@ -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}; diff --git a/src/servo/text/native_font.rs b/src/servo/text/native_font.rs index bd330da5f10..824b4e99c9d 100644 --- a/src/servo/text/native_font.rs +++ b/src/servo/text/native_font.rs @@ -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 { + quartz_native_font::create(buf) +} + +#[cfg(target_os = "linux")] +fn create(native_lib: &NativeFontLibrary, buf: &~[u8]) -> result { + 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); diff --git a/src/servo/text/native_font/ft_native_font.rs b/src/servo/text/native_font/ft_native_font.rs index d853e35615a..a1b4c077014 100644 --- a/src/servo/text/native_font/ft_native_font.rs +++ b/src/servo/text/native_font/ft_native_font.rs @@ -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}; diff --git a/src/servo/text/native_font/quartz_native_font.rs b/src/servo/text/native_font/quartz_native_font.rs index a101e089d19..91b37066050 100644 --- a/src/servo/text/native_font/quartz_native_font.rs +++ b/src/servo/text/native_font/quartz_native_font.rs @@ -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 { - let fontprov = vec::as_buf(buf, |cbuf, len| { +fn create(buf: &~[u8]) -> result { + 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); } diff --git a/src/servo/text/shaper.rs b/src/servo/text/shaper.rs index 40aa552785a..29aac68e970 100644 --- a/src/servo/text/shaper.rs +++ b/src/servo/text/shaper.rs @@ -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