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:
parent
2d5c54a642
commit
a5535d4c5b
@ -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 {
|
||||||
|
188
src/main.rs
188
src/main.rs
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user