mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 13:21:38 +03:00
extract freetype font impl to its own module
This commit is contained in:
parent
a6fd1b000d
commit
ad55946f07
@ -22,6 +22,7 @@ font-loader = "0.8"
|
||||
rusttype = "0.7"
|
||||
clipboard = "0.5"
|
||||
unicode-normalization = "~0.1"
|
||||
freetype = "~0.4"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
harfbuzz-sys = "~0.2"
|
||||
@ -45,7 +46,6 @@ features = [
|
||||
version = "~0.3"
|
||||
|
||||
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
|
||||
freetype = "~0.4"
|
||||
servo-fontconfig = "~0.4"
|
||||
egli = "~0.4"
|
||||
x11 = {version ="~2.17", features = ["xlib_xcb"]}
|
||||
|
@ -1,17 +1,11 @@
|
||||
//! Systems that use fontconfig and freetype
|
||||
|
||||
pub use self::fcwrap::Pattern as FontPattern;
|
||||
use super::hbwrap as harfbuzz;
|
||||
use config::{Config, TextStyle};
|
||||
use failure::{self, Error};
|
||||
use font::ftfont::FreeTypeFontImpl;
|
||||
use font::{fcwrap, ftwrap};
|
||||
use font::{
|
||||
shape_with_harfbuzz, FallbackIdx, Font, FontMetrics, FontSystem, GlyphInfo, NamedFont,
|
||||
RasterizedGlyph,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::slice;
|
||||
use font::{shape_with_harfbuzz, FallbackIdx, Font, FontSystem, GlyphInfo, NamedFont};
|
||||
|
||||
pub type FontSystemImpl = FontConfigAndFreeType;
|
||||
|
||||
@ -53,267 +47,13 @@ impl FontSystem for FontConfigAndFreeType {
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a loaded font alternative
|
||||
struct FontImpl {
|
||||
face: RefCell<ftwrap::Face>,
|
||||
font: RefCell<harfbuzz::Font>,
|
||||
/// nominal monospace cell height
|
||||
cell_height: f64,
|
||||
/// nominal monospace cell width
|
||||
cell_width: f64,
|
||||
}
|
||||
|
||||
impl Font for FontImpl {
|
||||
fn harfbuzz_shape(
|
||||
&self,
|
||||
buf: &mut harfbuzz::Buffer,
|
||||
features: Option<&[harfbuzz::hb_feature_t]>,
|
||||
) {
|
||||
self.font.borrow_mut().shape(buf, features)
|
||||
}
|
||||
fn has_color(&self) -> bool {
|
||||
let face = self.face.borrow();
|
||||
unsafe { ((*face.face).face_flags & i64::from(ftwrap::FT_FACE_FLAG_COLOR)) != 0 }
|
||||
}
|
||||
|
||||
fn metrics(&self) -> FontMetrics {
|
||||
let face = self.face.borrow();
|
||||
FontMetrics {
|
||||
cell_height: self.cell_height,
|
||||
cell_width: self.cell_width,
|
||||
// Note: face.face.descender is useless, we have to go through
|
||||
// face.face.size.metrics to get to the real descender!
|
||||
descender: unsafe { (*(*face.face).size).metrics.descender as i16 },
|
||||
}
|
||||
}
|
||||
|
||||
fn rasterize_glyph(&self, glyph_pos: u32) -> Result<RasterizedGlyph, Error> {
|
||||
let render_mode = //ftwrap::FT_Render_Mode::FT_RENDER_MODE_NORMAL;
|
||||
// ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD;
|
||||
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT;
|
||||
|
||||
// when changing the load flags, we also need
|
||||
// to change them for harfbuzz otherwise it won't
|
||||
// hint correctly
|
||||
let load_flags = (ftwrap::FT_LOAD_COLOR) as i32 |
|
||||
// enable FT_LOAD_TARGET bits. There are no flags defined
|
||||
// for these in the bindings so we do some bit magic for
|
||||
// ourselves. This is how the FT_LOAD_TARGET_() macro
|
||||
// assembles these bits.
|
||||
(render_mode as i32) << 16;
|
||||
|
||||
self.font.borrow_mut().set_load_flags(load_flags);
|
||||
// This clone is conceptually unsafe, but ok in practice as we are
|
||||
// single threaded and don't load any other glyphs in the body of
|
||||
// this load_glyph() function.
|
||||
let mut face = self.face.borrow_mut();
|
||||
let ft_glyph = face.load_and_render_glyph(glyph_pos, load_flags, render_mode)?;
|
||||
|
||||
let mode: ftwrap::FT_Pixel_Mode =
|
||||
unsafe { mem::transmute(u32::from(ft_glyph.bitmap.pixel_mode)) };
|
||||
|
||||
// pitch is the number of bytes per source row
|
||||
let pitch = ft_glyph.bitmap.pitch.abs() as usize;
|
||||
let data = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
ft_glyph.bitmap.buffer,
|
||||
ft_glyph.bitmap.rows as usize * pitch,
|
||||
)
|
||||
};
|
||||
|
||||
let glyph = match mode {
|
||||
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
|
||||
let width = ft_glyph.bitmap.width as usize / 3;
|
||||
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);
|
||||
for y in 0..height {
|
||||
let src_offset = y * pitch as usize;
|
||||
let dest_offset = y * width * 4;
|
||||
for x in 0..width {
|
||||
let blue = data[src_offset + (x * 3)];
|
||||
let green = data[src_offset + (x * 3) + 1];
|
||||
let red = data[src_offset + (x * 3) + 2];
|
||||
let alpha = red | green | blue;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
RasterizedGlyph {
|
||||
data: rgba,
|
||||
height,
|
||||
width,
|
||||
bearing_x: ft_glyph.bitmap_left,
|
||||
bearing_y: ft_glyph.bitmap_top,
|
||||
}
|
||||
}
|
||||
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
|
||||
let width = ft_glyph.bitmap.width as usize;
|
||||
let height = ft_glyph.bitmap.rows as usize;
|
||||
|
||||
// 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;
|
||||
|
||||
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];
|
||||
|
||||
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: 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 => {
|
||||
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);
|
||||
for y in 0..height {
|
||||
let src_offset = y * pitch;
|
||||
let dest_offset = y * width * 4;
|
||||
for x in 0..width {
|
||||
let gray = data[src_offset + x];
|
||||
|
||||
rgba[dest_offset + (x * 4)] = gray;
|
||||
rgba[dest_offset + (x * 4) + 1] = gray;
|
||||
rgba[dest_offset + (x * 4) + 2] = gray;
|
||||
rgba[dest_offset + (x * 4) + 3] = gray;
|
||||
}
|
||||
}
|
||||
RasterizedGlyph {
|
||||
data: rgba,
|
||||
height,
|
||||
width,
|
||||
bearing_x: ft_glyph.bitmap_left,
|
||||
bearing_y: ft_glyph.bitmap_top,
|
||||
}
|
||||
}
|
||||
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
|
||||
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);
|
||||
for y in 0..height {
|
||||
let src_offset = y * pitch;
|
||||
let dest_offset = y * width * 4;
|
||||
let mut x = 0;
|
||||
for i in 0..pitch {
|
||||
if x >= width {
|
||||
break;
|
||||
}
|
||||
let mut b = data[src_offset + i];
|
||||
for _ in 0..8 {
|
||||
if x >= width {
|
||||
break;
|
||||
}
|
||||
if b & 0x80 == 0x80 {
|
||||
for j in 0..4 {
|
||||
rgba[dest_offset + (x * 4) + j] = 0xff;
|
||||
}
|
||||
}
|
||||
b <<= 1;
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
RasterizedGlyph {
|
||||
data: rgba,
|
||||
height,
|
||||
width,
|
||||
bearing_x: ft_glyph.bitmap_left,
|
||||
bearing_y: ft_glyph.bitmap_top,
|
||||
}
|
||||
}
|
||||
mode => bail!("unhandled pixel mode: {:?}", mode),
|
||||
};
|
||||
Ok(glyph)
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds "the" font selected by the user. In actuality, it
|
||||
/// holds the set of fallback fonts that match their criteria
|
||||
pub struct NamedFontImpl {
|
||||
lib: ftwrap::Library,
|
||||
pattern: fcwrap::Pattern,
|
||||
font_list: fcwrap::FontSet,
|
||||
fonts: Vec<FontImpl>,
|
||||
fonts: Vec<FreeTypeFontImpl>,
|
||||
}
|
||||
|
||||
impl Drop for NamedFontImpl {
|
||||
@ -378,60 +118,13 @@ impl NamedFontImpl {
|
||||
|
||||
let size = pat.get_double("size")?;
|
||||
let dpi = pat.get_double("dpi")? as u32;
|
||||
debug!("set_char_size {} dpi={}", size, dpi);
|
||||
// Scaling before truncating to integer minimizes the chances of hitting
|
||||
// the fallback code for set_pixel_sizes below.
|
||||
let size = (size * 64.0) as i64;
|
||||
|
||||
let mut face = self.lib.new_face(file, 0)?;
|
||||
|
||||
let (cell_width, cell_height) = match face.set_char_size(size, size, dpi, dpi) {
|
||||
Ok(_) => {
|
||||
// Compute metrics for the nominal monospace cell
|
||||
face.cell_metrics()
|
||||
}
|
||||
Err(err) => {
|
||||
let sizes = unsafe {
|
||||
let rec = &(*face.face);
|
||||
slice::from_raw_parts(rec.available_sizes, rec.num_fixed_sizes as usize)
|
||||
};
|
||||
if sizes.is_empty() {
|
||||
return Err(err);
|
||||
}
|
||||
// Find the best matching size.
|
||||
// We just take the biggest.
|
||||
let mut best = 0;
|
||||
let mut best_size = 0;
|
||||
let mut cell_width = 0;
|
||||
let mut cell_height = 0;
|
||||
|
||||
for (idx, info) in sizes.iter().enumerate() {
|
||||
let size = best_size.max(info.height);
|
||||
if size > best_size {
|
||||
best = idx;
|
||||
best_size = size;
|
||||
cell_width = info.width;
|
||||
cell_height = info.height;
|
||||
}
|
||||
}
|
||||
face.select_size(best)?;
|
||||
(cell_width as f64, cell_height as f64)
|
||||
}
|
||||
};
|
||||
|
||||
debug!("metrics: width={} height={}", cell_width, cell_height);
|
||||
let font = harfbuzz::Font::new(face.face);
|
||||
|
||||
self.fonts.push(FontImpl {
|
||||
face: RefCell::new(face),
|
||||
font: RefCell::new(font),
|
||||
cell_height,
|
||||
cell_width,
|
||||
});
|
||||
let face = self.lib.new_face(file, 0)?;
|
||||
self.fonts
|
||||
.push(FreeTypeFontImpl::with_face_size_and_dpi(face, size, dpi)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_font(&mut self, idx: usize) -> Result<&mut FontImpl, Error> {
|
||||
fn get_font(&mut self, idx: usize) -> Result<&mut FreeTypeFontImpl, Error> {
|
||||
if idx >= self.fonts.len() {
|
||||
self.load_next_fallback()?;
|
||||
ensure!(
|
||||
|
325
src/font/ftfont.rs
Normal file
325
src/font/ftfont.rs
Normal file
@ -0,0 +1,325 @@
|
||||
use failure::Error;
|
||||
use font::{ftwrap, Font, FontMetrics, RasterizedGlyph};
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::slice;
|
||||
|
||||
#[cfg(unix)]
|
||||
use super::hbwrap as harfbuzz;
|
||||
|
||||
/// Holds a loaded font alternative
|
||||
pub struct FreeTypeFontImpl {
|
||||
face: RefCell<ftwrap::Face>,
|
||||
#[cfg(unix)]
|
||||
font: RefCell<harfbuzz::Font>,
|
||||
/// nominal monospace cell height
|
||||
cell_height: f64,
|
||||
/// nominal monospace cell width
|
||||
cell_width: f64,
|
||||
}
|
||||
|
||||
impl FreeTypeFontImpl {
|
||||
pub fn with_face_size_and_dpi(
|
||||
mut face: ftwrap::Face,
|
||||
size: f64,
|
||||
dpi: u32,
|
||||
) -> Result<Self, Error> {
|
||||
debug!("set_char_size {} dpi={}", size, dpi);
|
||||
// Scaling before truncating to integer minimizes the chances of hitting
|
||||
// the fallback code for set_pixel_sizes below.
|
||||
let size = (size * 64.0) as ftwrap::FT_F26Dot6;
|
||||
|
||||
let (cell_width, cell_height) = match face.set_char_size(size, size, dpi, dpi) {
|
||||
Ok(_) => {
|
||||
// Compute metrics for the nominal monospace cell
|
||||
face.cell_metrics()
|
||||
}
|
||||
Err(err) => {
|
||||
let sizes = unsafe {
|
||||
let rec = &(*face.face);
|
||||
slice::from_raw_parts(rec.available_sizes, rec.num_fixed_sizes as usize)
|
||||
};
|
||||
if sizes.is_empty() {
|
||||
return Err(err);
|
||||
}
|
||||
// Find the best matching size.
|
||||
// We just take the biggest.
|
||||
let mut best = 0;
|
||||
let mut best_size = 0;
|
||||
let mut cell_width = 0;
|
||||
let mut cell_height = 0;
|
||||
|
||||
for (idx, info) in sizes.iter().enumerate() {
|
||||
let size = best_size.max(info.height);
|
||||
if size > best_size {
|
||||
best = idx;
|
||||
best_size = size;
|
||||
cell_width = info.width;
|
||||
cell_height = info.height;
|
||||
}
|
||||
}
|
||||
face.select_size(best)?;
|
||||
(cell_width as f64, cell_height as f64)
|
||||
}
|
||||
};
|
||||
|
||||
debug!("metrics: width={} height={}", cell_width, cell_height);
|
||||
#[cfg(unix)]
|
||||
let font = harfbuzz::Font::new(face.face);
|
||||
|
||||
Ok(FreeTypeFontImpl {
|
||||
face: RefCell::new(face),
|
||||
#[cfg(unix)]
|
||||
font: RefCell::new(font),
|
||||
cell_height,
|
||||
cell_width,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Font for FreeTypeFontImpl {
|
||||
#[cfg(unix)]
|
||||
fn harfbuzz_shape(
|
||||
&self,
|
||||
buf: &mut harfbuzz::Buffer,
|
||||
features: Option<&[harfbuzz::hb_feature_t]>,
|
||||
) {
|
||||
self.font.borrow_mut().shape(buf, features)
|
||||
}
|
||||
fn has_color(&self) -> bool {
|
||||
let face = self.face.borrow();
|
||||
unsafe { (i64::from((*face.face).face_flags) & i64::from(ftwrap::FT_FACE_FLAG_COLOR)) != 0 }
|
||||
}
|
||||
|
||||
fn metrics(&self) -> FontMetrics {
|
||||
let face = self.face.borrow();
|
||||
FontMetrics {
|
||||
cell_height: self.cell_height,
|
||||
cell_width: self.cell_width,
|
||||
// Note: face.face.descender is useless, we have to go through
|
||||
// face.face.size.metrics to get to the real descender!
|
||||
descender: unsafe { (*(*face.face).size).metrics.descender as i16 },
|
||||
}
|
||||
}
|
||||
|
||||
fn rasterize_glyph(&self, glyph_pos: u32) -> Result<RasterizedGlyph, Error> {
|
||||
let render_mode = //ftwrap::FT_Render_Mode::FT_RENDER_MODE_NORMAL;
|
||||
// ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD;
|
||||
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT;
|
||||
|
||||
// when changing the load flags, we also need
|
||||
// to change them for harfbuzz otherwise it won't
|
||||
// hint correctly
|
||||
let load_flags = (ftwrap::FT_LOAD_COLOR) as i32 |
|
||||
// enable FT_LOAD_TARGET bits. There are no flags defined
|
||||
// for these in the bindings so we do some bit magic for
|
||||
// ourselves. This is how the FT_LOAD_TARGET_() macro
|
||||
// assembles these bits.
|
||||
(render_mode as i32) << 16;
|
||||
|
||||
#[cfg(unix)]
|
||||
self.font.borrow_mut().set_load_flags(load_flags);
|
||||
|
||||
// This clone is conceptually unsafe, but ok in practice as we are
|
||||
// single threaded and don't load any other glyphs in the body of
|
||||
// this load_glyph() function.
|
||||
let mut face = self.face.borrow_mut();
|
||||
let ft_glyph = face.load_and_render_glyph(glyph_pos, load_flags, render_mode)?;
|
||||
|
||||
let mode: ftwrap::FT_Pixel_Mode =
|
||||
unsafe { mem::transmute(u32::from(ft_glyph.bitmap.pixel_mode)) };
|
||||
|
||||
// pitch is the number of bytes per source row
|
||||
let pitch = ft_glyph.bitmap.pitch.abs() as usize;
|
||||
let data = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
ft_glyph.bitmap.buffer,
|
||||
ft_glyph.bitmap.rows as usize * pitch,
|
||||
)
|
||||
};
|
||||
|
||||
let glyph = match mode {
|
||||
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
|
||||
let width = ft_glyph.bitmap.width as usize / 3;
|
||||
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);
|
||||
for y in 0..height {
|
||||
let src_offset = y * pitch as usize;
|
||||
let dest_offset = y * width * 4;
|
||||
for x in 0..width {
|
||||
let blue = data[src_offset + (x * 3)];
|
||||
let green = data[src_offset + (x * 3) + 1];
|
||||
let red = data[src_offset + (x * 3) + 2];
|
||||
let alpha = red | green | blue;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
RasterizedGlyph {
|
||||
data: rgba,
|
||||
height,
|
||||
width,
|
||||
bearing_x: ft_glyph.bitmap_left,
|
||||
bearing_y: ft_glyph.bitmap_top,
|
||||
}
|
||||
}
|
||||
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
|
||||
let width = ft_glyph.bitmap.width as usize;
|
||||
let height = ft_glyph.bitmap.rows as usize;
|
||||
|
||||
// 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;
|
||||
|
||||
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];
|
||||
|
||||
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: 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 => {
|
||||
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);
|
||||
for y in 0..height {
|
||||
let src_offset = y * pitch;
|
||||
let dest_offset = y * width * 4;
|
||||
for x in 0..width {
|
||||
let gray = data[src_offset + x];
|
||||
|
||||
rgba[dest_offset + (x * 4)] = gray;
|
||||
rgba[dest_offset + (x * 4) + 1] = gray;
|
||||
rgba[dest_offset + (x * 4) + 2] = gray;
|
||||
rgba[dest_offset + (x * 4) + 3] = gray;
|
||||
}
|
||||
}
|
||||
RasterizedGlyph {
|
||||
data: rgba,
|
||||
height,
|
||||
width,
|
||||
bearing_x: ft_glyph.bitmap_left,
|
||||
bearing_y: ft_glyph.bitmap_top,
|
||||
}
|
||||
}
|
||||
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
|
||||
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);
|
||||
for y in 0..height {
|
||||
let src_offset = y * pitch;
|
||||
let dest_offset = y * width * 4;
|
||||
let mut x = 0;
|
||||
for i in 0..pitch {
|
||||
if x >= width {
|
||||
break;
|
||||
}
|
||||
let mut b = data[src_offset + i];
|
||||
for _ in 0..8 {
|
||||
if x >= width {
|
||||
break;
|
||||
}
|
||||
if b & 0x80 == 0x80 {
|
||||
for j in 0..4 {
|
||||
rgba[dest_offset + (x * 4) + j] = 0xff;
|
||||
}
|
||||
}
|
||||
b <<= 1;
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
RasterizedGlyph {
|
||||
data: rgba,
|
||||
height,
|
||||
width,
|
||||
bearing_x: ft_glyph.bitmap_left,
|
||||
bearing_y: ft_glyph.bitmap_top,
|
||||
}
|
||||
}
|
||||
mode => bail!("unhandled pixel mode: {:?}", mode),
|
||||
};
|
||||
Ok(glyph)
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use failure::Error;
|
||||
mod ftfont;
|
||||
#[cfg(unix)]
|
||||
mod hbwrap;
|
||||
#[cfg(unix)]
|
||||
@ -11,10 +12,6 @@ use std::rc::Rc;
|
||||
pub mod system;
|
||||
pub use self::system::*;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(unix, not(feature = "force-rusttype"), not(target_os = "macos"))
|
||||
))]
|
||||
pub mod ftwrap;
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos"), not(feature = "force-rusttype")))]
|
||||
|
@ -9,7 +9,6 @@ extern crate failure;
|
||||
extern crate failure_derive;
|
||||
#[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
|
||||
extern crate fontconfig; // from servo-fontconfig
|
||||
#[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
|
||||
extern crate freetype;
|
||||
extern crate futures;
|
||||
extern crate gl;
|
||||
|
Loading…
Reference in New Issue
Block a user