diff --git a/font-renderer/src/core_graphics.rs b/font-renderer/src/core_graphics.rs index 8d1416a3..bc87556e 100644 --- a/font-renderer/src/core_graphics.rs +++ b/font-renderer/src/core_graphics.rs @@ -24,7 +24,7 @@ use pathfinder_path_utils::PathCommand; use std::collections::BTreeMap; use std::collections::btree_map::Entry; use std::sync::Arc; -use {FontInstance, FontKey, GlyphDimensions, GlyphKey}; +use {FontInstance, FontKey, GlyphDimensions, GlyphImage, GlyphKey}; const CG_ZERO_RECT: CGRect = CGRect { origin: CG_ZERO_POINT, @@ -269,8 +269,3 @@ impl FontInstance { Ok(core_text::font::new_from_CGFont(core_graphics_font, self.size.to_f64_px())) } } - -pub struct GlyphImage { - pub dimensions: GlyphDimensions, - pub pixels: Vec, -} diff --git a/font-renderer/src/freetype/fixed.rs b/font-renderer/src/freetype/fixed.rs new file mode 100644 index 00000000..d9cd2b0b --- /dev/null +++ b/font-renderer/src/freetype/fixed.rs @@ -0,0 +1,51 @@ +// pathfinder/font-renderer/src/freetype/fixed.rs +// +// Copyright © 2017 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Utilities for FreeType 26.6 fixed-point numbers. + +use app_units::Au; +use freetype_sys::FT_F26Dot6; + +pub trait FromFtF26Dot6 { + fn from_ft_f26dot6(value: FT_F26Dot6) -> Self; +} + +impl FromFtF26Dot6 for f32 { + fn from_ft_f26dot6(value: FT_F26Dot6) -> f32 { + (value as f32) / 64.0 + } +} + +pub trait ToFtF26Dot6 { + fn to_ft_f26dot6(self) -> FT_F26Dot6; +} + +impl ToFtF26Dot6 for f32 { + fn to_ft_f26dot6(self) -> FT_F26Dot6 { + (self * 64.0 + 0.5) as FT_F26Dot6 + } +} + +impl ToFtF26Dot6 for f64 { + fn to_ft_f26dot6(self) -> FT_F26Dot6 { + (self * 64.0 + 0.5) as FT_F26Dot6 + } +} + +impl ToFtF26Dot6 for Au { + fn to_ft_f26dot6(self) -> FT_F26Dot6 { + self.to_f64_px().to_ft_f26dot6() + } +} + +#[inline] +pub fn floor(n: FT_F26Dot6) -> FT_F26Dot6 { + n & !0x3f +} diff --git a/font-renderer/src/freetype/mod.rs b/font-renderer/src/freetype/mod.rs index a8921c2f..27b19009 100644 --- a/font-renderer/src/freetype/mod.rs +++ b/font-renderer/src/freetype/mod.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use {FontKey, FontInstance, GlyphDimensions, GlyphKey}; -use app_units::Au; -use euclid::{Point2D, Size2D}; -use freetype_sys::{FT_BBox, FT_Done_Face, FT_F26Dot6, FT_Face, FT_GLYPH_FORMAT_OUTLINE}; -use freetype_sys::{FT_GlyphSlot, FT_Init_FreeType, FT_Int32, FT_LOAD_NO_HINTING, FT_Library}; +use euclid::{Point2D, Size2D, Vector2D}; +use freetype_sys::{FT_BBox, FT_Bitmap, FT_Done_Face, FT_F26Dot6, FT_Face, FT_GLYPH_FORMAT_OUTLINE}; +use freetype_sys::{FT_GlyphSlot, FT_Init_FreeType, FT_Int32, FT_LCD_FILTER_DEFAULT}; +use freetype_sys::{FT_LOAD_NO_HINTING, FT_Library, FT_Library_SetLcdFilter}; use freetype_sys::{FT_Load_Glyph, FT_Long, FT_New_Memory_Face, FT_Outline_Get_CBox}; +use freetype_sys::{FT_Outline_Translate, FT_PIXEL_MODE_LCD, FT_RENDER_MODE_LCD, FT_Render_Glyph}; use freetype_sys::{FT_Set_Char_Size, FT_UInt}; use pathfinder_path_utils::PathCommand; use std::collections::BTreeMap; @@ -21,10 +21,14 @@ use std::collections::btree_map::Entry; use std::marker::PhantomData; use std::mem; use std::ptr; +use std::slice; use std::sync::Arc; +use self::fixed::{FromFtF26Dot6, ToFtF26Dot6}; use self::outline::OutlineStream; +use {FontKey, FontInstance, GlyphDimensions, GlyphImage, GlyphKey}; +mod fixed; mod outline; // Default to no hinting. @@ -106,6 +110,90 @@ impl FontContext { }) } + /// Uses the FreeType library to rasterize a glyph on CPU. + pub fn rasterize_glyph_with_native_rasterizer(&self, + font_instance: &FontInstance, + glyph_key: &GlyphKey) + -> Result { + // Load the glyph. + let slot = match self.load_glyph(font_instance, glyph_key) { + None => return Err(()), + Some(slot) => slot, + }; + + // Get the subpixel offset. + let subpixel_offset: Vector2D = + Vector2D::new(f32::to_ft_f26dot6(glyph_key.subpixel_offset.into()), 0); + + // Move the outline curves to be at the origin, taking the subpixel positioning into + // account. + unsafe { + let outline = &(*slot).outline; + let mut control_box: FT_BBox = mem::uninitialized(); + FT_Outline_Get_CBox(outline, &mut control_box); + FT_Outline_Translate( + outline, + subpixel_offset.x - fixed::floor(control_box.xMin + subpixel_offset.x), + subpixel_offset.y - fixed::floor(control_box.yMin + subpixel_offset.y)); + } + + // Set the LCD filter. + // + // TODO(pcwalton): Non-subpixel AA. + unsafe { + FT_Library_SetLcdFilter(self.library, FT_LCD_FILTER_DEFAULT); + } + + // Render the glyph. + // + // TODO(pcwalton): Non-subpixel AA. + unsafe { + FT_Render_Glyph(slot, FT_RENDER_MODE_LCD); + } + + unsafe { + // Make sure that the pixel mode is LCD. + // + // TODO(pcwalton): Non-subpixel AA. + let bitmap: *const FT_Bitmap = &(*slot).bitmap; + if (*bitmap).pixel_mode as u32 != FT_PIXEL_MODE_LCD { + return Err(()) + } + + debug_assert_eq!((*bitmap).width % 3, 0); + let pixel_size = Size2D::new((*bitmap).width as u32 / 3, (*bitmap).rows as u32); + let pixel_origin = Point2D::new((*slot).bitmap_left, (*slot).bitmap_top); + + // Allocate the RGBA8 buffer. + let area = pixel_size.area() as usize; + let mut dest_pixels: Vec = vec![0; area]; + let src_pixels = slice::from_raw_parts((*bitmap).buffer, area * 3); + + // Convert to RGBA8. + let pixel_width = pixel_size.width as usize; + for y in 0..(pixel_size.height as usize) { + let dest_row = &mut dest_pixels[(y * pixel_width)..((y + 1) * pixel_width)]; + let src_row = &src_pixels[(y * pixel_width * 3)..((y + 1) * pixel_width * 3)]; + for (x, dest) in dest_row.iter_mut().enumerate() { + *dest = (src_row[x * 3 + 2] as u32) | + ((src_row[x * 3 + 1] as u32) << 8) | + ((src_row[x * 3 + 0] as u32) << 16) | + (0xff << 24) + } + } + + // Return the result. + Ok(GlyphImage { + dimensions: GlyphDimensions { + origin: pixel_origin, + size: pixel_size, + advance: f32::from_ft_f26dot6((*slot).metrics.horiAdvance), + }, + pixels: convert_vec_u32_to_vec_u8(dest_pixels), + }) + } + } + fn load_glyph(&self, font_instance: &FontInstance, glyph_key: &GlyphKey) -> Option { let face = match self.faces.get(&font_instance.font_key) { @@ -149,7 +237,7 @@ impl FontContext { (bounding_box.yMax >> 6) as i32), size: Size2D::new(((bounding_box.xMax - bounding_box.xMin) >> 6) as u32, ((bounding_box.yMax - bounding_box.yMin) >> 6) as u32), - advance: metrics.horiAdvance as f32 / 64.0, + advance: f32::from_ft_f26dot6(metrics.horiAdvance), }) } } @@ -166,10 +254,10 @@ impl FontContext { }; // Outset the box to device pixel boundaries. This matches what WebRender does. - bounding_box.xMin &= !0x3f; - bounding_box.yMin &= !0x3f; - bounding_box.xMax = (bounding_box.xMax + 0x3f) & !0x3f; - bounding_box.yMax = (bounding_box.yMax + 0x3f) & !0x3f; + bounding_box.xMin = fixed::floor(bounding_box.xMin); + bounding_box.yMin = fixed::floor(bounding_box.yMin); + bounding_box.xMax = fixed::floor(bounding_box.xMax + 0x3f); + bounding_box.yMax = fixed::floor(bounding_box.yMax + 0x3f); bounding_box } @@ -200,28 +288,8 @@ impl Drop for Face { } } -trait FromFtF26Dot6 { - fn from_ft_f26dot6(value: FT_F26Dot6) -> Self; +unsafe fn convert_vec_u32_to_vec_u8(mut input: Vec) -> Vec { + let (ptr, len, cap) = (input.as_mut_ptr(), input.len(), input.capacity()); + mem::forget(input); + Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) } - -impl FromFtF26Dot6 for f32 { - fn from_ft_f26dot6(value: FT_F26Dot6) -> f32 { - (value as f32) / 64.0 - } -} - -trait ToFtF26Dot6 { - fn to_ft_f26dot6(&self) -> FT_F26Dot6; -} - -impl ToFtF26Dot6 for f64 { - fn to_ft_f26dot6(&self) -> FT_F26Dot6 { - (*self * 64.0 + 0.5) as FT_F26Dot6 - } -} - -impl ToFtF26Dot6 for Au { - fn to_ft_f26dot6(&self) -> FT_F26Dot6 { - self.to_f64_px().to_ft_f26dot6() - } -} \ No newline at end of file diff --git a/font-renderer/src/lib.rs b/font-renderer/src/lib.rs index d746cf86..6c5fb3e1 100644 --- a/font-renderer/src/lib.rs +++ b/font-renderer/src/lib.rs @@ -157,3 +157,8 @@ pub struct GlyphDimensions { pub size: Size2D, pub advance: f32, } + +pub struct GlyphImage { + pub dimensions: GlyphDimensions, + pub pixels: Vec, +}