Add preliminary DirectWrite support on Windows.
This will eventually need to call into GDI to do hinting, but it's good enough for now.
This commit is contained in:
parent
1c693d5029
commit
0b179f3a40
|
@ -524,7 +524,7 @@ class ThreeDRenderer extends Renderer {
|
|||
}
|
||||
|
||||
protected newTimingsReceived(): void {
|
||||
const newTimings: Timings = _.pick(this.lastTimings, ['rendering']);
|
||||
const newTimings: Partial<Timings> = _.pick(this.lastTimings, ['rendering']);
|
||||
this.renderContext.appController.newTimingsReceived(newTimings);
|
||||
}
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ export abstract class DemoAppController<View extends DemoView> extends AppContro
|
|||
this.updateAALevel();
|
||||
}
|
||||
|
||||
newTimingsReceived(timings: Timings) {
|
||||
newTimingsReceived(timings: Partial<Timings>) {
|
||||
if (this.fpsLabel == null)
|
||||
return;
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"lockfileVersion": 1
|
||||
}
|
|
@ -274,8 +274,15 @@ fn partition_font(request: Json<PartitionFontRequest>)
|
|||
font_key: font_key,
|
||||
size: Au::from_f64_px(request.point_size),
|
||||
};
|
||||
let mut font_context = FontContext::new();
|
||||
let mut font_context = match FontContext::new() {
|
||||
Ok(font_context) => font_context,
|
||||
Err(_) => {
|
||||
println!("Failed to create a font context!");
|
||||
return Err(PartitionFontError::FontLoadingFailed)
|
||||
}
|
||||
};
|
||||
if font_context.add_font_from_memory(&font_key, otf_data, request.font_index).is_err() {
|
||||
println!("Failed to add font from memory!");
|
||||
return Err(PartitionFontError::FontLoadingFailed)
|
||||
}
|
||||
|
||||
|
@ -288,7 +295,7 @@ fn partition_font(request: Json<PartitionFontRequest>)
|
|||
|
||||
// This might fail; if so, just leave it blank.
|
||||
if let Ok(glyph_outline) = font_context.glyph_outline(&font_instance_key, &glyph_key) {
|
||||
let stream = Transform2DPathStream::new(glyph_outline, &glyph.transform);
|
||||
let stream = Transform2DPathStream::new(glyph_outline.into_iter(), &glyph.transform);
|
||||
let stream = MonotonicPathCommandStream::new(stream);
|
||||
path_buffer.add_stream(stream)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ freetype = ["freetype-sys"]
|
|||
[dependencies]
|
||||
app_units = "0.5"
|
||||
euclid = "0.15"
|
||||
libc = "0.2"
|
||||
log = "0.3"
|
||||
|
||||
[dependencies.freetype-sys]
|
||||
|
@ -28,5 +29,11 @@ core-graphics = "0.11"
|
|||
[target.'cfg(target_os = "macos")'.dependencies.core-text]
|
||||
git = "https://github.com/servo/core-text-rs.git"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
dwrite-sys = "0.2"
|
||||
kernel32-sys = "0.2"
|
||||
uuid-sys = "0.1"
|
||||
winapi = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.4"
|
||||
|
|
|
@ -25,17 +25,19 @@ use {FontInstanceKey, FontKey, GlyphDimensions, GlyphKey};
|
|||
|
||||
const CURVE_APPROX_ERROR_BOUND: f32 = 0.1;
|
||||
|
||||
pub type GlyphOutline = Vec<PathCommand>;
|
||||
|
||||
pub struct FontContext {
|
||||
core_graphics_fonts: BTreeMap<FontKey, CGFont>,
|
||||
core_text_fonts: BTreeMap<FontInstanceKey, CTFont>,
|
||||
}
|
||||
|
||||
impl FontContext {
|
||||
pub fn new() -> FontContext {
|
||||
FontContext {
|
||||
pub fn new() -> Result<FontContext, ()> {
|
||||
Ok(FontContext {
|
||||
core_graphics_fonts: BTreeMap::new(),
|
||||
core_text_fonts: BTreeMap::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_font_from_memory(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, _: u32)
|
||||
|
@ -111,37 +113,7 @@ impl FontContext {
|
|||
let core_text_font = try!(self.ensure_core_text_font(font_instance));
|
||||
let path = try!(core_text_font.create_path_for_glyph(glyph_key.glyph_index as CGGlyph,
|
||||
&CG_AFFINE_TRANSFORM_IDENTITY));
|
||||
Ok(GlyphOutline::new(&path))
|
||||
}
|
||||
}
|
||||
|
||||
impl FontInstanceKey {
|
||||
fn instantiate(&self, core_graphics_font: &CGFont) -> Result<CTFont, ()> {
|
||||
Ok(core_text::font::new_from_CGFont(core_graphics_font, self.size.to_f64_px()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlyphOutline {
|
||||
commands: Vec<PathCommand>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl Iterator for GlyphOutline {
|
||||
type Item = PathCommand;
|
||||
|
||||
fn next(&mut self) -> Option<PathCommand> {
|
||||
match self.commands.get(self.index) {
|
||||
None => None,
|
||||
Some(command) => {
|
||||
self.index += 1;
|
||||
Some(*command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlyphOutline {
|
||||
fn new(path: &CGPath) -> GlyphOutline {
|
||||
let mut commands = vec![];
|
||||
path.apply(&|element| {
|
||||
let points = element.points();
|
||||
|
@ -170,13 +142,16 @@ impl GlyphOutline {
|
|||
|
||||
let approx_commands: Vec<_> = approx_stream.collect();
|
||||
|
||||
return GlyphOutline {
|
||||
commands: approx_commands,
|
||||
index: 0,
|
||||
};
|
||||
return Ok(approx_commands);
|
||||
|
||||
fn convert_point(core_graphics_point: &CGPoint) -> Point2D<f32> {
|
||||
Point2D::new(core_graphics_point.x as f32, core_graphics_point.y as f32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FontInstanceKey {
|
||||
fn instantiate(&self, core_graphics_font: &CGFont) -> Result<CTFont, ()> {
|
||||
Ok(core_text::font::new_from_CGFont(core_graphics_font, self.size.to_f64_px()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
// pathfinder/font-renderer/src/directwrite/com.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.
|
||||
|
||||
//! Utility types for Microsoft COM.
|
||||
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use uuid;
|
||||
use winapi::{E_NOINTERFACE, E_POINTER, GUID, HRESULT, IUnknown, REFIID, S_OK, ULONG};
|
||||
|
||||
pub struct PathfinderComPtr<T> {
|
||||
ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<T> PathfinderComPtr<T> {
|
||||
#[inline]
|
||||
pub unsafe fn new(ptr: *mut T) -> PathfinderComPtr<T> {
|
||||
PathfinderComPtr {
|
||||
ptr: ptr,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_raw(self) -> *mut T {
|
||||
let ptr = self.ptr;
|
||||
mem::forget(self);
|
||||
ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for PathfinderComPtr<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> PathfinderComPtr<T> {
|
||||
unsafe {
|
||||
(*(self.ptr as *mut IUnknown)).AddRef();
|
||||
}
|
||||
PathfinderComPtr {
|
||||
ptr: self.ptr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for PathfinderComPtr<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(*(self.ptr as *mut IUnknown)).Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for PathfinderComPtr<T> {
|
||||
type Target = *mut T;
|
||||
#[inline]
|
||||
fn deref(&self) -> &*mut T {
|
||||
&self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PathfinderCoclass {
|
||||
type InterfaceVtable: 'static;
|
||||
fn interface_guid() -> &'static GUID;
|
||||
fn vtable() -> &'static Self::InterfaceVtable;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PathfinderComObject<DerivedClass> where DerivedClass: PathfinderCoclass {
|
||||
vtable: &'static DerivedClass::InterfaceVtable,
|
||||
ref_count: AtomicUsize,
|
||||
}
|
||||
|
||||
impl<DerivedClass> PathfinderComObject<DerivedClass> where DerivedClass: PathfinderCoclass {
|
||||
#[inline]
|
||||
pub unsafe fn construct() -> PathfinderComObject<DerivedClass> {
|
||||
PathfinderComObject {
|
||||
vtable: DerivedClass::vtable(),
|
||||
ref_count: AtomicUsize::new(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn AddRef(this: *mut IUnknown) -> ULONG {
|
||||
let this = this as *mut PathfinderComObject<DerivedClass>;
|
||||
((*this).ref_count.fetch_add(1, Ordering::SeqCst) + 1) as ULONG
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn Release(this: *mut IUnknown) -> ULONG {
|
||||
let this = this as *mut PathfinderComObject<DerivedClass>;
|
||||
let new_ref_count = (*this).ref_count.fetch_sub(1, Ordering::SeqCst) - 1;
|
||||
if new_ref_count == 0 {
|
||||
drop(Box::from_raw(this))
|
||||
}
|
||||
new_ref_count as ULONG
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn QueryInterface(this: *mut IUnknown,
|
||||
riid: REFIID,
|
||||
object: *mut *mut c_void)
|
||||
-> HRESULT {
|
||||
if object.is_null() {
|
||||
return E_POINTER
|
||||
}
|
||||
if guids_are_equal(&*riid, &uuid::IID_IUnknown) ||
|
||||
guids_are_equal(&*riid, DerivedClass::interface_guid()) {
|
||||
*object = this as *mut c_void;
|
||||
return S_OK
|
||||
}
|
||||
*object = ptr::null_mut();
|
||||
E_NOINTERFACE
|
||||
}
|
||||
}
|
||||
|
||||
fn guids_are_equal(a: &GUID, b: &GUID) -> bool {
|
||||
a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 && a.Data4 == b.Data4
|
||||
}
|
|
@ -0,0 +1,619 @@
|
|||
// pathfinder/font-renderer/src/directwrite/mod.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.
|
||||
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
|
||||
use dwrite;
|
||||
use euclid::{Point2D, Size2D};
|
||||
use kernel32;
|
||||
use pathfinder_path_utils::PathCommand;
|
||||
use pathfinder_path_utils::cubic::{CubicPathCommand, CubicPathCommandApproxStream};
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::sync::Arc;
|
||||
use uuid::IID_ID2D1SimplifiedGeometrySink;
|
||||
use winapi::winerror::{self, S_OK};
|
||||
use winapi::{self, BOOL, D2D1_BEZIER_SEGMENT, D2D1_FIGURE_BEGIN, D2D1_FIGURE_END};
|
||||
use winapi::{D2D1_FIGURE_END_CLOSED, D2D1_FILL_MODE, D2D1_PATH_SEGMENT, D2D1_POINT_2F};
|
||||
use winapi::{DWRITE_FONT_METRICS, DWRITE_GLYPH_METRICS, E_BOUNDS, E_INVALIDARG, FALSE, FILETIME};
|
||||
use winapi::{FLOAT, GUID, HRESULT, ID2D1SimplifiedGeometrySinkVtbl, IDWriteFactory};
|
||||
use winapi::{IDWriteFontCollectionLoader, IDWriteFontCollectionLoaderVtbl, IDWriteFontFace};
|
||||
use winapi::{IDWriteFontFile, IDWriteFontFileEnumerator, IDWriteFontFileEnumeratorVtbl};
|
||||
use winapi::{IDWriteFontFileLoader, IDWriteFontFileLoaderVtbl, IDWriteFontFileStream};
|
||||
use winapi::{IDWriteFontFileStreamVtbl, IDWriteGdiInterop, IDWriteGeometrySink, IUnknown};
|
||||
use winapi::{IUnknownVtbl, TRUE, UINT16, UINT32, UINT64, UINT};
|
||||
|
||||
use self::com::{PathfinderCoclass, PathfinderComObject, PathfinderComPtr};
|
||||
use {FontInstanceKey, FontKey, GlyphDimensions, GlyphKey};
|
||||
|
||||
mod com;
|
||||
|
||||
DEFINE_GUID! {
|
||||
IID_IDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48
|
||||
}
|
||||
DEFINE_GUID! {
|
||||
IID_IDWriteFontCollectionLoader,
|
||||
0xcca920e4, 0x52f0, 0x492b, 0xbf, 0xa8, 0x29, 0xc7, 0x2e, 0xe0, 0xa4, 0x68
|
||||
}
|
||||
DEFINE_GUID! {
|
||||
IID_IDWriteFontFileEnumerator,
|
||||
0x72755049, 0x5ff7, 0x435d, 0x83, 0x48, 0x4b, 0xe9, 0x7c, 0xfa, 0x6c, 0x7c
|
||||
}
|
||||
DEFINE_GUID! {
|
||||
IID_IDWriteFontFileLoader,
|
||||
0x727cad4e, 0xd6af, 0x4c9e, 0x8a, 0x08, 0xd6, 0x95, 0xb1, 0x1c, 0xaa, 0x49
|
||||
}
|
||||
DEFINE_GUID! {
|
||||
IID_IDWriteFontFileStream,
|
||||
0x6d4865fe, 0x0ab8, 0x4d91, 0x8f, 0x62, 0x5d, 0xd6, 0xbe, 0x34, 0xa3, 0xe0
|
||||
}
|
||||
|
||||
pub type GlyphOutline = Vec<PathCommand>;
|
||||
|
||||
const CURVE_APPROX_ERROR_BOUND: f32 = 0.1;
|
||||
|
||||
static PATHFINDER_FONT_COLLECTION_KEY: [u8; 17] = *b"MEMORY_COLLECTION";
|
||||
static PATHFINDER_FONT_FILE_KEY: [u8; 11] = *b"MEMORY_FILE";
|
||||
|
||||
pub struct FontContext {
|
||||
dwrite_factory: PathfinderComPtr<IDWriteFactory>,
|
||||
#[allow(unused)]
|
||||
dwrite_gdi_interop: PathfinderComPtr<IDWriteGdiInterop>,
|
||||
dwrite_font_faces: BTreeMap<FontKey, PathfinderComPtr<IDWriteFontFace>>,
|
||||
}
|
||||
|
||||
impl FontContext {
|
||||
pub fn new() -> Result<FontContext, ()> {
|
||||
unsafe {
|
||||
let mut factory: *mut IDWriteFactory = ptr::null_mut();
|
||||
if !winerror::SUCCEEDED(dwrite::DWriteCreateFactory(winapi::DWRITE_FACTORY_TYPE_SHARED,
|
||||
&IID_IDWriteFactory,
|
||||
&mut factory as *mut *mut _ as
|
||||
*mut *mut IUnknown)) {
|
||||
return Err(())
|
||||
}
|
||||
let factory = PathfinderComPtr::new(factory);
|
||||
|
||||
let mut gdi_interop: *mut IDWriteGdiInterop = ptr::null_mut();
|
||||
if !winerror::SUCCEEDED((**factory).GetGdiInterop(&mut gdi_interop)) {
|
||||
return Err(())
|
||||
}
|
||||
let gdi_interop = PathfinderComPtr::new(gdi_interop);
|
||||
|
||||
Ok(FontContext {
|
||||
dwrite_factory: factory,
|
||||
dwrite_gdi_interop: gdi_interop,
|
||||
dwrite_font_faces: BTreeMap::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_font_from_memory(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, _: u32)
|
||||
-> Result<(), ()> {
|
||||
unsafe {
|
||||
let font_file_loader = PathfinderFontFileLoader::new(bytes.clone());
|
||||
|
||||
let result = (**self.dwrite_factory).RegisterFontFileLoader(
|
||||
font_file_loader.clone().into_raw() as *mut IDWriteFontFileLoader);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let mut font_file = ptr::null_mut();
|
||||
let result = (**self.dwrite_factory).CreateCustomFontFileReference(
|
||||
PATHFINDER_FONT_FILE_KEY.as_ptr() as *const c_void,
|
||||
PATHFINDER_FONT_FILE_KEY.len() as UINT,
|
||||
font_file_loader.clone().into_raw() as *mut IDWriteFontFileLoader,
|
||||
&mut font_file);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
let font_file = PathfinderComPtr::new(font_file);
|
||||
|
||||
let font_collection_loader = PathfinderFontCollectionLoader::new(font_file);
|
||||
|
||||
let result = (**self.dwrite_factory).RegisterFontCollectionLoader(
|
||||
font_collection_loader.clone().into_raw() as *mut IDWriteFontCollectionLoader);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let mut font_collection = ptr::null_mut();
|
||||
let result = (**self.dwrite_factory).CreateCustomFontCollection(
|
||||
font_collection_loader.clone().into_raw() as *mut IDWriteFontCollectionLoader,
|
||||
PATHFINDER_FONT_COLLECTION_KEY.as_ptr() as *const c_void,
|
||||
PATHFINDER_FONT_COLLECTION_KEY.len() as UINT32,
|
||||
&mut font_collection);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
let font_collection = PathfinderComPtr::new(font_collection);
|
||||
|
||||
let mut font_family = ptr::null_mut();
|
||||
let result = (**font_collection).GetFontFamily(0, &mut font_family);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
let font_family = PathfinderComPtr::new(font_family);
|
||||
|
||||
let mut font = ptr::null_mut();
|
||||
let result = (**font_family).GetFont(0, &mut font);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
let font = PathfinderComPtr::new(font);
|
||||
|
||||
let mut font_face = ptr::null_mut();
|
||||
let result = (**font).CreateFontFace(&mut font_face);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
let font_face = PathfinderComPtr::new(font_face);
|
||||
|
||||
let result = (**self.dwrite_factory).UnregisterFontCollectionLoader(
|
||||
font_collection_loader.into_raw() as *mut IDWriteFontCollectionLoader);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let result = (**self.dwrite_factory).UnregisterFontFileLoader(
|
||||
font_file_loader.into_raw() as *mut IDWriteFontFileLoader);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
self.dwrite_font_faces.insert(*font_key, font_face);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn delete_font(&mut self, font_key: &FontKey) {
|
||||
self.dwrite_font_faces.remove(font_key);
|
||||
}
|
||||
|
||||
pub fn glyph_dimensions(&self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey)
|
||||
-> Option<GlyphDimensions> {
|
||||
unsafe {
|
||||
let font_face = match self.dwrite_font_faces.get(&font_instance.font_key) {
|
||||
None => return None,
|
||||
Some(font_face) => (*font_face).clone(),
|
||||
};
|
||||
|
||||
let glyph_index = glyph_key.glyph_index as UINT16;
|
||||
let mut metrics: DWRITE_GLYPH_METRICS = mem::zeroed();
|
||||
|
||||
let result = (**font_face).GetDesignGlyphMetrics(&glyph_index, 1, &mut metrics, FALSE);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return None
|
||||
}
|
||||
|
||||
Some(GlyphDimensions {
|
||||
advance: metrics.advanceWidth as f32,
|
||||
origin: Point2D::new(metrics.leftSideBearing, metrics.bottomSideBearing),
|
||||
size: Size2D::new((metrics.rightSideBearing - metrics.leftSideBearing) as u32,
|
||||
(metrics.topSideBearing - metrics.bottomSideBearing) as u32),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glyph_outline(&mut self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey)
|
||||
-> Result<GlyphOutline, ()> {
|
||||
unsafe {
|
||||
let font_face = match self.dwrite_font_faces.get(&font_instance.font_key) {
|
||||
None => return Err(()),
|
||||
Some(font_face) => (*font_face).clone(),
|
||||
};
|
||||
|
||||
let mut metrics: DWRITE_FONT_METRICS = mem::zeroed();
|
||||
(**font_face).GetMetrics(&mut metrics);
|
||||
|
||||
let geometry_sink = PathfinderGeometrySink::new();
|
||||
let glyph_index = glyph_key.glyph_index as UINT16;
|
||||
|
||||
let result =
|
||||
(**font_face).GetGlyphRunOutline(metrics.designUnitsPerEm as FLOAT,
|
||||
&glyph_index,
|
||||
ptr::null(),
|
||||
ptr::null(),
|
||||
1,
|
||||
FALSE,
|
||||
FALSE,
|
||||
*geometry_sink as *mut IDWriteGeometrySink);
|
||||
if !winerror::SUCCEEDED(result) {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let approx_stream =
|
||||
CubicPathCommandApproxStream::new((**geometry_sink).commands.iter().cloned(),
|
||||
CURVE_APPROX_ERROR_BOUND);
|
||||
|
||||
let approx_commands: Vec<_> = approx_stream.collect();
|
||||
Ok(approx_commands)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PathfinderFontCollectionLoader {
|
||||
object: PathfinderComObject<PathfinderFontCollectionLoader>,
|
||||
font_file: PathfinderComPtr<IDWriteFontFile>,
|
||||
}
|
||||
|
||||
static PATHFINDER_FONT_COLLECTION_LOADER_VTABLE:
|
||||
IDWriteFontCollectionLoaderVtbl = IDWriteFontCollectionLoaderVtbl {
|
||||
parent: IUnknownVtbl {
|
||||
AddRef: PathfinderComObject::<PathfinderFontCollectionLoader>::AddRef,
|
||||
Release: PathfinderComObject::<PathfinderFontCollectionLoader>::Release,
|
||||
QueryInterface: PathfinderComObject::<PathfinderFontCollectionLoader>::QueryInterface,
|
||||
},
|
||||
CreateEnumeratorFromKey: PathfinderFontCollectionLoader::CreateEnumeratorFromKey,
|
||||
};
|
||||
|
||||
impl PathfinderCoclass for PathfinderFontCollectionLoader {
|
||||
type InterfaceVtable = IDWriteFontCollectionLoaderVtbl;
|
||||
fn interface_guid() -> &'static GUID { &IID_IDWriteFontCollectionLoader }
|
||||
fn vtable() -> &'static IDWriteFontCollectionLoaderVtbl {
|
||||
&PATHFINDER_FONT_COLLECTION_LOADER_VTABLE
|
||||
}
|
||||
}
|
||||
|
||||
impl PathfinderFontCollectionLoader {
|
||||
#[inline]
|
||||
fn new(font_file: PathfinderComPtr<IDWriteFontFile>)
|
||||
-> PathfinderComPtr<PathfinderFontCollectionLoader> {
|
||||
unsafe {
|
||||
PathfinderComPtr::new(Box::into_raw(Box::new(PathfinderFontCollectionLoader {
|
||||
object: PathfinderComObject::construct(),
|
||||
font_file: font_file,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn CreateEnumeratorFromKey(
|
||||
this: *mut IDWriteFontCollectionLoader,
|
||||
factory: *mut IDWriteFactory,
|
||||
_: *const c_void,
|
||||
_: UINT32,
|
||||
font_file_enumerator: *mut *mut IDWriteFontFileEnumerator)
|
||||
-> HRESULT {
|
||||
let this = this as *mut PathfinderFontCollectionLoader;
|
||||
|
||||
let factory = PathfinderComPtr::new(factory);
|
||||
let font_file = (*this).font_file.clone();
|
||||
let new_font_file_enumerator = PathfinderFontFileEnumerator::new(factory, font_file);
|
||||
|
||||
*font_file_enumerator = new_font_file_enumerator.into_raw() as
|
||||
*mut IDWriteFontFileEnumerator;
|
||||
S_OK
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PathfinderFontFileEnumerator {
|
||||
object: PathfinderComObject<PathfinderFontFileEnumerator>,
|
||||
factory: PathfinderComPtr<IDWriteFactory>,
|
||||
font_file: PathfinderComPtr<IDWriteFontFile>,
|
||||
state: PathfinderFontFileEnumeratorState,
|
||||
}
|
||||
|
||||
static PATHFINDER_FONT_FILE_ENUMERATOR_VTABLE:
|
||||
IDWriteFontFileEnumeratorVtbl = IDWriteFontFileEnumeratorVtbl {
|
||||
parent: IUnknownVtbl {
|
||||
AddRef: PathfinderComObject::<PathfinderFontFileEnumerator>::AddRef,
|
||||
Release: PathfinderComObject::<PathfinderFontFileEnumerator>::Release,
|
||||
QueryInterface: PathfinderComObject::<PathfinderFontFileEnumerator>::QueryInterface,
|
||||
},
|
||||
GetCurrentFontFile: PathfinderFontFileEnumerator::GetCurrentFontFile,
|
||||
MoveNext: PathfinderFontFileEnumerator::MoveNext,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
enum PathfinderFontFileEnumeratorState {
|
||||
Start,
|
||||
AtFontFile,
|
||||
End,
|
||||
}
|
||||
|
||||
impl PathfinderCoclass for PathfinderFontFileEnumerator {
|
||||
type InterfaceVtable = IDWriteFontFileEnumeratorVtbl;
|
||||
fn interface_guid() -> &'static GUID { &IID_IDWriteFontFileEnumerator }
|
||||
fn vtable() -> &'static IDWriteFontFileEnumeratorVtbl {
|
||||
&PATHFINDER_FONT_FILE_ENUMERATOR_VTABLE
|
||||
}
|
||||
}
|
||||
|
||||
impl PathfinderFontFileEnumerator {
|
||||
#[inline]
|
||||
fn new(factory: PathfinderComPtr<IDWriteFactory>, font_file: PathfinderComPtr<IDWriteFontFile>)
|
||||
-> PathfinderComPtr<PathfinderFontFileEnumerator> {
|
||||
unsafe {
|
||||
PathfinderComPtr::new(Box::into_raw(Box::new(PathfinderFontFileEnumerator {
|
||||
object: PathfinderComObject::construct(),
|
||||
factory: factory,
|
||||
font_file: font_file,
|
||||
state: PathfinderFontFileEnumeratorState::Start,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn GetCurrentFontFile(this: *mut IDWriteFontFileEnumerator,
|
||||
font_file: *mut *mut IDWriteFontFile)
|
||||
-> HRESULT {
|
||||
let this = this as *mut PathfinderFontFileEnumerator;
|
||||
if (*this).state != PathfinderFontFileEnumeratorState::AtFontFile {
|
||||
*font_file = ptr::null_mut();
|
||||
return E_BOUNDS
|
||||
}
|
||||
|
||||
*font_file = (*this).font_file.clone().into_raw();
|
||||
S_OK
|
||||
}
|
||||
|
||||
unsafe extern "system" fn MoveNext(this: *mut IDWriteFontFileEnumerator,
|
||||
has_current_file: *mut BOOL)
|
||||
-> HRESULT {
|
||||
let this = this as *mut PathfinderFontFileEnumerator;
|
||||
match (*this).state {
|
||||
PathfinderFontFileEnumeratorState::Start => {
|
||||
(*this).state = PathfinderFontFileEnumeratorState::AtFontFile;
|
||||
*has_current_file = TRUE;
|
||||
}
|
||||
PathfinderFontFileEnumeratorState::AtFontFile => {
|
||||
(*this).state = PathfinderFontFileEnumeratorState::End;
|
||||
*has_current_file = FALSE;
|
||||
}
|
||||
PathfinderFontFileEnumeratorState::End => *has_current_file = FALSE,
|
||||
}
|
||||
S_OK
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PathfinderFontFileLoader {
|
||||
object: PathfinderComObject<PathfinderFontFileLoader>,
|
||||
buffer: Arc<Vec<u8>>,
|
||||
}
|
||||
|
||||
static PATHFINDER_FONT_FILE_LOADER_VTABLE: IDWriteFontFileLoaderVtbl = IDWriteFontFileLoaderVtbl {
|
||||
parent: IUnknownVtbl {
|
||||
AddRef: PathfinderComObject::<PathfinderFontFileLoader>::AddRef,
|
||||
Release: PathfinderComObject::<PathfinderFontFileLoader>::Release,
|
||||
QueryInterface: PathfinderComObject::<PathfinderFontFileLoader>::QueryInterface,
|
||||
},
|
||||
CreateStreamFromKey: PathfinderFontFileLoader::CreateStreamFromKey,
|
||||
};
|
||||
|
||||
impl PathfinderCoclass for PathfinderFontFileLoader {
|
||||
type InterfaceVtable = IDWriteFontFileLoaderVtbl;
|
||||
fn interface_guid() -> &'static GUID { &IID_IDWriteFontFileLoader }
|
||||
fn vtable() -> &'static IDWriteFontFileLoaderVtbl { &PATHFINDER_FONT_FILE_LOADER_VTABLE }
|
||||
}
|
||||
|
||||
impl PathfinderFontFileLoader {
|
||||
#[inline]
|
||||
fn new(buffer: Arc<Vec<u8>>) -> PathfinderComPtr<PathfinderFontFileLoader> {
|
||||
unsafe {
|
||||
PathfinderComPtr::new(Box::into_raw(Box::new(PathfinderFontFileLoader {
|
||||
object: PathfinderComObject::construct(),
|
||||
buffer: buffer,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn CreateStreamFromKey(
|
||||
this: *mut IDWriteFontFileLoader,
|
||||
font_file_reference_key: *const c_void,
|
||||
font_file_reference_key_size: UINT32,
|
||||
font_file_stream: *mut *mut IDWriteFontFileStream)
|
||||
-> HRESULT {
|
||||
let this = this as *mut PathfinderFontFileLoader;
|
||||
let font_file_reference = slice::from_raw_parts(font_file_reference_key as *const u8,
|
||||
font_file_reference_key_size as usize);
|
||||
if font_file_reference != PATHFINDER_FONT_FILE_KEY {
|
||||
*font_file_stream = ptr::null_mut();
|
||||
return E_INVALIDARG
|
||||
}
|
||||
|
||||
*font_file_stream = PathfinderFontFileStream::new((*this).buffer.clone()).into_raw() as
|
||||
*mut IDWriteFontFileStream;
|
||||
S_OK
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PathfinderFontFileStream {
|
||||
object: PathfinderComObject<PathfinderFontFileStream>,
|
||||
buffer: Arc<Vec<u8>>,
|
||||
creation_time: UINT64,
|
||||
}
|
||||
|
||||
static PATHFINDER_FONT_FILE_STREAM_VTABLE: IDWriteFontFileStreamVtbl = IDWriteFontFileStreamVtbl {
|
||||
parent: IUnknownVtbl {
|
||||
AddRef: PathfinderComObject::<PathfinderFontFileStream>::AddRef,
|
||||
Release: PathfinderComObject::<PathfinderFontFileStream>::Release,
|
||||
QueryInterface: PathfinderComObject::<PathfinderFontFileStream>::QueryInterface,
|
||||
},
|
||||
GetFileSize: PathfinderFontFileStream::GetFileSize,
|
||||
GetLastWriteTime: PathfinderFontFileStream::GetLastWriteTime,
|
||||
ReadFileFragment: PathfinderFontFileStream::ReadFileFragment,
|
||||
ReleaseFileFragment: PathfinderFontFileStream::ReleaseFileFragment,
|
||||
};
|
||||
|
||||
impl PathfinderCoclass for PathfinderFontFileStream {
|
||||
type InterfaceVtable = IDWriteFontFileStreamVtbl;
|
||||
fn interface_guid() -> &'static GUID { &IID_IDWriteFontFileStream }
|
||||
fn vtable() -> &'static IDWriteFontFileStreamVtbl { &PATHFINDER_FONT_FILE_STREAM_VTABLE }
|
||||
}
|
||||
|
||||
impl PathfinderFontFileStream {
|
||||
#[inline]
|
||||
fn new(buffer: Arc<Vec<u8>>) -> PathfinderComPtr<PathfinderFontFileStream> {
|
||||
unsafe {
|
||||
let mut now = FILETIME {
|
||||
dwLowDateTime: 0,
|
||||
dwHighDateTime: 0,
|
||||
};
|
||||
kernel32::GetSystemTimeAsFileTime(&mut now);
|
||||
|
||||
PathfinderComPtr::new(Box::into_raw(Box::new(PathfinderFontFileStream {
|
||||
object: PathfinderComObject::construct(),
|
||||
buffer: buffer,
|
||||
creation_time: ((now.dwHighDateTime as UINT64) << 32) |
|
||||
(now.dwLowDateTime as UINT64),
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn GetFileSize(this: *mut IDWriteFontFileStream, file_size: *mut UINT64)
|
||||
-> HRESULT {
|
||||
let this = this as *mut PathfinderFontFileStream;
|
||||
*file_size = (*this).buffer.len() as UINT64;
|
||||
S_OK
|
||||
}
|
||||
|
||||
unsafe extern "system" fn GetLastWriteTime(this: *mut IDWriteFontFileStream,
|
||||
last_write_time: *mut UINT64)
|
||||
-> HRESULT {
|
||||
let this = this as *mut PathfinderFontFileStream;
|
||||
*last_write_time = (*this).creation_time;
|
||||
S_OK
|
||||
}
|
||||
|
||||
unsafe extern "system" fn ReadFileFragment(this: *mut IDWriteFontFileStream,
|
||||
fragment_start: *mut *const c_void,
|
||||
file_offset: UINT64,
|
||||
fragment_size: UINT64,
|
||||
fragment_context: *mut *mut c_void)
|
||||
-> HRESULT {
|
||||
let this = this as *mut PathfinderFontFileStream;
|
||||
let buffer_length = (*this).buffer.len() as u64;
|
||||
if file_offset > buffer_length || file_offset + fragment_size > buffer_length {
|
||||
return E_BOUNDS
|
||||
}
|
||||
|
||||
let ptr = (*(*this).buffer).as_ptr().offset(file_offset as isize) as *const c_void;
|
||||
*fragment_start = ptr;
|
||||
*fragment_context = ptr as *mut c_void;
|
||||
(*(this as *mut IUnknown)).AddRef();
|
||||
S_OK
|
||||
}
|
||||
|
||||
unsafe extern "system" fn ReleaseFileFragment(this: *mut IDWriteFontFileStream,
|
||||
_: *mut c_void) {
|
||||
let this = this as *mut PathfinderFontFileStream;
|
||||
(*(this as *mut IUnknown)).Release();
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PathfinderGeometrySink {
|
||||
object: PathfinderComObject<PathfinderGeometrySink>,
|
||||
commands: Vec<CubicPathCommand>,
|
||||
}
|
||||
|
||||
static PATHFINDER_GEOMETRY_SINK_VTABLE: ID2D1SimplifiedGeometrySinkVtbl =
|
||||
ID2D1SimplifiedGeometrySinkVtbl {
|
||||
parent: IUnknownVtbl {
|
||||
AddRef: PathfinderComObject::<PathfinderGeometrySink>::AddRef,
|
||||
Release: PathfinderComObject::<PathfinderGeometrySink>::Release,
|
||||
QueryInterface: PathfinderComObject::<PathfinderGeometrySink>::QueryInterface,
|
||||
},
|
||||
AddBeziers: PathfinderGeometrySink::AddBeziers,
|
||||
AddLines: PathfinderGeometrySink::AddLines,
|
||||
BeginFigure: PathfinderGeometrySink::BeginFigure,
|
||||
Close: PathfinderGeometrySink::Close,
|
||||
EndFigure: PathfinderGeometrySink::EndFigure,
|
||||
SetFillMode: PathfinderGeometrySink::SetFillMode,
|
||||
SetSegmentFlags: PathfinderGeometrySink::SetSegmentFlags,
|
||||
};
|
||||
|
||||
impl PathfinderCoclass for PathfinderGeometrySink {
|
||||
type InterfaceVtable = ID2D1SimplifiedGeometrySinkVtbl;
|
||||
fn interface_guid() -> &'static GUID { unsafe { &IID_ID2D1SimplifiedGeometrySink } }
|
||||
fn vtable() -> &'static ID2D1SimplifiedGeometrySinkVtbl { &PATHFINDER_GEOMETRY_SINK_VTABLE }
|
||||
}
|
||||
|
||||
impl PathfinderGeometrySink {
|
||||
#[inline]
|
||||
fn new() -> PathfinderComPtr<PathfinderGeometrySink> {
|
||||
unsafe {
|
||||
PathfinderComPtr::new(Box::into_raw(Box::new(PathfinderGeometrySink {
|
||||
object: PathfinderComObject::construct(),
|
||||
commands: vec![],
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn AddBeziers(this: *mut IDWriteGeometrySink,
|
||||
beziers: *const D2D1_BEZIER_SEGMENT,
|
||||
beziers_count: UINT) {
|
||||
let this = this as *mut PathfinderGeometrySink;
|
||||
let beziers = slice::from_raw_parts(beziers, beziers_count as usize);
|
||||
for bezier in beziers {
|
||||
let control_point_0 =
|
||||
PathfinderGeometrySink::d2d_point_2f_to_flipped_f32_point(&bezier.point1);
|
||||
let control_point_1 =
|
||||
PathfinderGeometrySink::d2d_point_2f_to_flipped_f32_point(&bezier.point2);
|
||||
let endpoint =
|
||||
PathfinderGeometrySink::d2d_point_2f_to_flipped_f32_point(&bezier.point3);
|
||||
(*this).commands.push(CubicPathCommand::CubicCurveTo(control_point_0,
|
||||
control_point_1,
|
||||
endpoint))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn AddLines(this: *mut IDWriteGeometrySink,
|
||||
points: *const D2D1_POINT_2F,
|
||||
points_count: UINT) {
|
||||
let this = this as *mut PathfinderGeometrySink;
|
||||
let points = slice::from_raw_parts(points, points_count as usize);
|
||||
for point in points {
|
||||
let point = PathfinderGeometrySink::d2d_point_2f_to_flipped_f32_point(&point);
|
||||
(*this).commands.push(CubicPathCommand::LineTo(point))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn BeginFigure(this: *mut IDWriteGeometrySink,
|
||||
start_point: D2D1_POINT_2F,
|
||||
_: D2D1_FIGURE_BEGIN) {
|
||||
let this = this as *mut PathfinderGeometrySink;
|
||||
let start_point = PathfinderGeometrySink::d2d_point_2f_to_flipped_f32_point(&start_point);
|
||||
(*this).commands.push(CubicPathCommand::MoveTo(start_point))
|
||||
}
|
||||
|
||||
unsafe extern "system" fn Close(_: *mut IDWriteGeometrySink) -> HRESULT {
|
||||
S_OK
|
||||
}
|
||||
|
||||
unsafe extern "system" fn EndFigure(this: *mut IDWriteGeometrySink,
|
||||
figure_end: D2D1_FIGURE_END) {
|
||||
let this = this as *mut PathfinderGeometrySink;
|
||||
if figure_end == D2D1_FIGURE_END_CLOSED {
|
||||
(*this).commands.push(CubicPathCommand::ClosePath)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn SetFillMode(_: *mut IDWriteGeometrySink, _: D2D1_FILL_MODE) {
|
||||
// TODO(pcwalton)
|
||||
}
|
||||
|
||||
unsafe extern "system" fn SetSegmentFlags(_: *mut IDWriteGeometrySink, _: D2D1_PATH_SEGMENT) {
|
||||
// Should be unused.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn d2d_point_2f_to_flipped_f32_point(point: &D2D1_POINT_2F) -> Point2D<f32> {
|
||||
Point2D::new(point.x, -point.y)
|
||||
}
|
||||
}
|
|
@ -43,16 +43,18 @@ pub struct FontContext {
|
|||
}
|
||||
|
||||
impl FontContext {
|
||||
pub fn new() -> FontContext {
|
||||
pub fn new() -> Result<FontContext, ()> {
|
||||
let mut library: FT_Library = ptr::null_mut();
|
||||
unsafe {
|
||||
let result = FT_Init_FreeType(&mut library);
|
||||
assert!(result == 0, "Unable to initialize FreeType");
|
||||
if result != 0 {
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
FontContext {
|
||||
Ok(FontContext {
|
||||
library: library,
|
||||
faces: BTreeMap::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_font_from_memory(&mut self,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
extern crate app_units;
|
||||
extern crate euclid;
|
||||
extern crate libc;
|
||||
extern crate pathfinder_path_utils;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
|
@ -28,18 +29,31 @@ extern crate core_text;
|
|||
#[cfg(any(target_os = "linux", feature = "freetype"))]
|
||||
extern crate freetype_sys;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate dwrite;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate kernel32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate uuid;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[macro_use(DEFINE_GUID)]
|
||||
extern crate winapi;
|
||||
|
||||
use app_units::Au;
|
||||
use euclid::{Point2D, Size2D};
|
||||
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
|
||||
|
||||
#[cfg(all(target_os = "macos", not(feature = "freetype")))]
|
||||
pub use core_graphics::FontContext;
|
||||
#[cfg(all(target_os = "windows", not(feature = "freetype")))]
|
||||
pub use directwrite::FontContext;
|
||||
#[cfg(any(target_os = "linux", feature = "freetype"))]
|
||||
pub use freetype::FontContext;
|
||||
|
||||
#[cfg(all(target_os = "macos", not(feature = "freetype")))]
|
||||
mod core_graphics;
|
||||
|
||||
#[cfg(all(target_os = "windows", not(feature = "freetype")))]
|
||||
mod directwrite;
|
||||
#[cfg(any(target_os = "linux", feature = "freetype"))]
|
||||
mod freetype;
|
||||
|
||||
|
|
|
@ -8,10 +8,6 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(alloc_jemalloc)]
|
||||
|
||||
// needed to work around a problem with `heapsize`
|
||||
extern crate alloc_jemalloc;
|
||||
extern crate bincode;
|
||||
extern crate bit_vec;
|
||||
extern crate byteorder;
|
||||
|
|
Loading…
Reference in New Issue