diff --git a/src/font/fontconfigandfreetype.rs b/src/font/fontconfigandfreetype.rs index 571c14782..3e528d8ae 100644 --- a/src/font/fontconfigandfreetype.rs +++ b/src/font/fontconfigandfreetype.rs @@ -134,30 +134,88 @@ impl Font for FontImpl { ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => { let width = ft_glyph.bitmap.width as usize; let height = ft_glyph.bitmap.rows as usize; - let size = (width * height * 4) as usize; - let mut rgba = Vec::with_capacity(size); - rgba.resize(size, 0u8); + + // emoji glyphs don't always fill the bitmap size, so we compute + // the non-transparent bounds here with this simplistic code. + // This can likely be improved! + + let mut first_line = None; + let mut first_col = None; + let mut last_col = None; + let mut last_line = None; + for y in 0..height { let src_offset = y * pitch as usize; - let dest_offset = y * width * 4; + for x in 0..width { + let alpha = data[src_offset + (x * 4) + 3]; + if alpha != 0 { + if first_line.is_none() { + first_line = Some(y); + } + first_col = match first_col.take() { + Some(other) if x < other => Some(x), + Some(other) => Some(other), + None => Some(x), + }; + } + } + } + for y in (0..height).rev() { + let src_offset = y * pitch as usize; + + for x in (0..width).rev() { + let alpha = data[src_offset + (x * 4) + 3]; + if alpha != 0 { + if last_line.is_none() { + last_line = Some(y); + } + last_col = match last_col.take() { + Some(other) if x > other => Some(x), + Some(other) => Some(other), + None => Some(x), + }; + } + } + } + + let first_line = first_line.unwrap_or(0); + let last_line = last_line.unwrap_or(0); + let first_col = first_col.unwrap_or(0); + let last_col = last_col.unwrap_or(0); + + let dest_width = 1 + last_col - first_col; + let dest_height = 1 + last_line - first_line; + + let size = (dest_width * dest_height * 4) as usize; + let mut rgba = Vec::with_capacity(size); + rgba.resize(size, 0u8); + + for y in first_line..=last_line { + let src_offset = y * pitch as usize; + let dest_offset = (y - first_line) * dest_width * 4; + for x in first_col..=last_col { let blue = data[src_offset + (x * 4)]; let green = data[src_offset + (x * 4) + 1]; let red = data[src_offset + (x * 4) + 2]; let alpha = data[src_offset + (x * 4) + 3]; - rgba[dest_offset + (x * 4)] = red; - rgba[dest_offset + (x * 4) + 1] = green; - rgba[dest_offset + (x * 4) + 2] = blue; - rgba[dest_offset + (x * 4) + 3] = alpha; + let dest_x = x - first_col; + + rgba[dest_offset + (dest_x * 4)] = red; + rgba[dest_offset + (dest_x * 4) + 1] = green; + rgba[dest_offset + (dest_x * 4) + 2] = blue; + rgba[dest_offset + (dest_x * 4) + 3] = alpha; } } RasterizedGlyph { data: rgba, - height, - width, - bearing_x: ft_glyph.bitmap_left, - bearing_y: ft_glyph.bitmap_top, + height: dest_height, + width: dest_width, + bearing_x: (ft_glyph.bitmap_left as f64 * (dest_width as f64 / width as f64)) + as i32, + bearing_y: (ft_glyph.bitmap_top as f64 * (dest_height as f64 / height as f64)) + as i32, } } ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => { diff --git a/src/opengl/render.rs b/src/opengl/render.rs index a40e708af..b14a44429 100644 --- a/src/opengl/render.rs +++ b/src/opengl/render.rs @@ -441,7 +441,7 @@ impl Renderer { /// Perform the load and render of a glyph fn load_glyph(&self, info: &GlyphInfo, style: &TextStyle) -> Result, Error> { - let (has_color, glyph, cell_width, cell_height) = { + let (has_color, glyph, cell_width, _cell_height) = { let font = self.fonts.cached_font(style)?; let mut font = font.borrow_mut(); let metrics = font.get_fallback(0)?.metrics(); @@ -451,13 +451,14 @@ impl Renderer { (has_color, glyph, metrics.cell_width, metrics.cell_height) }; - let scale = if (info.x_advance / f64::from(info.num_cells)).floor() > cell_width { - f64::from(info.num_cells) * (cell_width / info.x_advance) - } else if glyph.height as f64 > cell_height { - cell_height / glyph.height as f64 + let effective_width = f64::from(info.num_cells) * cell_width; + + let scale = if glyph.width as f64 > effective_width { + effective_width / glyph.width as f64 } else { 1.0f64 }; + #[cfg_attr(feature = "cargo-clippy", allow(float_cmp))] let (x_offset, y_offset) = if scale != 1.0 { (info.x_offset * scale, info.y_offset * scale)