Implement basic native FreeType glyph rendering, untested as of yet

This commit is contained in:
Patrick Walton 2017-10-31 11:25:38 -07:00
parent ce91d0490a
commit dc6bdda7ff
4 changed files with 159 additions and 40 deletions

View File

@ -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<u8>,
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
}

View File

@ -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<GlyphImage, ()> {
// 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<FT_F26Dot6> =
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<u32> = 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<FT_GlyphSlot> {
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<u32>) -> Vec<u8> {
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()
}
}

View File

@ -157,3 +157,8 @@ pub struct GlyphDimensions {
pub size: Size2D<u32>,
pub advance: f32,
}
pub struct GlyphImage {
pub dimensions: GlyphDimensions,
pub pixels: Vec<u8>,
}