diff options
author | Joe Wilm <joe@jwilm.com> | 2016-06-24 21:52:32 -0700 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2016-06-24 21:58:13 -0700 |
commit | 23dbf721547f652d4afa42e505c7703dfb927a47 (patch) | |
tree | 9945fc35fee64498cc0e0e56ff502cfbd28274d0 | |
parent | 078dd41e371a9bd905b7d99fa757baafad6a4733 (diff) | |
download | alacritty-23dbf721547f652d4afa42e505c7703dfb927a47.tar.gz alacritty-23dbf721547f652d4afa42e505c7703dfb927a47.zip |
Fix subpixel rendering for macOS
This adds a bunch of APIs to CGContext (and supporting types) that
aren't actually necessary to turn on subpixel rendering. The key for
subpixel rendering were the options passed to bitmap_context_create().
Specifically, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host
are necessary to enable it.
-rw-r--r-- | font/Cargo.toml | 2 | ||||
-rw-r--r-- | font/src/darwin/byte_order.rs | 34 | ||||
-rw-r--r-- | font/src/darwin/cg_color.rs | 79 | ||||
-rw-r--r-- | font/src/darwin/mod.rs | 147 | ||||
-rw-r--r-- | font/src/lib.rs | 3 |
5 files changed, 234 insertions, 31 deletions
diff --git a/font/Cargo.toml b/font/Cargo.toml index 8345af73..d6cfcc38 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -7,11 +7,11 @@ license = "Apache-2.0" [dependencies] euclid = "0.6.8" +libc = "0.2.11" [target.'cfg(not(target_os = "macos"))'.dependencies] servo-fontconfig = { git = "https://github.com/jwilm/rust-fontconfig" } freetype-rs = "0.9.0" -libc = "0.2.11" [target.'cfg(target_os = "macos")'.dependencies] core-text = "1.1.1" diff --git a/font/src/darwin/byte_order.rs b/font/src/darwin/byte_order.rs new file mode 100644 index 00000000..5b696554 --- /dev/null +++ b/font/src/darwin/byte_order.rs @@ -0,0 +1,34 @@ +//! Constants for bitmap byte order +#![allow(non_upper_case_globals)] +pub const kCGBitmapByteOrder32Little: u32 = 2 << 12; +pub const kCGBitmapByteOrder32Big: u32 = 4 << 12; + +#[cfg(target_endian = "little")] +pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Little; + +#[cfg(target_endian = "big")] +pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Big; + +#[cfg(target_endian = "little")] +pub fn extract_rgb(bytes: Vec<u8>) -> Vec<u8> { + let pixels = bytes.len() / 4; + let mut rgb = Vec::with_capacity(pixels * 3); + + for i in 0..pixels { + let offset = i * 4; + rgb.push(bytes[offset + 2]); + rgb.push(bytes[offset + 1]); + rgb.push(bytes[offset + 0]); + } + + rgb +} + +#[cfg(target_endian = "big")] +pub fn extract_rgb(bytes: Vec<u8>) -> Vec<u8> { + bytes.into_iter() + .enumerate() + .filter(|&(index, _)| ((index) % 4) != 0) + .map(|(_, val)| val) + .collect::<Vec<_>>() +} diff --git a/font/src/darwin/cg_color.rs b/font/src/darwin/cg_color.rs new file mode 100644 index 00000000..552137cc --- /dev/null +++ b/font/src/darwin/cg_color.rs @@ -0,0 +1,79 @@ +use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType}; +use core_graphics::color_space::{CGColorSpace, CGColorSpaceRef}; +use core_graphics::base::CGFloat; +use std::mem; + +#[repr(C)] +pub struct __CGColor; + +pub type CGColorRef = *const __CGColor; + +pub struct CGColor { + obj: CGColorRef, +} + +impl Drop for CGColor { + fn drop(&mut self) { + unsafe { + CFRelease(self.as_CFTypeRef()) + } + } +} + +impl Clone for CGColor { + fn clone(&self) -> CGColor { + unsafe { + TCFType::wrap_under_get_rule(self.as_concrete_TypeRef()) + } + } +} + +impl TCFType<CGColorRef> for CGColor { + #[inline] + fn as_concrete_TypeRef(&self) -> CGColorRef { + self.obj + } + + #[inline] + unsafe fn wrap_under_get_rule(reference: CGColorRef) -> CGColor { + let reference: CGColorRef = mem::transmute(CFRetain(mem::transmute(reference))); + TCFType::wrap_under_create_rule(reference) + } + + #[inline] + fn as_CFTypeRef(&self) -> CFTypeRef { + unsafe { + mem::transmute(self.as_concrete_TypeRef()) + } + } + + #[inline] + unsafe fn wrap_under_create_rule(obj: CGColorRef) -> CGColor { + CGColor { + obj: obj, + } + } + + #[inline] + fn type_id() -> CFTypeID { + unsafe { + CGColorGetTypeID() + } + } +} + +impl CGColor { + pub fn new(color_space: CGColorSpace, values: [CGFloat; 4]) -> CGColor { + unsafe { + let result = CGColorCreate(color_space.as_concrete_TypeRef(), values.as_ptr()); + TCFType::wrap_under_create_rule(result) + } + } +} + +#[link(name = "ApplicationServices", kind = "framework")] +extern { + fn CGColorCreate(space: CGColorSpaceRef, vals: *const CGFloat) -> CGColorRef; + fn CGColorGetTypeID() -> CFTypeID; +} + diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 844850e0..5e53abd5 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -1,20 +1,20 @@ //! Font rendering based on CoreText //! //! TODO error handling... just search for unwrap. +#![allow(improper_ctypes)] use std::collections::HashMap; -use std::ops::Deref; use std::ptr; use core_foundation::base::TCFType; use core_foundation::string::{CFString, CFStringRef}; use core_foundation::array::CFIndex; use core_foundation_sys::string::UniChar; -use core_graphics::base::kCGImageAlphaNoneSkipFirst; -use core_graphics::base::kCGImageAlphaPremultipliedLast; +use core_graphics::base::kCGImageAlphaPremultipliedFirst; +use core_graphics::base::CGFloat; use core_graphics::color_space::CGColorSpace; use core_graphics::context::{CGContext, CGContextRef}; -use core_graphics::font::CGGlyph; -use core_graphics::geometry::CGPoint; +use core_graphics::font::{CGFont, CGFontRef, CGGlyph}; +use core_graphics::geometry::{CGPoint, CGRect, CGSize}; use core_text::font::{CTFont, new_from_descriptor as ct_new_from_descriptor}; use core_text::font_collection::create_for_family; use core_text::font_collection::get_family_names as ct_get_family_names; @@ -23,12 +23,21 @@ use core_text::font_descriptor::kCTFontHorizontalOrientation; use core_text::font_descriptor::kCTFontVerticalOrientation; use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, CTFontOrientation}; +use libc::size_t; + use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; use super::{FontDesc, RasterizedGlyph, Metrics}; +pub mod cg_color; +use self::cg_color::{CGColorRef, CGColor}; + +pub mod byte_order; +use self::byte_order::kCGBitmapByteOrder32Host; +use self::byte_order::extract_rgb; + /// Font descriptor /// /// The descriptor provides data about a font and supports creating a font. @@ -52,7 +61,7 @@ pub struct Rasterizer { } impl Rasterizer { - pub fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32) -> Rasterizer { + pub fn new(_dpi_x: f32, _dpi_y: f32, device_pixel_ratio: f32) -> Rasterizer { println!("device_pixel_ratio: {}", device_pixel_ratio); Rasterizer { fonts: HashMap::new(), @@ -106,9 +115,10 @@ impl Default for FontOrientation { } /// A font -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Font { - ct_font: CTFont + ct_font: CTFont, + cg_font: CGFont, } unsafe impl Send for Font {} @@ -159,21 +169,14 @@ impl Descriptor { /// Create a Font from this descriptor pub fn to_font(&self, pt_size: f64) -> Font { let ct_font = ct_new_from_descriptor(&self.ct_descriptor, pt_size); + let cg_font = ct_font.copy_to_CGFont(); Font { - ct_font: ct_font + ct_font: ct_font, + cg_font: cg_font, } } } -impl Deref for Font { - type Target = CTFont; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.ct_font - } -} - impl Font { /// The the bounding rect of a glyph pub fn bounding_rect_for_glyph(&self, orientation: FontOrientation, index: u32) -> Rect<f64> { @@ -211,7 +214,7 @@ impl Font { 1) } - pub fn get_glyph(&self, character: char, size: f64) -> RasterizedGlyph { + pub fn get_glyph(&self, character: char, _size: f64) -> RasterizedGlyph { let glyph_index = match self.glyph_index(character) { Some(i) => i, None => { @@ -252,14 +255,27 @@ impl Font { 8, // bits per component rasterized_width as usize * 4, &CGColorSpace::create_device_rgb(), - kCGImageAlphaNoneSkipFirst); + kCGImageAlphaPremultipliedFirst | + kCGBitmapByteOrder32Host); + + // Give the context an opaque, black background + cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0); + let context_rect = CGRect::new(&CGPoint::new(0.0, 0.0), + &CGSize::new(rasterized_width as f64, + rasterized_height as f64)); + cg_context.fill_rect(context_rect); cg_context.set_allows_font_smoothing(true); cg_context.set_should_smooth_fonts(true); cg_context.set_allows_font_subpixel_quantization(true); cg_context.set_should_subpixel_quantize_fonts(true); - cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); + cg_context.set_allows_font_subpixel_positioning(true); + cg_context.set_should_subpixel_position_fonts(true); + cg_context.set_allows_antialiasing(true); + cg_context.set_should_antialias(true); + // Set fill color to white for drawing the glyph + cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); let rasterization_origin = CGPoint { x: -rasterized_left as f64, y: rasterized_descent as f64, @@ -269,13 +285,10 @@ impl Font { &[rasterization_origin], cg_context.clone()); - let rasterized_area = (rasterized_width * rasterized_height) as usize; let rasterized_pixels = cg_context.data().to_vec(); - let buf = rasterized_pixels.into_iter() - .enumerate() - .filter(|&(index, _)| (index % 4) != 0) - .map(|(_, val)| val) - .collect::<Vec<_>>(); + println!("rasterized_pixels: {:?}", rasterized_pixels); + + let buf = extract_rgb(rasterized_pixels); RasterizedGlyph { c: character, @@ -304,9 +317,18 @@ impl Font { /// Additional methods needed to render fonts for Alacritty /// /// TODO upstream these into core_graphics crate -trait CGContextExt { +pub trait CGContextExt { fn set_allows_font_subpixel_quantization(&self, bool); fn set_should_subpixel_quantize_fonts(&self, bool); + fn set_allows_font_subpixel_positioning(&self, bool); + fn set_should_subpixel_position_fonts(&self, bool); + fn set_allows_antialiasing(&self, bool); + fn set_should_antialias(&self, bool); + fn fill_rect(&self, rect: CGRect); + fn set_font_smoothing_background_color(&self, color: CGColor); + fn show_glyphs_at_positions(&self, &[CGGlyph], &[CGPoint]); + fn set_font(&self, &CGFont); + fn set_font_size(&self, size: f64); } impl CGContextExt for CGContext { @@ -321,12 +343,81 @@ impl CGContextExt for CGContext { CGContextSetShouldSubpixelQuantizeFonts(self.as_concrete_TypeRef(), should); } } + + fn set_should_subpixel_position_fonts(&self, should: bool) { + unsafe { + CGContextSetShouldSubpixelPositionFonts(self.as_concrete_TypeRef(), should); + } + } + + fn set_allows_font_subpixel_positioning(&self, allows: bool) { + unsafe { + CGContextSetAllowsFontSubpixelPositioning(self.as_concrete_TypeRef(), allows); + } + } + + fn set_should_antialias(&self, should: bool) { + unsafe { + CGContextSetShouldAntialias(self.as_concrete_TypeRef(), should); + } + } + + fn set_allows_antialiasing(&self, allows: bool) { + unsafe { + CGContextSetAllowsAntialiasing(self.as_concrete_TypeRef(), allows); + } + } + + fn fill_rect(&self, rect: CGRect) { + unsafe { + CGContextFillRect(self.as_concrete_TypeRef(), rect); + } + } + + fn set_font_smoothing_background_color(&self, color: CGColor) { + unsafe { + CGContextSetFontSmoothingBackgroundColor(self.as_concrete_TypeRef(), + color.as_concrete_TypeRef()); + } + } + + fn show_glyphs_at_positions(&self, glyphs: &[CGGlyph], positions: &[CGPoint]) { + assert_eq!(glyphs.len(), positions.len()); + unsafe { + CGContextShowGlyphsAtPositions(self.as_concrete_TypeRef(), + glyphs.as_ptr(), + positions.as_ptr(), + glyphs.len()); + } + } + + fn set_font(&self, font: &CGFont) { + unsafe { + CGContextSetFont(self.as_concrete_TypeRef(), font.as_concrete_TypeRef()); + } + } + + fn set_font_size(&self, size: f64) { + unsafe { + CGContextSetFontSize(self.as_concrete_TypeRef(), size as CGFloat); + } + } } #[link(name = "ApplicationServices", kind = "framework")] extern { fn CGContextSetAllowsFontSubpixelQuantization(c: CGContextRef, allows: bool); fn CGContextSetShouldSubpixelQuantizeFonts(c: CGContextRef, should: bool); + fn CGContextSetAllowsFontSubpixelPositioning(c: CGContextRef, allows: bool); + fn CGContextSetShouldSubpixelPositionFonts(c: CGContextRef, should: bool); + fn CGContextSetAllowsAntialiasing(c: CGContextRef, allows: bool); + fn CGContextSetShouldAntialias(c: CGContextRef, should: bool); + fn CGContextFillRect(c: CGContextRef, r: CGRect); + fn CGContextSetFontSmoothingBackgroundColor(c: CGContextRef, color: CGColorRef); + fn CGContextShowGlyphsAtPositions(c: CGContextRef, glyphs: *const CGGlyph, + positions: *const CGPoint, count: size_t); + fn CGContextSetFont(c: CGContextRef, font: CGFontRef); + fn CGContextSetFontSize(c: CGContextRef, size: CGFloat); } #[cfg(test)] diff --git a/font/src/lib.rs b/font/src/lib.rs index ba632bee..bd485ff0 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -10,8 +10,6 @@ extern crate fontconfig; #[cfg(not(target_os = "macos"))] extern crate freetype; -#[cfg(not(target_os = "macos"))] -extern crate libc; #[cfg(target_os = "macos")] extern crate core_text; @@ -23,6 +21,7 @@ extern crate core_foundation_sys; extern crate core_graphics; extern crate euclid; +extern crate libc; use std::fmt; |