From f08f56d40d4feaa28fb007ab0f8528068cecc2f4 Mon Sep 17 00:00:00 2001 From: "Brian J. Burg" Date: Mon, 1 Oct 2012 22:17:01 -0700 Subject: [PATCH] Add port of Gecko's CompressedGlyph. --- src/servo/text/font.rs | 10 +- src/servo/text/glyph.rs | 191 +++++++++++++++++++++++++++++++++- src/servo/text/native_font.rs | 13 ++- src/servo/text/shaper.rs | 8 +- 4 files changed, 202 insertions(+), 20 deletions(-) diff --git a/src/servo/text/font.rs b/src/servo/text/font.rs index 2a7883ffde4..0e3dd7aa868 100644 --- a/src/servo/text/font.rs +++ b/src/servo/text/font.rs @@ -7,10 +7,10 @@ use libc::{ c_int, c_double, c_ulong }; use ptr::{ null, addr_of }; use native_font::NativeFont; -#[doc = " +/** A font handle. Layout can use this to calculate glyph metrics and the renderer can use it to render text. -"] +*/ struct Font { // A back reference to keep the library alive lib: @FontCache, @@ -78,7 +78,7 @@ fn should_get_glyph_indexes() { let lib = FontCache(); let font = lib.get_test_font(); let glyph_idx = font.glyph_index('w'); - assert glyph_idx == Some(40u); + assert glyph_idx == Some(40u as GlyphIndex); } fn should_get_glyph_advance() { @@ -86,7 +86,7 @@ fn should_get_glyph_advance() { let lib = FontCache(); let font = lib.get_test_font(); - let x = font.glyph_h_advance(40u); + let x = font.glyph_h_advance(40u as GlyphIndex); assert x == 15; } @@ -102,7 +102,7 @@ fn should_get_glyph_advance_stress() { do task::spawn { let lib = FontCache(); let font = lib.get_test_font(); - let x = font.glyph_h_advance(40u); + let x = font.glyph_h_advance(40u as GlyphIndex); assert x == 15; chan.send(()); } diff --git a/src/servo/text/glyph.rs b/src/servo/text/glyph.rs index 2814a20861e..d08c4994b22 100644 --- a/src/servo/text/glyph.rs +++ b/src/servo/text/glyph.rs @@ -1,10 +1,193 @@ export GlyphIndex, GlyphPos, Glyph; -use gfx::geometry::au; +use au = gfx::geometry; +use au::au; use geom::point::Point2D; /** The index of a particular glyph within a font */ -type GlyphIndex = uint; +struct CompressedGlyph { + mut value : u32 +} +type GlyphIndex = u32; + +enum BreakTypeFlags { + BREAK_TYPE_NONE = 0x0, + BREAK_TYPE_NORMAL = 0x1, + BREAK_TYPE_HYPEN = 0x2, +} + +// TODO: make this more type-safe. + +const FLAG_CHAR_IS_SPACE : u32 = 0x10000000u32; +// These two bits store a BreakTypeFlags +const FLAG_CAN_BREAK_MASK : u32 = 0x60000000u32; +const FLAG_CAN_BREAK_SHIFT : u32 = 29u32; +const FLAG_IS_SIMPLE_GLYPH : u32 = 0x80000000u32; + +// glyph advance; in au's. +const GLYPH_ADVANCE_MASK : u32 = 0x0FFF0000u32; +const GLYPH_ADVANCE_SHIFT : u32 = 16; +const GLYPH_ID_MASK : u32 = 0x0000FFFFu32; + +// Non-simple glyphs (more than one glyph per char; missing glyph, +// newline, tab, large advance, or nonzero x/y offsets) may have one +// or more detailed glyphs associated with them. They are stored in a +// side array so that there is a 1:1 mapping of CompressedGlyph to +// unicode char. + +// The number of detailed glyphs for this char. If the char couldn't +// be mapped to a glyph (!FLAG_NOT_MISSING), then this actually holds +// the UTF8 code point instead. +const GLYPH_COUNT_MASK : u32 = 0x00FFFF00u32; +const GLYPH_COUNT_SHIFT : u32 = 8; +// N.B. following Gecko, these are all inverted so that a lot of +// missing chars can be memset with zeros in one fell swoop. +const FLAG_NOT_MISSING : u32 = 0x00000001u32; +const FLAG_NOT_CLUSTER_START : u32 = 0x00000002u32; +const FLAG_NOT_LIGATURE_GROUP_START : u32 = 0x00000004u32; + +const FLAG_CHAR_IS_TAB : u32 = 0x00000008u32; +const FLAG_CHAR_IS_NEWLINE : u32 = 0x00000010u32; +const FLAG_CHAR_IS_LOW_SURROGATE : u32 = 0x00000020u32; +const CHAR_IDENTITY_FLAGS_MASK : u32 = 0x00000038u32; + + +pure fn is_simple_glyph_id(glyphId: GlyphIndex) -> bool { + ((glyphId as u32) & GLYPH_ID_MASK) == glyphId +} + +pure fn is_simple_advance(advance: au) -> bool { + let unsignedAu = advance.to_int() as u32; + (unsignedAu & (GLYPH_ADVANCE_MASK >> GLYPH_ADVANCE_SHIFT)) == unsignedAu +} + +type DetailedGlyphCount = u16; + +enum GlyphStoreResult { + Simple(T), + Detailed(u32) +} + +fn SimpleGlyph(index: GlyphIndex, advance: au) -> CompressedGlyph { + assert is_simple_glyph_id(index); + assert is_simple_advance(advance); + + let index_mask = index as u32; + let advance_mask = (*advance as u32) << GLYPH_ADVANCE_SHIFT; + + CompressedGlyph { + value: index_mask | advance_mask | FLAG_IS_SIMPLE_GLYPH + } +} + +fn ComplexGlyph(startsCluster: bool, startsLigature: bool, glyphCount: u16) -> CompressedGlyph { + let mut val = FLAG_NOT_MISSING; + + if !startsCluster { + val |= FLAG_NOT_CLUSTER_START; + } + if !startsLigature { + val |= FLAG_NOT_LIGATURE_GROUP_START; + } + val |= (glyphCount as u32) << GLYPH_COUNT_SHIFT; + + CompressedGlyph { + value: val + } +} + +fn MissingGlyphs(glyphCount: u16) -> CompressedGlyph { + CompressedGlyph { + value: (glyphCount as u32) << GLYPH_COUNT_SHIFT + } +} + +impl CompressedGlyph { + pure fn advance() -> GlyphStoreResult { + match self.is_simple() { + true => Simple(au::from_int(((self.value & GLYPH_ADVANCE_MASK) >> GLYPH_ADVANCE_SHIFT) as int)), + false => Detailed(self.glyph_count()) + } + } + + pure fn glyph() -> GlyphStoreResult { + match self.is_simple() { + true => Simple(self.value & GLYPH_ID_MASK), + false => Detailed(self.glyph_count()) + } + } + + pure fn offset() -> GlyphStoreResult> { + match self.is_simple() { + true => Simple(Point2D(au(0), au(0))), + false => Detailed(self.glyph_count()) + } + } + + // getter methods + + // TODO: some getters are still missing; add them as needed. + + // True if original char was normal (U+0020) space. Other chars may + // map to space glyph, but this does not account for them. + pure fn char_is_space() -> bool { + self.has_flag(FLAG_CHAR_IS_SPACE) + } + + pure fn char_is_tab() -> bool { + !self.is_simple() && self.has_flag(FLAG_CHAR_IS_TAB) + } + + pure fn char_is_newline() -> bool { + !self.is_simple() && self.has_flag(FLAG_CHAR_IS_NEWLINE) + } + + // TODO: make typesafe break enum + pure fn can_break_before() -> u8 { + ((self.value & FLAG_CAN_BREAK_MASK) >> FLAG_CAN_BREAK_SHIFT) as u8 + } + + // setter methods + + fn set_is_space() { + self.value |= FLAG_CHAR_IS_SPACE; + } + + fn set_is_tab() { + assert !self.is_simple(); + self.value |= FLAG_CHAR_IS_TAB; + } + + fn set_is_newline() { + assert !self.is_simple(); + self.value |= FLAG_CHAR_IS_NEWLINE; + } + + // returns whether the setting had changed. + fn set_can_break_before(flags: u8) -> bool { + assert flags <= 0x2; + let mask = (flags as u32) << FLAG_CAN_BREAK_SHIFT; + let toggle = mask ^ (self.value & FLAG_CAN_BREAK_MASK); + self.value ^= toggle; + + toggle as bool + } + + // helper methods + + /*priv*/ pure fn glyph_count() -> u32 { + assert !self.is_simple(); + (self.value & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT as u32 + } + + /*priv*/ pure fn is_simple() -> bool { + (self.value & FLAG_IS_SIMPLE_GLYPH) == self.value + } + + /*priv*/ pure fn has_flag(flag: u32) -> bool { + (self.value & flag) != 0 + } +} /** The position of a glyph on the screen. */ struct GlyphPos { @@ -21,11 +204,11 @@ fn GlyphPos(advance: Point2D, offset: Point2D) -> GlyphPos { /** A single glyph. */ struct Glyph { - index: GlyphIndex, + index: u32, pos: GlyphPos, } -fn Glyph(index: GlyphIndex, pos: GlyphPos) -> Glyph { +fn Glyph(index: u32, pos: GlyphPos) -> Glyph { Glyph { index : index, pos : copy pos, diff --git a/src/servo/text/native_font.rs b/src/servo/text/native_font.rs index 0f59c49a77f..03be27728c0 100644 --- a/src/servo/text/native_font.rs +++ b/src/servo/text/native_font.rs @@ -1,14 +1,13 @@ -#[doc = " - +/** NativeFont encapsulates access to the platform's font API, e.g. quartz, FreeType. It provides access to metrics and tables needed by the text shaper as well as access to the underlying font resources needed by the graphics layer to draw glyphs. - -"]; +*/ export NativeFont, create; +use glyph::GlyphIndex; use font_cache::native::NativeFontCache; #[cfg(target_os = "macos")] @@ -41,7 +40,7 @@ fn with_test_native_font(f: fn@(nf: &NativeFont)) { fn should_get_glyph_indexes() { with_test_native_font(|font| { let idx = font.glyph_index('w'); - assert idx == Some(40u); + assert idx == Some(40u as GlyphIndex); }) } @@ -57,7 +56,7 @@ fn should_return_none_glyph_index_for_bad_codepoints() { #[ignore(cfg(target_os = "macos"))] fn should_get_glyph_h_advance() { with_test_native_font(|font| { - let adv = font.glyph_h_advance(40u); + let adv = font.glyph_h_advance(40u as GlyphIndex); assert adv == Some(15); }) } @@ -66,7 +65,7 @@ fn should_get_glyph_h_advance() { #[ignore(cfg(target_os = "macos"))] fn should_return_none_glyph_h_advance_for_bad_codepoints() { with_test_native_font(|font| { - let adv = font.glyph_h_advance(-1 as uint); + let adv = font.glyph_h_advance(-1 as GlyphIndex); assert adv == None; }) } diff --git a/src/servo/text/shaper.rs b/src/servo/text/shaper.rs index 7f3367f58b6..27e0816e77f 100644 --- a/src/servo/text/shaper.rs +++ b/src/servo/text/shaper.rs @@ -6,7 +6,7 @@ use au = gfx::geometry; use libc::types::common::c99::int32_t; use libc::{c_uint, c_int, c_void, c_char}; use font::Font; -use glyph::{Glyph, GlyphPos}; +use glyph::{Glyph, GlyphPos, GlyphIndex}; use ptr::{null, addr_of, offset}; use gfx::geometry::au; use geom::point::Point2D; @@ -87,7 +87,7 @@ fn shape_text(font: &Font, text: &str) -> ~[Glyph] unsafe { for uint::range(0u, info_len as uint) |i| { let info_ = offset(info_, i); let pos = offset(pos, i); - let codepoint = (*info_).codepoint as uint; + let codepoint = (*info_).codepoint as u32; let pos = hb_glyph_pos_to_servo_glyph_pos(&*pos); #debug("glyph %?: codep %?, x_adv %?, y_adv %?, x_off %?, y_of %?", i, codepoint, pos.advance.x, pos.advance.y, pos.offset.x, pos.offset.y); @@ -132,7 +132,7 @@ extern fn glyph_h_advance_func(_font: *hb_font_t, let font: *Font = reinterpret_cast(&font_data); assert font.is_not_null(); - let h_advance = (*font).glyph_h_advance(glyph as uint); + let h_advance = (*font).glyph_h_advance(glyph as u32); #debug("h_advance for codepoint %? is %?", glyph, h_advance); return h_advance as hb_position_t; } @@ -152,7 +152,7 @@ fn should_get_glyph_indexes() { let font = lib.get_test_font(); let glyphs = shape_text(font, ~"firecracker"); let idxs = glyphs.map(|glyph| glyph.index); - assert idxs == ~[32u, 8u, 13u, 14u, 10u, 13u, 201u, 10u, 37u, 14u, 13u]; + assert idxs == ~[32u32, 8u32, 13u32, 14u32, 10u32, 13u32, 201u32, 10u32, 37u32, 14u32, 13u32]; } fn should_get_glyph_h_advance() {