Implement basic native FreeType glyph rendering, untested as of yet
This commit is contained in:
parent
ce91d0490a
commit
dc6bdda7ff
|
@ -24,7 +24,7 @@ use pathfinder_path_utils::PathCommand;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::btree_map::Entry;
|
use std::collections::btree_map::Entry;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use {FontInstance, FontKey, GlyphDimensions, GlyphKey};
|
use {FontInstance, FontKey, GlyphDimensions, GlyphImage, GlyphKey};
|
||||||
|
|
||||||
const CG_ZERO_RECT: CGRect = CGRect {
|
const CG_ZERO_RECT: CGRect = CGRect {
|
||||||
origin: CG_ZERO_POINT,
|
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()))
|
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>,
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -8,12 +8,12 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use {FontKey, FontInstance, GlyphDimensions, GlyphKey};
|
use euclid::{Point2D, Size2D, Vector2D};
|
||||||
use app_units::Au;
|
use freetype_sys::{FT_BBox, FT_Bitmap, FT_Done_Face, FT_F26Dot6, FT_Face, FT_GLYPH_FORMAT_OUTLINE};
|
||||||
use euclid::{Point2D, Size2D};
|
use freetype_sys::{FT_GlyphSlot, FT_Init_FreeType, FT_Int32, FT_LCD_FILTER_DEFAULT};
|
||||||
use freetype_sys::{FT_BBox, FT_Done_Face, FT_F26Dot6, FT_Face, FT_GLYPH_FORMAT_OUTLINE};
|
use freetype_sys::{FT_LOAD_NO_HINTING, FT_Library, FT_Library_SetLcdFilter};
|
||||||
use freetype_sys::{FT_GlyphSlot, FT_Init_FreeType, FT_Int32, FT_LOAD_NO_HINTING, FT_Library};
|
|
||||||
use freetype_sys::{FT_Load_Glyph, FT_Long, FT_New_Memory_Face, FT_Outline_Get_CBox};
|
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 freetype_sys::{FT_Set_Char_Size, FT_UInt};
|
||||||
use pathfinder_path_utils::PathCommand;
|
use pathfinder_path_utils::PathCommand;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -21,10 +21,14 @@ use std::collections::btree_map::Entry;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::slice;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use self::fixed::{FromFtF26Dot6, ToFtF26Dot6};
|
||||||
use self::outline::OutlineStream;
|
use self::outline::OutlineStream;
|
||||||
|
use {FontKey, FontInstance, GlyphDimensions, GlyphImage, GlyphKey};
|
||||||
|
|
||||||
|
mod fixed;
|
||||||
mod outline;
|
mod outline;
|
||||||
|
|
||||||
// Default to no hinting.
|
// 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)
|
fn load_glyph(&self, font_instance: &FontInstance, glyph_key: &GlyphKey)
|
||||||
-> Option<FT_GlyphSlot> {
|
-> Option<FT_GlyphSlot> {
|
||||||
let face = match self.faces.get(&font_instance.font_key) {
|
let face = match self.faces.get(&font_instance.font_key) {
|
||||||
|
@ -149,7 +237,7 @@ impl FontContext {
|
||||||
(bounding_box.yMax >> 6) as i32),
|
(bounding_box.yMax >> 6) as i32),
|
||||||
size: Size2D::new(((bounding_box.xMax - bounding_box.xMin) >> 6) as u32,
|
size: Size2D::new(((bounding_box.xMax - bounding_box.xMin) >> 6) as u32,
|
||||||
((bounding_box.yMax - bounding_box.yMin) >> 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.
|
// Outset the box to device pixel boundaries. This matches what WebRender does.
|
||||||
bounding_box.xMin &= !0x3f;
|
bounding_box.xMin = fixed::floor(bounding_box.xMin);
|
||||||
bounding_box.yMin &= !0x3f;
|
bounding_box.yMin = fixed::floor(bounding_box.yMin);
|
||||||
bounding_box.xMax = (bounding_box.xMax + 0x3f) & !0x3f;
|
bounding_box.xMax = fixed::floor(bounding_box.xMax + 0x3f);
|
||||||
bounding_box.yMax = (bounding_box.yMax + 0x3f) & !0x3f;
|
bounding_box.yMax = fixed::floor(bounding_box.yMax + 0x3f);
|
||||||
|
|
||||||
bounding_box
|
bounding_box
|
||||||
}
|
}
|
||||||
|
@ -200,28 +288,8 @@ impl Drop for Face {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait FromFtF26Dot6 {
|
unsafe fn convert_vec_u32_to_vec_u8(mut input: Vec<u32>) -> Vec<u8> {
|
||||||
fn from_ft_f26dot6(value: FT_F26Dot6) -> Self;
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -157,3 +157,8 @@ pub struct GlyphDimensions {
|
||||||
pub size: Size2D<u32>,
|
pub size: Size2D<u32>,
|
||||||
pub advance: f32,
|
pub advance: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GlyphImage {
|
||||||
|
pub dimensions: GlyphDimensions,
|
||||||
|
pub pixels: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue