diff options
Diffstat (limited to 'font/src/ft/mod.rs')
-rw-r--r-- | font/src/ft/mod.rs | 787 |
1 files changed, 0 insertions, 787 deletions
diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs deleted file mode 100644 index dd15c4bb..00000000 --- a/font/src/ft/mod.rs +++ /dev/null @@ -1,787 +0,0 @@ -//! Rasterization powered by FreeType and Fontconfig. - -use std::cmp::{min, Ordering}; -use std::collections::HashMap; -use std::fmt::{self, Display, Formatter}; -use std::rc::Rc; - -use freetype::face::LoadFlag; -use freetype::tt_os2::TrueTypeOS2Table; -use freetype::{self, Library, Matrix}; -use freetype::{freetype_sys, Face as FTFace}; -use libc::{c_long, c_uint}; -use log::{debug, trace}; - -pub mod fc; - -use fc::{CharSet, FTFaceLocation, Pattern, PatternHash, PatternRef}; - -use super::{ - BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, Rasterize, RasterizedGlyph, Size, Slant, - Style, Weight, -}; - -struct FallbackFont { - pattern: Pattern, - key: FontKey, -} - -impl FallbackFont { - fn new(pattern: Pattern, key: FontKey) -> FallbackFont { - Self { pattern, key } - } -} - -impl FontKey { - fn from_pattern_hashes(lhs: PatternHash, rhs: PatternHash) -> Self { - // XOR two hashes to get a font ID. - Self { token: lhs.0.rotate_left(1) ^ rhs.0 } - } -} - -#[derive(Default)] -struct FallbackList { - list: Vec<FallbackFont>, - coverage: CharSet, -} - -struct FaceLoadingProperties { - load_flags: LoadFlag, - render_mode: freetype::RenderMode, - lcd_filter: c_uint, - non_scalable: Option<f32>, - colored: bool, - embolden: bool, - matrix: Option<Matrix>, - pixelsize_fixup_factor: Option<f64>, - ft_face: Rc<FTFace>, -} - -impl fmt::Debug for FaceLoadingProperties { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("Face") - .field("ft_face", &self.ft_face) - .field("load_flags", &self.load_flags) - .field("render_mode", &match self.render_mode { - freetype::RenderMode::Normal => "Normal", - freetype::RenderMode::Light => "Light", - freetype::RenderMode::Mono => "Mono", - freetype::RenderMode::Lcd => "Lcd", - freetype::RenderMode::LcdV => "LcdV", - freetype::RenderMode::Max => "Max", - }) - .field("lcd_filter", &self.lcd_filter) - .finish() - } -} - -/// Rasterizes glyphs for a single font face. -pub struct FreeTypeRasterizer { - library: Library, - faces: HashMap<FontKey, FaceLoadingProperties>, - ft_faces: HashMap<FTFaceLocation, Rc<FTFace>>, - fallback_lists: HashMap<FontKey, FallbackList>, - device_pixel_ratio: f32, -} - -#[inline] -fn to_freetype_26_6(f: f32) -> isize { - ((1i32 << 6) as f32 * f).round() as isize -} - -#[inline] -fn to_fixedpoint_16_6(f: f64) -> c_long { - (f * 65536.0) as c_long -} - -impl Rasterize for FreeTypeRasterizer { - type Err = Error; - - fn new(device_pixel_ratio: f32, _: bool) -> Result<FreeTypeRasterizer, Error> { - let library = Library::init()?; - - unsafe { - // Initialize default properties, like user preferred interpreter. - freetype_sys::FT_Set_Default_Properties(library.raw()); - }; - - Ok(FreeTypeRasterizer { - faces: HashMap::new(), - ft_faces: HashMap::new(), - fallback_lists: HashMap::new(), - library, - device_pixel_ratio, - }) - } - - fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> { - let face = &mut self.faces.get(&key).ok_or(Error::FontNotLoaded)?; - let full = self.full_metrics(&face)?; - - let height = (full.size_metrics.height / 64) as f64; - let descent = (full.size_metrics.descender / 64) as f32; - - // Get underline position and thickness in device pixels. - let x_scale = full.size_metrics.x_scale as f32 / 65536.0; - let mut underline_position = f32::from(face.ft_face.underline_position()) * x_scale / 64.; - let mut underline_thickness = f32::from(face.ft_face.underline_thickness()) * x_scale / 64.; - - // Fallback for bitmap fonts which do not provide underline metrics. - if underline_position == 0. { - underline_thickness = (descent.abs() / 5.).round(); - underline_position = descent / 2.; - } - - // Get strikeout position and thickness in device pixels. - let (strikeout_position, strikeout_thickness) = - match TrueTypeOS2Table::from_face(&mut (*face.ft_face).clone()) { - Some(os2) => { - let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.; - let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.; - (strikeout_position, strikeout_thickness) - }, - _ => { - // Fallback if font doesn't provide info about strikeout. - trace!("Using fallback strikeout metrics"); - let strikeout_position = height as f32 / 2. + descent; - (strikeout_position, underline_thickness) - }, - }; - - Ok(Metrics { - average_advance: full.cell_width, - line_height: height, - descent, - underline_position, - underline_thickness, - strikeout_position, - strikeout_thickness, - }) - } - - fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> { - self.get_face(desc, size) - } - - fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> { - self.get_rendered_glyph(glyph_key) - } - - fn update_dpr(&mut self, device_pixel_ratio: f32) { - self.device_pixel_ratio = device_pixel_ratio; - } -} - -pub trait IntoFontconfigType { - type FcType; - fn into_fontconfig_type(&self) -> Self::FcType; -} - -impl IntoFontconfigType for Slant { - type FcType = fc::Slant; - - fn into_fontconfig_type(&self) -> Self::FcType { - match *self { - Slant::Normal => fc::Slant::Roman, - Slant::Italic => fc::Slant::Italic, - Slant::Oblique => fc::Slant::Oblique, - } - } -} - -impl IntoFontconfigType for Weight { - type FcType = fc::Weight; - - fn into_fontconfig_type(&self) -> Self::FcType { - match *self { - Weight::Normal => fc::Weight::Regular, - Weight::Bold => fc::Weight::Bold, - } - } -} - -struct FullMetrics { - size_metrics: freetype::ffi::FT_Size_Metrics, - cell_width: f64, -} - -impl FreeTypeRasterizer { - /// Load a font face according to `FontDesc`. - fn get_face(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> { - // Adjust for DPR. - let size = f64::from(size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); - - let config = fc::Config::get_current(); - let mut pattern = Pattern::new(); - pattern.add_family(&desc.name); - pattern.add_pixelsize(size); - - // Add style to a pattern. - match desc.style { - Style::Description { slant, weight } => { - // Match nearest font. - pattern.set_weight(weight.into_fontconfig_type()); - pattern.set_slant(slant.into_fontconfig_type()); - }, - Style::Specific(ref style) => { - // If a name was specified, try and load specifically that font. - pattern.add_style(style); - }, - } - - // Hash requested pattern. - let hash = pattern.hash(); - - pattern.config_substitute(config, fc::MatchKind::Pattern); - pattern.default_substitute(); - - // Get font list using pattern. First font is the primary one while the rest are fallbacks. - let matched_fonts = - fc::font_sort(&config, &pattern).ok_or_else(|| Error::MissingFont(desc.to_owned()))?; - let mut matched_fonts = matched_fonts.into_iter(); - - let primary_font = - matched_fonts.next().ok_or_else(|| Error::MissingFont(desc.to_owned()))?; - - // We should render patterns to get values like `pixelsizefixupfactor`. - let primary_font = pattern.render_prepare(config, primary_font); - - // Hash pattern together with request pattern to include requested font size in the hash. - let primary_font_key = FontKey::from_pattern_hashes(hash, primary_font.hash()); - - // Return if we already have the same primary font. - if self.fallback_lists.contains_key(&primary_font_key) { - return Ok(primary_font_key); - } - - // Load font if we haven't loaded it yet. - if !self.faces.contains_key(&primary_font_key) { - self.face_from_pattern(&primary_font, primary_font_key) - .and_then(|pattern| pattern.ok_or_else(|| Error::MissingFont(desc.to_owned())))?; - } - - // Coverage for fallback fonts. - let coverage = CharSet::new(); - let empty_charset = CharSet::new(); - - let list: Vec<FallbackFont> = matched_fonts - .map(|fallback_font| { - let charset = fallback_font.get_charset().unwrap_or(&empty_charset); - - // Use original pattern to preserve loading flags. - let fallback_font = pattern.render_prepare(config, fallback_font); - let fallback_font_key = FontKey::from_pattern_hashes(hash, fallback_font.hash()); - - let _ = coverage.merge(&charset); - - FallbackFont::new(fallback_font, fallback_font_key) - }) - .collect(); - - self.fallback_lists.insert(primary_font_key, FallbackList { list, coverage }); - - Ok(primary_font_key) - } - - fn full_metrics(&self, face_load_props: &FaceLoadingProperties) -> Result<FullMetrics, Error> { - let ft_face = &face_load_props.ft_face; - let size_metrics = ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?; - - let width = match ft_face.load_char('0' as usize, face_load_props.load_flags) { - Ok(_) => ft_face.glyph().metrics().horiAdvance / 64, - Err(_) => size_metrics.max_advance / 64, - } as f64; - - Ok(FullMetrics { size_metrics, cell_width: width }) - } - - fn load_ft_face(&mut self, ft_face_location: FTFaceLocation) -> Result<Rc<FTFace>, Error> { - let mut ft_face = self.library.new_face(&ft_face_location.path, ft_face_location.index)?; - if ft_face.has_color() { - unsafe { - // Select the colored bitmap size to use from the array of available sizes. - freetype_sys::FT_Select_Size(ft_face.raw_mut(), 0); - } - } - - let ft_face = Rc::new(ft_face); - self.ft_faces.insert(ft_face_location, Rc::clone(&ft_face)); - - Ok(ft_face) - } - - fn face_from_pattern( - &mut self, - pattern: &PatternRef, - font_key: FontKey, - ) -> Result<Option<FontKey>, Error> { - if let Some(ft_face_location) = pattern.ft_face_location(0) { - if self.faces.get(&font_key).is_some() { - return Ok(Some(font_key)); - } - - trace!("Got font path={:?}, index={:?}", ft_face_location.path, ft_face_location.index); - - let ft_face = match self.ft_faces.get(&ft_face_location) { - Some(ft_face) => Rc::clone(ft_face), - None => self.load_ft_face(ft_face_location)?, - }; - - let non_scalable = if pattern.scalable().next().unwrap_or(true) { - None - } else { - Some(pattern.pixelsize().next().expect("has 1+ pixelsize") as f32) - }; - - let embolden = pattern.embolden().next().unwrap_or(false); - - let matrix = pattern.get_matrix().map(|matrix| { - // Convert Fontconfig matrix to FreeType matrix. - let xx = to_fixedpoint_16_6(matrix.xx); - let xy = to_fixedpoint_16_6(matrix.xy); - let yx = to_fixedpoint_16_6(matrix.yx); - let yy = to_fixedpoint_16_6(matrix.yy); - - Matrix { xx, xy, yx, yy } - }); - - let pixelsize_fixup_factor = pattern.pixelsizefixupfactor().next(); - - let face = FaceLoadingProperties { - load_flags: Self::ft_load_flags(pattern), - render_mode: Self::ft_render_mode(pattern), - lcd_filter: Self::ft_lcd_filter(pattern), - non_scalable, - colored: ft_face.has_color(), - embolden, - matrix, - pixelsize_fixup_factor, - ft_face, - }; - - debug!("Loaded Face {:?}", face); - - self.faces.insert(font_key, face); - - Ok(Some(font_key)) - } else { - Ok(None) - } - } - - fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> Result<FontKey, Error> { - if let Some(face) = self.faces.get(&glyph_key.font_key) { - let index = face.ft_face.get_char_index(glyph_key.c as usize); - - if index != 0 { - return Ok(glyph_key.font_key); - } - } - - Ok(self.load_face_with_glyph(glyph_key).unwrap_or(glyph_key.font_key)) - } - - fn load_face_with_glyph(&mut self, glyph: GlyphKey) -> Result<FontKey, Error> { - let fallback_list = self.fallback_lists.get(&glyph.font_key).unwrap(); - - // Check whether glyph is presented in any fallback font. - if !fallback_list.coverage.has_char(glyph.c) { - return Ok(glyph.font_key); - } - - for fallback_font in &fallback_list.list { - let font_key = fallback_font.key; - let font_pattern = &fallback_font.pattern; - match self.faces.get(&font_key) { - Some(face) => { - let index = face.ft_face.get_char_index(glyph.c as usize); - - // We found something in a current face, so let's use it. - if index != 0 { - return Ok(font_key); - } - }, - None => { - if font_pattern.get_charset().map(|cs| cs.has_char(glyph.c)) != Some(true) { - continue; - } - - let pattern = font_pattern.clone(); - let key = self.face_from_pattern(&pattern, font_key)?.unwrap(); - - return Ok(key); - }, - } - } - - // You can hit this return, if you're failing to get charset from a pattern. - Ok(glyph.font_key) - } - - fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> { - // Render a normal character if it's not a cursor. - let font_key = self.face_for_glyph(glyph_key)?; - let face = &self.faces[&font_key]; - let index = face.ft_face.get_char_index(glyph_key.c as usize); - let pixelsize = face - .non_scalable - .unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); - - if !face.colored { - face.ft_face.set_char_size(to_freetype_26_6(pixelsize), 0, 0, 0)?; - } - - unsafe { - let ft_lib = self.library.raw(); - freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter); - } - - face.ft_face.load_glyph(index as u32, face.load_flags)?; - - let glyph = face.ft_face.glyph(); - - // Generate synthetic bold. - if face.embolden { - unsafe { - freetype_sys::FT_GlyphSlot_Embolden(glyph.raw() - as *const freetype_sys::FT_GlyphSlotRec - as *mut freetype_sys::FT_GlyphSlotRec); - } - } - - // Transform glyphs with the matrix from Fontconfig. Primarily used to generate italics. - if let Some(matrix) = face.matrix.as_ref() { - let glyph = face.ft_face.raw().glyph; - - unsafe { - // Check that the glyph is a vectorial outline, not a bitmap. - if (*glyph).format == freetype_sys::FT_GLYPH_FORMAT_OUTLINE { - let outline = &(*glyph).outline; - - freetype_sys::FT_Outline_Transform(outline, matrix); - } - } - } - - glyph.render_glyph(face.render_mode)?; - - let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?; - - let rasterized_glyph = RasterizedGlyph { - c: glyph_key.c, - top: glyph.bitmap_top(), - left: glyph.bitmap_left(), - width: pixel_width, - height: pixel_height, - buf, - }; - - if face.colored { - let fixup_factor = if let Some(pixelsize_fixup_factor) = face.pixelsize_fixup_factor { - pixelsize_fixup_factor - } else { - // Fallback if user has bitmap scaling disabled. - let metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?; - f64::from(pixelsize) / f64::from(metrics.y_ppem) - }; - Ok(downsample_bitmap(rasterized_glyph, fixup_factor)) - } else { - Ok(rasterized_glyph) - } - } - - fn ft_load_flags(pattern: &PatternRef) -> LoadFlag { - let antialias = pattern.antialias().next().unwrap_or(true); - let autohint = pattern.autohint().next().unwrap_or(false); - let hinting = pattern.hinting().next().unwrap_or(true); - let rgba = pattern.rgba().next().unwrap_or(fc::Rgba::Unknown); - let embedded_bitmaps = pattern.embeddedbitmap().next().unwrap_or(true); - let scalable = pattern.scalable().next().unwrap_or(true); - let color = pattern.color().next().unwrap_or(false); - - // Disable hinting if so was requested. - let hintstyle = if hinting { - pattern.hintstyle().next().unwrap_or(fc::HintStyle::Full) - } else { - fc::HintStyle::None - }; - - let mut flags = match (antialias, hintstyle, rgba) { - (false, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::MONOCHROME, - (false, ..) => LoadFlag::TARGET_MONO | LoadFlag::MONOCHROME, - (true, fc::HintStyle::None, _) => LoadFlag::NO_HINTING, - // `hintslight` does *not* use LCD hinting even when a subpixel mode - // is selected. - // - // According to the FreeType docs, - // - // > You can use a hinting algorithm that doesn't correspond to the - // > same rendering mode. As an example, it is possible to use the - // > ‘light’ hinting algorithm and have the results rendered in - // > horizontal LCD pixel mode. - // - // In practice, this means we can have `FT_LOAD_TARGET_LIGHT` with - // subpixel render modes like `FT_RENDER_MODE_LCD`. Libraries like - // cairo take the same approach and consider `hintslight` to always - // prefer `FT_LOAD_TARGET_LIGHT`. - (true, fc::HintStyle::Slight, _) => LoadFlag::TARGET_LIGHT, - (true, fc::HintStyle::Medium, _) => LoadFlag::TARGET_NORMAL, - // If LCD hinting is to be used, must select hintmedium or hintfull, - // have AA enabled, and select a subpixel mode. - (true, fc::HintStyle::Full, fc::Rgba::Rgb) - | (true, fc::HintStyle::Full, fc::Rgba::Bgr) => LoadFlag::TARGET_LCD, - (true, fc::HintStyle::Full, fc::Rgba::Vrgb) - | (true, fc::HintStyle::Full, fc::Rgba::Vbgr) => LoadFlag::TARGET_LCD_V, - // For non-rgba modes with Full hinting, just use the default hinting algorithm. - (true, fc::HintStyle::Full, fc::Rgba::Unknown) - | (true, fc::HintStyle::Full, fc::Rgba::None) => LoadFlag::TARGET_NORMAL, - }; - - // Non scalable fonts only have bitmaps, so disabling them entirely is likely not a - // desirable thing. Colored fonts aren't scalable, but also only have bitmaps. - if !embedded_bitmaps && scalable && !color { - flags |= LoadFlag::NO_BITMAP; - } - - // Use color for colored fonts. - if color { - flags |= LoadFlag::COLOR; - } - - // Force autohint if it was requested. - if autohint { - flags |= LoadFlag::FORCE_AUTOHINT; - } - - flags - } - - fn ft_render_mode(pat: &PatternRef) -> freetype::RenderMode { - let antialias = pat.antialias().next().unwrap_or(true); - let rgba = pat.rgba().next().unwrap_or(fc::Rgba::Unknown); - - match (antialias, rgba) { - (false, _) => freetype::RenderMode::Mono, - (_, fc::Rgba::Rgb) | (_, fc::Rgba::Bgr) => freetype::RenderMode::Lcd, - (_, fc::Rgba::Vrgb) | (_, fc::Rgba::Vbgr) => freetype::RenderMode::LcdV, - (true, _) => freetype::RenderMode::Normal, - } - } - - fn ft_lcd_filter(pat: &PatternRef) -> c_uint { - match pat.lcdfilter().next().unwrap_or(fc::LcdFilter::Default) { - fc::LcdFilter::None => freetype::ffi::FT_LCD_FILTER_NONE, - fc::LcdFilter::Default => freetype::ffi::FT_LCD_FILTER_DEFAULT, - fc::LcdFilter::Light => freetype::ffi::FT_LCD_FILTER_LIGHT, - fc::LcdFilter::Legacy => freetype::ffi::FT_LCD_FILTER_LEGACY, - } - } - - /// Given a FreeType `Bitmap`, returns packed buffer with 1 byte per LCD channel. - /// - /// The i32 value in the return type is the number of pixels per row. - fn normalize_buffer( - bitmap: &freetype::bitmap::Bitmap, - ) -> freetype::FtResult<(i32, i32, BitmapBuffer)> { - use freetype::bitmap::PixelMode; - - let buf = bitmap.buffer(); - let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize); - let pitch = bitmap.pitch().abs() as usize; - match bitmap.pixel_mode()? { - PixelMode::Lcd => { - for i in 0..bitmap.rows() { - let start = (i as usize) * pitch; - let stop = start + bitmap.width() as usize; - packed.extend_from_slice(&buf[start..stop]); - } - Ok((bitmap.rows(), bitmap.width() / 3, BitmapBuffer::RGB(packed))) - }, - PixelMode::LcdV => { - for i in 0..bitmap.rows() / 3 { - for j in 0..bitmap.width() { - for k in 0..3 { - let offset = ((i as usize) * 3 + k) * pitch + (j as usize); - packed.push(buf[offset]); - } - } - } - Ok((bitmap.rows() / 3, bitmap.width(), BitmapBuffer::RGB(packed))) - }, - // Mono data is stored in a packed format using 1 bit per pixel. - PixelMode::Mono => { - fn unpack_byte(res: &mut Vec<u8>, byte: u8, mut count: u8) { - // Mono stores MSBit at top of byte - let mut bit = 7; - while count != 0 { - let value = ((byte >> bit) & 1) * 255; - // Push value 3x since result buffer should be 1 byte - // per channel. - res.push(value); - res.push(value); - res.push(value); - count -= 1; - bit -= 1; - } - }; - - for i in 0..(bitmap.rows() as usize) { - let mut columns = bitmap.width(); - let mut byte = 0; - let offset = i * bitmap.pitch().abs() as usize; - while columns != 0 { - let bits = min(8, columns); - unpack_byte(&mut packed, buf[offset + byte], bits as u8); - - columns -= bits; - byte += 1; - } - } - Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGB(packed))) - }, - // Gray data is stored as a value between 0 and 255 using 1 byte per pixel. - PixelMode::Gray => { - for i in 0..bitmap.rows() { - let start = (i as usize) * pitch; - let stop = start + bitmap.width() as usize; - for byte in &buf[start..stop] { - packed.push(*byte); - packed.push(*byte); - packed.push(*byte); - } - } - Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGB(packed))) - }, - PixelMode::Bgra => { - let buf_size = (bitmap.rows() * bitmap.width() * 4) as usize; - let mut i = 0; - while i < buf_size { - packed.push(buf[i + 2]); - packed.push(buf[i + 1]); - packed.push(buf[i]); - packed.push(buf[i + 3]); - i += 4; - } - Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGBA(packed))) - }, - mode => panic!("unhandled pixel mode: {:?}", mode), - } - } -} - -/// Downscale a bitmap by a fixed factor. -/// -/// This will take the `bitmap_glyph` as input and return the glyph's content downscaled by -/// `fixup_factor`. -fn downsample_bitmap(mut bitmap_glyph: RasterizedGlyph, fixup_factor: f64) -> RasterizedGlyph { - // Only scale colored buffers which are bigger than required. - let bitmap_buffer = match (&bitmap_glyph.buf, fixup_factor.partial_cmp(&1.0)) { - (BitmapBuffer::RGBA(buffer), Some(Ordering::Less)) => buffer, - _ => return bitmap_glyph, - }; - - let bitmap_width = bitmap_glyph.width as usize; - let bitmap_height = bitmap_glyph.height as usize; - - let target_width = (bitmap_width as f64 * fixup_factor) as usize; - let target_height = (bitmap_height as f64 * fixup_factor) as usize; - - // Number of pixels in the input buffer, per pixel in the output buffer. - let downsampling_step = 1.0 / fixup_factor; - - let mut downsampled_buffer = Vec::<u8>::with_capacity(target_width * target_height * 4); - - for line_index in 0..target_height { - // Get the first and last line which will be consolidated in the current output pixel. - let line_index = line_index as f64; - let source_line_start = (line_index * downsampling_step).round() as usize; - let source_line_end = ((line_index + 1.) * downsampling_step).round() as usize; - - for column_index in 0..target_width { - // Get the first and last column which will be consolidated in the current output - // pixel. - let column_index = column_index as f64; - let source_column_start = (column_index * downsampling_step).round() as usize; - let source_column_end = ((column_index + 1.) * downsampling_step).round() as usize; - - let (mut r, mut g, mut b, mut a) = (0u32, 0u32, 0u32, 0u32); - let mut pixels_picked: u32 = 0; - - // Consolidate all pixels within the source rectangle into a single averaged pixel. - for source_line in source_line_start..source_line_end { - let source_pixel_index = source_line * bitmap_width; - - for source_column in source_column_start..source_column_end { - let offset = (source_pixel_index + source_column) * 4; - r += u32::from(bitmap_buffer[offset]); - g += u32::from(bitmap_buffer[offset + 1]); - b += u32::from(bitmap_buffer[offset + 2]); - a += u32::from(bitmap_buffer[offset + 3]); - pixels_picked += 1; - } - } - - // Add a single pixel to the output buffer for the downscaled source rectangle. - downsampled_buffer.push((r / pixels_picked) as u8); - downsampled_buffer.push((g / pixels_picked) as u8); - downsampled_buffer.push((b / pixels_picked) as u8); - downsampled_buffer.push((a / pixels_picked) as u8); - } - } - - bitmap_glyph.buf = BitmapBuffer::RGBA(downsampled_buffer); - - // Downscale the metrics. - bitmap_glyph.top = (f64::from(bitmap_glyph.top) * fixup_factor) as i32; - bitmap_glyph.left = (f64::from(bitmap_glyph.left) * fixup_factor) as i32; - bitmap_glyph.width = target_width as i32; - bitmap_glyph.height = target_height as i32; - - bitmap_glyph -} - -/// Errors occurring when using the freetype rasterizer. -#[derive(Debug)] -pub enum Error { - /// Error occurred within the FreeType library. - FreeType(freetype::Error), - - /// Couldn't find font matching description. - MissingFont(FontDesc), - - /// Tried to get size metrics from a Face that didn't have a size. - MissingSizeMetrics, - - /// Requested an operation with a FontKey that isn't known to the rasterizer. - FontNotLoaded, -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::FreeType(err) => err.source(), - _ => None, - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Error::FreeType(err) => err.fmt(f), - Error::MissingFont(err) => write!(f, "Unable to find the font {}", err), - Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"), - Error::MissingSizeMetrics => { - f.write_str("Tried to get size metrics from a face without a size") - }, - } - } -} - -impl From<freetype::Error> for Error { - fn from(val: freetype::Error) -> Error { - Error::FreeType(val) - } -} - -unsafe impl Send for FreeTypeRasterizer {} |