1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-22 22:42:48 +03:00

scale emoji bitmaps to match size of preferred font

This commit is contained in:
Wez Furlong 2018-01-16 00:24:49 -08:00
parent 2d5c54a642
commit a5535d4c5b
2 changed files with 146 additions and 72 deletions

View File

@ -59,11 +59,6 @@ impl Face {
(), (),
) )
} }
pub fn has_codepoint(&self, cp: char) -> bool {
unsafe {
FT_Get_Char_Index(self.face, cp as u64) != 0
}
}
pub fn load_and_render_glyph( pub fn load_and_render_glyph(
&mut self, &mut self,
@ -79,6 +74,31 @@ impl Face {
ft_result(res, &*(*self.face).glyph) ft_result(res, &*(*self.face).glyph)
} }
} }
pub fn cell_metrics(&mut self) -> (i64, i64) {
unsafe {
let metrics = &(*(*self.face).size).metrics;
let height = FT_MulFix(metrics.y_scale, (*self.face).height as i64) / 64;
let mut width = 0;
for i in 32..128 {
let glyph_pos = FT_Get_Char_Index(self.face, i);
let res =
FT_Load_Glyph(self.face, glyph_pos, FT_LOAD_COLOR as i32);
if res.succeeded() {
let glyph = &(*(*self.face).glyph);
// FIXME: I don't like this +1, but without it
// we can end up with a width 1 pixel too small
width = width.max(
((glyph.metrics.horiAdvance as f64) /
64f64)
.ceil() as i64 + 1,
);
}
}
(width, height)
}
}
} }
pub struct Library { pub struct Library {

View File

@ -65,79 +65,113 @@ impl<'a> Glyph<'a> {
texture_creator: &'a TextureCreator<T>, texture_creator: &'a TextureCreator<T>,
glyph: &ftwrap::FT_GlyphSlotRec_, glyph: &ftwrap::FT_GlyphSlotRec_,
info: &GlyphInfo, info: &GlyphInfo,
target_cell_height: i64,
target_cell_width: i64,
) -> Result<Glyph<'a>, Error> { ) -> Result<Glyph<'a>, Error> {
// Special case for zero sized bitmaps; we can't let mut info = info.clone();
// build a texture with zero dimenions, so we return let mut tex = None;
// a Glyph with tex=None. let mut width = glyph.bitmap.width as u32;
if glyph.bitmap.width == 0 || glyph.bitmap.rows == 0 { let mut height = glyph.bitmap.rows as u32;
return Ok(Glyph { let has_color = false;
has_color: false,
tex: None, if width == 0 || height == 0 {
width: glyph.bitmap.width, // Special case for zero sized bitmaps; we can't
height: glyph.bitmap.rows, // build a texture with zero dimenions, so we return
info: info.clone(), // a Glyph with tex=None.
bearing_x: glyph.bitmap_left, tex = None;
bearing_y: glyph.bitmap_top, } else {
});
let mode: ftwrap::FT_Pixel_Mode =
unsafe { mem::transmute(glyph.bitmap.pixel_mode as u32) };
// pitch is the number of bytes per source row
let pitch = glyph.bitmap.pitch.abs() as usize;
let data = unsafe {
slice::from_raw_parts(
glyph.bitmap.buffer,
height as usize * pitch,
)
};
let mut t = match mode {
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
width = width / 3;
texture_creator
.create_texture_static(
Some(PixelFormatEnum::BGR24),
width,
height,
)
.map_err(failure::err_msg)?
}
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
texture_creator
.create_texture_static(
// Note: the pixelformat is BGRA and we really
// want to use BGRA32 as the PixelFormatEnum
// value (which is endian corrected) but that
// is missing. Instead, we have to go to the
// lower level pixel format value and handle
// the endianness for ourselves
Some(if cfg!(target_endian = "big") {
PixelFormatEnum::BGRA8888
} else {
PixelFormatEnum::ARGB8888
}),
width,
height,
)
.map_err(failure::err_msg)?
}
mode @ _ => bail!("unhandled pixel mode: {:?}", mode),
};
t.update(None, data, pitch)?;
tex = Some(t);
} }
let mode: ftwrap::FT_Pixel_Mode = let mut bearing_x = glyph.bitmap_left;
unsafe { mem::transmute(glyph.bitmap.pixel_mode as u32) }; let mut bearing_y = glyph.bitmap_top;
// pitch is the number of bytes per source row let scale = if height as i64 > target_cell_height {
let pitch = glyph.bitmap.pitch.abs() as usize; target_cell_height as f64 / height as f64
let height = glyph.bitmap.rows as usize; } else if (width / info.text.chars().count() as u32) as i64 >
let data = unsafe { target_cell_width
slice::from_raw_parts(glyph.bitmap.buffer, height as usize * pitch) {
target_cell_width as f64 / width as f64
} else {
1.0f64
}; };
let (width, mut tex) = match mode { if scale != 1.0f64 {
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_LCD => { println!(
let width = (glyph.bitmap.width as usize) / 3; "scaling {:?} w={} {}, h={} {} by {}",
let tex = texture_creator info,
.create_texture_static( width,
Some(PixelFormatEnum::BGR24), target_cell_width,
width as u32, height,
height as u32, target_cell_height,
) scale
.map_err(failure::err_msg)?; );
(width, tex) width = (width as f64 * scale) as u32;
} height = (height as f64 * scale) as u32;
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => { bearing_x = (bearing_x as f64 * scale) as i32;
let width = glyph.bitmap.width as usize; bearing_y = (bearing_y as f64 * scale) as i32;
let tex = texture_creator info.x_advance = (info.x_advance as f64 * scale) as i32;
.create_texture_static( info.y_advance = (info.y_advance as f64 * scale) as i32;
// Note: the pixelformat is BGRA and we really info.x_offset = (info.x_offset as f64 * scale) as i32;
// want to use BGRA32 as the PixelFormatEnum info.y_offset = (info.y_offset as f64 * scale) as i32;
// value (which is endian corrected) but that }
// is missing. Instead, we have to go to the
// lower level pixel format value and handle
// the endianness for ourselves
Some(if cfg!(target_endian = "big") {
PixelFormatEnum::BGRA8888
} else {
PixelFormatEnum::ARGB8888
}),
width as u32,
height as u32,
)
.map_err(failure::err_msg)?;
(width, tex)
}
mode @ _ => bail!("unhandled pixel mode: {:?}", mode),
};
tex.update(None, data, pitch)?;
Ok(Glyph { Ok(Glyph {
has_color: false, has_color: false,
tex: Some(tex), tex: tex,
width: width as u32, width,
height: height as u32, height,
info: info.clone(), info,
bearing_x: glyph.bitmap_left, bearing_x,
bearing_y: glyph.bitmap_top, bearing_y,
}) })
} }
} }
@ -145,6 +179,8 @@ impl<'a> Glyph<'a> {
struct FontInfo { struct FontInfo {
face: ftwrap::Face, face: ftwrap::Face,
font: hbwrap::Font, font: hbwrap::Font,
cell_height: i64,
cell_width: i64,
} }
struct FontHolder { struct FontHolder {
@ -222,7 +258,16 @@ impl FontHolder {
} }
let font = hbwrap::Font::new(&face); let font = hbwrap::Font::new(&face);
self.fonts.push(FontInfo { face, font }); // Compute metrics for the nominal monospace cell
let (cell_width, cell_height) = face.cell_metrics();
println!("metrics: width={} height={}", cell_width, cell_height);
self.fonts.push(FontInfo {
face,
font,
cell_height,
cell_width,
});
Ok(()) Ok(())
} }
@ -387,7 +432,15 @@ fn glyphs_for_text<'a, T>(
s: &str, s: &str,
) -> Result<Vec<Glyph<'a>>, Error> { ) -> Result<Vec<Glyph<'a>>, Error> {
let mut font_holder = FontHolder::new(36)?; let mut font_holder = FontHolder::new(16)?;
// We always load the cell_height for font 0,
// regardless of which font we are shaping here,
// so that we can scale glyphs appropriately
let (cell_height, cell_width) = {
let font = font_holder.get_font(0)?;
(font.cell_height, font.cell_width)
};
let mut result = Vec::new(); let mut result = Vec::new();
for info in font_holder.shape(0, s)? { for info in font_holder.shape(0, s)? {
@ -398,7 +451,8 @@ fn glyphs_for_text<'a, T>(
let glyph = font_holder.load_glyph(info.font_idx, info.glyph_pos)?; let glyph = font_holder.load_glyph(info.font_idx, info.glyph_pos)?;
let g = Glyph::new(texture_creator, glyph, &info)?; let g =
Glyph::new(texture_creator, glyph, &info, cell_height, cell_width)?;
result.push(g); result.push(g);
} }
Ok(result) Ok(result)
@ -444,7 +498,7 @@ fn run() -> Result<(), Error> {
canvas canvas
.copy( .copy(
&tex, &tex,
Some(Rect::new(0, 0, g.width, g.height)), None,
Some(Rect::new( Some(Rect::new(
x + g.info.x_offset - g.bearing_x, x + g.info.x_offset - g.bearing_x,
y - (g.info.y_offset + g.bearing_y as i32) as i32, y - (g.info.y_offset + g.bearing_y as i32) as i32,