From 0b179f3a40868de7dca182337428b0984c05083a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 23 Oct 2017 09:40:27 -0700 Subject: [PATCH] Add preliminary DirectWrite support on Windows. This will eventually need to call into GDI to do hinting, but it's good enough for now. --- demo/client/src/3d-demo.ts | 2 +- demo/client/src/app-controller.ts | 2 +- demo/server/package-lock.json | 3 + demo/server/src/main.rs | 11 +- font-renderer/Cargo.toml | 7 + font-renderer/src/core_graphics.rs | 49 +-- font-renderer/src/directwrite/com.rs | 124 ++++++ font-renderer/src/directwrite/mod.rs | 619 +++++++++++++++++++++++++++ font-renderer/src/freetype/mod.rs | 10 +- font-renderer/src/lib.rs | 16 +- partitioner/src/lib.rs | 4 - 11 files changed, 797 insertions(+), 50 deletions(-) create mode 100644 demo/server/package-lock.json create mode 100644 font-renderer/src/directwrite/com.rs create mode 100644 font-renderer/src/directwrite/mod.rs diff --git a/demo/client/src/3d-demo.ts b/demo/client/src/3d-demo.ts index 42cca77b..67263f7c 100644 --- a/demo/client/src/3d-demo.ts +++ b/demo/client/src/3d-demo.ts @@ -524,7 +524,7 @@ class ThreeDRenderer extends Renderer { } protected newTimingsReceived(): void { - const newTimings: Timings = _.pick(this.lastTimings, ['rendering']); + const newTimings: Partial = _.pick(this.lastTimings, ['rendering']); this.renderContext.appController.newTimingsReceived(newTimings); } diff --git a/demo/client/src/app-controller.ts b/demo/client/src/app-controller.ts index a5f28d1b..0420f125 100644 --- a/demo/client/src/app-controller.ts +++ b/demo/client/src/app-controller.ts @@ -177,7 +177,7 @@ export abstract class DemoAppController extends AppContro this.updateAALevel(); } - newTimingsReceived(timings: Timings) { + newTimingsReceived(timings: Partial) { if (this.fpsLabel == null) return; diff --git a/demo/server/package-lock.json b/demo/server/package-lock.json new file mode 100644 index 00000000..48e341a0 --- /dev/null +++ b/demo/server/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index 8fbe80df..199b14f7 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -274,8 +274,15 @@ fn partition_font(request: Json) 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) // 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) } diff --git a/font-renderer/Cargo.toml b/font-renderer/Cargo.toml index 3c873aaa..27b93e9c 100644 --- a/font-renderer/Cargo.toml +++ b/font-renderer/Cargo.toml @@ -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" diff --git a/font-renderer/src/core_graphics.rs b/font-renderer/src/core_graphics.rs index 6c94ca5e..fd13694e 100644 --- a/font-renderer/src/core_graphics.rs +++ b/font-renderer/src/core_graphics.rs @@ -25,17 +25,19 @@ use {FontInstanceKey, FontKey, GlyphDimensions, GlyphKey}; const CURVE_APPROX_ERROR_BOUND: f32 = 0.1; +pub type GlyphOutline = Vec; + pub struct FontContext { core_graphics_fonts: BTreeMap, core_text_fonts: BTreeMap, } impl FontContext { - pub fn new() -> FontContext { - FontContext { + pub fn new() -> Result { + 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>, _: 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 { - Ok(core_text::font::new_from_CGFont(core_graphics_font, self.size.to_f64_px())) - } -} - -pub struct GlyphOutline { - commands: Vec, - index: usize, -} - -impl Iterator for GlyphOutline { - type Item = PathCommand; - - fn next(&mut self) -> Option { - 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 { Point2D::new(core_graphics_point.x as f32, core_graphics_point.y as f32) } } } + +impl FontInstanceKey { + fn instantiate(&self, core_graphics_font: &CGFont) -> Result { + Ok(core_text::font::new_from_CGFont(core_graphics_font, self.size.to_f64_px())) + } +} diff --git a/font-renderer/src/directwrite/com.rs b/font-renderer/src/directwrite/com.rs new file mode 100644 index 00000000..0421c761 --- /dev/null +++ b/font-renderer/src/directwrite/com.rs @@ -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 or the MIT license +// , 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 { + ptr: *mut T, +} + +impl PathfinderComPtr { + #[inline] + pub unsafe fn new(ptr: *mut T) -> PathfinderComPtr { + PathfinderComPtr { + ptr: ptr, + } + } + + #[inline] + pub fn into_raw(self) -> *mut T { + let ptr = self.ptr; + mem::forget(self); + ptr + } +} + +impl Clone for PathfinderComPtr { + #[inline] + fn clone(&self) -> PathfinderComPtr { + unsafe { + (*(self.ptr as *mut IUnknown)).AddRef(); + } + PathfinderComPtr { + ptr: self.ptr, + } + } +} + +impl Drop for PathfinderComPtr { + #[inline] + fn drop(&mut self) { + unsafe { + (*(self.ptr as *mut IUnknown)).Release(); + } + } +} + +impl Deref for PathfinderComPtr { + 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 where DerivedClass: PathfinderCoclass { + vtable: &'static DerivedClass::InterfaceVtable, + ref_count: AtomicUsize, +} + +impl PathfinderComObject where DerivedClass: PathfinderCoclass { + #[inline] + pub unsafe fn construct() -> PathfinderComObject { + 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; + ((*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; + 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 +} \ No newline at end of file diff --git a/font-renderer/src/directwrite/mod.rs b/font-renderer/src/directwrite/mod.rs new file mode 100644 index 00000000..bcaef015 --- /dev/null +++ b/font-renderer/src/directwrite/mod.rs @@ -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 or the MIT license +// , 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; + +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, + #[allow(unused)] + dwrite_gdi_interop: PathfinderComPtr, + dwrite_font_faces: BTreeMap>, +} + +impl FontContext { + pub fn new() -> Result { + 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>, _: 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 { + 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 { + 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, + font_file: PathfinderComPtr, +} + +static PATHFINDER_FONT_COLLECTION_LOADER_VTABLE: + IDWriteFontCollectionLoaderVtbl = IDWriteFontCollectionLoaderVtbl { + parent: IUnknownVtbl { + AddRef: PathfinderComObject::::AddRef, + Release: PathfinderComObject::::Release, + QueryInterface: PathfinderComObject::::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) + -> PathfinderComPtr { + 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, + factory: PathfinderComPtr, + font_file: PathfinderComPtr, + state: PathfinderFontFileEnumeratorState, +} + +static PATHFINDER_FONT_FILE_ENUMERATOR_VTABLE: + IDWriteFontFileEnumeratorVtbl = IDWriteFontFileEnumeratorVtbl { + parent: IUnknownVtbl { + AddRef: PathfinderComObject::::AddRef, + Release: PathfinderComObject::::Release, + QueryInterface: PathfinderComObject::::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, font_file: PathfinderComPtr) + -> PathfinderComPtr { + 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, + buffer: Arc>, +} + +static PATHFINDER_FONT_FILE_LOADER_VTABLE: IDWriteFontFileLoaderVtbl = IDWriteFontFileLoaderVtbl { + parent: IUnknownVtbl { + AddRef: PathfinderComObject::::AddRef, + Release: PathfinderComObject::::Release, + QueryInterface: PathfinderComObject::::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>) -> PathfinderComPtr { + 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, + buffer: Arc>, + creation_time: UINT64, +} + +static PATHFINDER_FONT_FILE_STREAM_VTABLE: IDWriteFontFileStreamVtbl = IDWriteFontFileStreamVtbl { + parent: IUnknownVtbl { + AddRef: PathfinderComObject::::AddRef, + Release: PathfinderComObject::::Release, + QueryInterface: PathfinderComObject::::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>) -> PathfinderComPtr { + 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, + commands: Vec, +} + +static PATHFINDER_GEOMETRY_SINK_VTABLE: ID2D1SimplifiedGeometrySinkVtbl = + ID2D1SimplifiedGeometrySinkVtbl { + parent: IUnknownVtbl { + AddRef: PathfinderComObject::::AddRef, + Release: PathfinderComObject::::Release, + QueryInterface: PathfinderComObject::::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 { + 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 { + Point2D::new(point.x, -point.y) + } +} diff --git a/font-renderer/src/freetype/mod.rs b/font-renderer/src/freetype/mod.rs index a16987b6..b033a3ae 100644 --- a/font-renderer/src/freetype/mod.rs +++ b/font-renderer/src/freetype/mod.rs @@ -43,16 +43,18 @@ pub struct FontContext { } impl FontContext { - pub fn new() -> FontContext { + pub fn new() -> Result { 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, diff --git a/font-renderer/src/lib.rs b/font-renderer/src/lib.rs index 5a1dde5c..5b53a12d 100644 --- a/font-renderer/src/lib.rs +++ b/font-renderer/src/lib.rs @@ -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; diff --git a/partitioner/src/lib.rs b/partitioner/src/lib.rs index 839f2361..aa5e52f2 100644 --- a/partitioner/src/lib.rs +++ b/partitioner/src/lib.rs @@ -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;