Add port of Gecko's CompressedGlyph.

This commit is contained in:
Brian J. Burg 2012-10-01 22:17:01 -07:00
parent eaada181b1
commit f08f56d40d
4 changed files with 202 additions and 20 deletions

View file

@ -7,10 +7,10 @@ use libc::{ c_int, c_double, c_ulong };
use ptr::{ null, addr_of }; use ptr::{ null, addr_of };
use native_font::NativeFont; use native_font::NativeFont;
#[doc = " /**
A font handle. Layout can use this to calculate glyph metrics A font handle. Layout can use this to calculate glyph metrics
and the renderer can use it to render text. and the renderer can use it to render text.
"] */
struct Font { struct Font {
// A back reference to keep the library alive // A back reference to keep the library alive
lib: @FontCache, lib: @FontCache,
@ -78,7 +78,7 @@ fn should_get_glyph_indexes() {
let lib = FontCache(); let lib = FontCache();
let font = lib.get_test_font(); let font = lib.get_test_font();
let glyph_idx = font.glyph_index('w'); let glyph_idx = font.glyph_index('w');
assert glyph_idx == Some(40u); assert glyph_idx == Some(40u as GlyphIndex);
} }
fn should_get_glyph_advance() { fn should_get_glyph_advance() {
@ -86,7 +86,7 @@ fn should_get_glyph_advance() {
let lib = FontCache(); let lib = FontCache();
let font = lib.get_test_font(); 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; assert x == 15;
} }
@ -102,7 +102,7 @@ fn should_get_glyph_advance_stress() {
do task::spawn { do task::spawn {
let lib = FontCache(); let lib = FontCache();
let font = lib.get_test_font(); 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; assert x == 15;
chan.send(()); chan.send(());
} }

View file

@ -1,10 +1,193 @@
export GlyphIndex, GlyphPos, Glyph; export GlyphIndex, GlyphPos, Glyph;
use gfx::geometry::au; use au = gfx::geometry;
use au::au;
use geom::point::Point2D; use geom::point::Point2D;
/** The index of a particular glyph within a font */ /** 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<T> {
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<au> {
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<GlyphIndex> {
match self.is_simple() {
true => Simple(self.value & GLYPH_ID_MASK),
false => Detailed(self.glyph_count())
}
}
pure fn offset() -> GlyphStoreResult<Point2D<au>> {
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. */ /** The position of a glyph on the screen. */
struct GlyphPos { struct GlyphPos {
@ -21,11 +204,11 @@ fn GlyphPos(advance: Point2D<au>, offset: Point2D<au>) -> GlyphPos {
/** A single glyph. */ /** A single glyph. */
struct Glyph { struct Glyph {
index: GlyphIndex, index: u32,
pos: GlyphPos, pos: GlyphPos,
} }
fn Glyph(index: GlyphIndex, pos: GlyphPos) -> Glyph { fn Glyph(index: u32, pos: GlyphPos) -> Glyph {
Glyph { Glyph {
index : index, index : index,
pos : copy pos, pos : copy pos,

View file

@ -1,14 +1,13 @@
#[doc = " /**
NativeFont encapsulates access to the platform's font API, NativeFont encapsulates access to the platform's font API,
e.g. quartz, FreeType. It provides access to metrics and tables e.g. quartz, FreeType. It provides access to metrics and tables
needed by the text shaper as well as access to the underlying 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.
*/
"];
export NativeFont, create; export NativeFont, create;
use glyph::GlyphIndex;
use font_cache::native::NativeFontCache; use font_cache::native::NativeFontCache;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -41,7 +40,7 @@ fn with_test_native_font(f: fn@(nf: &NativeFont)) {
fn should_get_glyph_indexes() { fn should_get_glyph_indexes() {
with_test_native_font(|font| { with_test_native_font(|font| {
let idx = font.glyph_index('w'); 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"))] #[ignore(cfg(target_os = "macos"))]
fn should_get_glyph_h_advance() { fn should_get_glyph_h_advance() {
with_test_native_font(|font| { 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); assert adv == Some(15);
}) })
} }
@ -66,7 +65,7 @@ fn should_get_glyph_h_advance() {
#[ignore(cfg(target_os = "macos"))] #[ignore(cfg(target_os = "macos"))]
fn should_return_none_glyph_h_advance_for_bad_codepoints() { fn should_return_none_glyph_h_advance_for_bad_codepoints() {
with_test_native_font(|font| { 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; assert adv == None;
}) })
} }

View file

@ -6,7 +6,7 @@ use au = gfx::geometry;
use libc::types::common::c99::int32_t; use libc::types::common::c99::int32_t;
use libc::{c_uint, c_int, c_void, c_char}; use libc::{c_uint, c_int, c_void, c_char};
use font::Font; use font::Font;
use glyph::{Glyph, GlyphPos}; use glyph::{Glyph, GlyphPos, GlyphIndex};
use ptr::{null, addr_of, offset}; use ptr::{null, addr_of, offset};
use gfx::geometry::au; use gfx::geometry::au;
use geom::point::Point2D; 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| { for uint::range(0u, info_len as uint) |i| {
let info_ = offset(info_, i); let info_ = offset(info_, i);
let pos = offset(pos, 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); let pos = hb_glyph_pos_to_servo_glyph_pos(&*pos);
#debug("glyph %?: codep %?, x_adv %?, y_adv %?, x_off %?, y_of %?", #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); 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); let font: *Font = reinterpret_cast(&font_data);
assert font.is_not_null(); 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); #debug("h_advance for codepoint %? is %?", glyph, h_advance);
return h_advance as hb_position_t; return h_advance as hb_position_t;
} }
@ -152,7 +152,7 @@ fn should_get_glyph_indexes() {
let font = lib.get_test_font(); let font = lib.get_test_font();
let glyphs = shape_text(font, ~"firecracker"); let glyphs = shape_text(font, ~"firecracker");
let idxs = glyphs.map(|glyph| glyph.index); 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() { fn should_get_glyph_h_advance() {