1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-11 14:25:57 +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(
&mut self,
@ -79,6 +74,31 @@ impl Face {
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 {

View File

@ -65,79 +65,113 @@ impl<'a> Glyph<'a> {
texture_creator: &'a TextureCreator<T>,
glyph: &ftwrap::FT_GlyphSlotRec_,
info: &GlyphInfo,
target_cell_height: i64,
target_cell_width: i64,
) -> Result<Glyph<'a>, Error> {
// Special case for zero sized bitmaps; we can't
// build a texture with zero dimenions, so we return
// a Glyph with tex=None.
if glyph.bitmap.width == 0 || glyph.bitmap.rows == 0 {
return Ok(Glyph {
has_color: false,
tex: None,
width: glyph.bitmap.width,
height: glyph.bitmap.rows,
info: info.clone(),
bearing_x: glyph.bitmap_left,
bearing_y: glyph.bitmap_top,
});
let mut info = info.clone();
let mut tex = None;
let mut width = glyph.bitmap.width as u32;
let mut height = glyph.bitmap.rows as u32;
let has_color = false;
if width == 0 || height == 0 {
// Special case for zero sized bitmaps; we can't
// build a texture with zero dimenions, so we return
// a Glyph with tex=None.
tex = None;
} 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 =
unsafe { mem::transmute(glyph.bitmap.pixel_mode as u32) };
let mut bearing_x = glyph.bitmap_left;
let mut bearing_y = glyph.bitmap_top;
// pitch is the number of bytes per source row
let pitch = glyph.bitmap.pitch.abs() as usize;
let height = glyph.bitmap.rows as usize;
let data = unsafe {
slice::from_raw_parts(glyph.bitmap.buffer, height as usize * pitch)
let scale = if height as i64 > target_cell_height {
target_cell_height as f64 / height as f64
} else if (width / info.text.chars().count() as u32) as i64 >
target_cell_width
{
target_cell_width as f64 / width as f64
} else {
1.0f64
};
let (width, mut tex) = match mode {
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
let width = (glyph.bitmap.width as usize) / 3;
let tex = texture_creator
.create_texture_static(
Some(PixelFormatEnum::BGR24),
width as u32,
height as u32,
)
.map_err(failure::err_msg)?;
(width, tex)
}
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
let width = glyph.bitmap.width as usize;
let tex = 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 as u32,
height as u32,
)
.map_err(failure::err_msg)?;
(width, tex)
}
mode @ _ => bail!("unhandled pixel mode: {:?}", mode),
};
tex.update(None, data, pitch)?;
if scale != 1.0f64 {
println!(
"scaling {:?} w={} {}, h={} {} by {}",
info,
width,
target_cell_width,
height,
target_cell_height,
scale
);
width = (width as f64 * scale) as u32;
height = (height as f64 * scale) as u32;
bearing_x = (bearing_x as f64 * scale) as i32;
bearing_y = (bearing_y as f64 * scale) as i32;
info.x_advance = (info.x_advance as f64 * scale) as i32;
info.y_advance = (info.y_advance as f64 * scale) as i32;
info.x_offset = (info.x_offset as f64 * scale) as i32;
info.y_offset = (info.y_offset as f64 * scale) as i32;
}
Ok(Glyph {
has_color: false,
tex: Some(tex),
width: width as u32,
height: height as u32,
info: info.clone(),
bearing_x: glyph.bitmap_left,
bearing_y: glyph.bitmap_top,
tex: tex,
width,
height,
info,
bearing_x,
bearing_y,
})
}
}
@ -145,6 +179,8 @@ impl<'a> Glyph<'a> {
struct FontInfo {
face: ftwrap::Face,
font: hbwrap::Font,
cell_height: i64,
cell_width: i64,
}
struct FontHolder {
@ -222,7 +258,16 @@ impl FontHolder {
}
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(())
}
@ -387,7 +432,15 @@ fn glyphs_for_text<'a, T>(
s: &str,
) -> 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();
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 g = Glyph::new(texture_creator, glyph, &info)?;
let g =
Glyph::new(texture_creator, glyph, &info, cell_height, cell_width)?;
result.push(g);
}
Ok(result)
@ -444,7 +498,7 @@ fn run() -> Result<(), Error> {
canvas
.copy(
&tex,
Some(Rect::new(0, 0, g.width, g.height)),
None,
Some(Rect::new(
x + g.info.x_offset - g.bearing_x,
y - (g.info.y_offset + g.bearing_y as i32) as i32,