Skip to content

Commit

Permalink
Correct vertical offset for Apple Color Emoji
Browse files Browse the repository at this point in the history
Experimentation indicates that CoreText special cases the Apple Color Emoji font and adds a 100 font unit vertical offset when rendering. This PR adds a hack to mimic that behavior for correct emoji placement on Mac.

Also fixes a tiny general sbix bug where we were adding rather than subtracting the height of the image.
  • Loading branch information
dfrg committed Jan 17, 2025
1 parent 0e79178 commit eb93e7e
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 10 deletions.
2 changes: 1 addition & 1 deletion vello/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ impl<'a> DrawGlyphs<'a> {
bitmap::Origin::TopLeft => transform,
bitmap::Origin::BottomLeft => transform.pre_translate(Vec2 {
x: 0.,
y: f64::from(image.height),
y: -f64::from(image.height),
}),
};
if let Some(glyph_transform) = self.run.glyph_transform {
Expand Down
54 changes: 45 additions & 9 deletions vello/src/scene/bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use skrifa::{
types::{GlyphId, Tag},
FontData, TableProvider,
},
MetadataProvider,
string::StringId,
FontRef, MetadataProvider,
};

/// Set of strikes, each containing embedded bitmaps of a single size.
Expand Down Expand Up @@ -45,6 +46,7 @@ impl<'a> BitmapStrikes<'a> {
let kind = match format {
BitmapFormat::Sbix => StrikesKind::Sbix(
font.sbix().ok()?,
SbixKind::from_font(font),
font.glyph_metrics(Size::unscaled(), LocationRef::default()),
),
BitmapFormat::Cbdt => {
Expand Down Expand Up @@ -72,7 +74,7 @@ impl<'a> BitmapStrikes<'a> {
pub fn len(&self) -> usize {
match &self.0 {
StrikesKind::None => 0,
StrikesKind::Sbix(sbix, _) => sbix.strikes().len(),
StrikesKind::Sbix(sbix, ..) => sbix.strikes().len(),
StrikesKind::Cbdt(cbdt) => cbdt.location.bitmap_sizes().len(),
StrikesKind::Ebdt(ebdt) => ebdt.location.bitmap_sizes().len(),
}
Expand All @@ -87,8 +89,8 @@ impl<'a> BitmapStrikes<'a> {
pub fn get(&self, index: usize) -> Option<BitmapStrike<'a>> {
let kind = match &self.0 {
StrikesKind::None => return None,
StrikesKind::Sbix(sbix, metrics) => {
StrikeKind::Sbix(sbix.strikes().get(index).ok()?, metrics.clone())
StrikesKind::Sbix(sbix, kind, metrics) => {
StrikeKind::Sbix(sbix.strikes().get(index).ok()?, *kind, metrics.clone())
}
StrikesKind::Cbdt(tables) => StrikeKind::Cbdt(
tables.location.bitmap_sizes().get(index).copied()?,
Expand Down Expand Up @@ -138,11 +140,32 @@ impl<'a> BitmapStrikes<'a> {
#[derive(Clone)]
enum StrikesKind<'a> {
None,
Sbix(sbix::Sbix<'a>, GlyphMetrics<'a>),
Sbix(sbix::Sbix<'a>, SbixKind, GlyphMetrics<'a>),
Cbdt(CbdtTables<'a>),
Ebdt(EbdtTables<'a>),
}

#[derive(Copy, Clone, PartialEq)]
enum SbixKind {
Apple,
Other,
}

impl SbixKind {
fn from_font<'a>(font: &impl skrifa::MetadataProvider<'a>) -> Self {
if font
.localized_strings(skrifa::string::StringId::POSTSCRIPT_NAME)
.next()
.map(|s| s.chars().eq("AppleColorEmoji".chars()))
.unwrap_or_default()
{
Self::Apple
} else {
Self::Other
}
}
}

/// Set of embedded bitmap glyphs of a specific size.
#[derive(Clone)]
pub struct BitmapStrike<'a>(StrikeKind<'a>);
Expand All @@ -151,7 +174,7 @@ impl<'a> BitmapStrike<'a> {
/// Returns the pixels-per-em (size) of this strike.
pub fn ppem(&self) -> f32 {
match &self.0 {
StrikeKind::Sbix(sbix, _) => sbix.ppem() as f32,
StrikeKind::Sbix(sbix, ..) => sbix.ppem() as f32,
StrikeKind::Cbdt(size, _) => size.ppem_y() as f32,
StrikeKind::Ebdt(size, _) => size.ppem_y() as f32,
}
Expand All @@ -160,7 +183,7 @@ impl<'a> BitmapStrike<'a> {
/// Returns a bitmap glyph for the given identifier, if available.
pub fn get(&self, glyph_id: GlyphId) -> Option<BitmapGlyph<'a>> {
match &self.0 {
StrikeKind::Sbix(sbix, metrics) => {
StrikeKind::Sbix(sbix, kind, metrics) => {
let glyph = sbix.glyph_data(glyph_id).ok()??;
if glyph.graphic_type() != Tag::new(b"png ") {
return None;
Expand All @@ -174,10 +197,23 @@ impl<'a> BitmapStrike<'a> {
let reader = FontData::new(png_data);
let width = reader.read_at::<u32>(16).ok()?;
let height = reader.read_at::<u32>(20).ok()?;
// CoreText appears to special case Apple Color Emoji, adding
// a 100 font unit vertical offset. We do the same but only
// when both vertical offsets are 0 to avoid incorrect
// rendering if Apple ever does encode the offset directly in
// the font.
let bearing_y = if glyf_bb.y_min == 0.0
&& glyph.origin_offset_y() == 0
&& *kind == SbixKind::Apple
{
100.0
} else {
glyf_bb.y_min
};
Some(BitmapGlyph {
data: BitmapData::Png(glyph.data()),
bearing_x: lsb,
bearing_y: glyf_bb.y_min as f32,
bearing_y,
inner_bearing_x: glyph.origin_offset_x() as f32,
inner_bearing_y: glyph.origin_offset_y() as f32,
ppem_x: ppem,
Expand Down Expand Up @@ -208,7 +244,7 @@ impl<'a> BitmapStrike<'a> {

#[derive(Clone)]
enum StrikeKind<'a> {
Sbix(sbix::Strike<'a>, GlyphMetrics<'a>),
Sbix(sbix::Strike<'a>, SbixKind, GlyphMetrics<'a>),
Cbdt(bitmap::BitmapSize, CbdtTables<'a>),
Ebdt(bitmap::BitmapSize, EbdtTables<'a>),
}
Expand Down

0 comments on commit eb93e7e

Please sign in to comment.