diff --git a/Cargo.lock b/Cargo.lock index b99a00db04..1954c8b64f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,7 +197,7 @@ dependencies = [ [[package]] name = "core-graphics" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -219,11 +219,11 @@ dependencies = [ [[package]] name = "core-text" -version = "11.0.0" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -904,11 +904,11 @@ dependencies = [ [[package]] name = "pathfinder_font_renderer" version = "0.5.0" -source = "git+https://github.com/pcwalton/pathfinder#a518cdac215e3dc3980affbc1fa0fb173ed709fa" +source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#d6a4ade138f665c8f773f65c3afd683fa5cae013" dependencies = [ "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrite-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -925,7 +925,7 @@ dependencies = [ [[package]] name = "pathfinder_gfx_utils" version = "0.2.0" -source = "git+https://github.com/pcwalton/pathfinder#a518cdac215e3dc3980affbc1fa0fb173ed709fa" +source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#d6a4ade138f665c8f773f65c3afd683fa5cae013" dependencies = [ "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -933,7 +933,7 @@ dependencies = [ [[package]] name = "pathfinder_partitioner" version = "0.2.0" -source = "git+https://github.com/pcwalton/pathfinder#a518cdac215e3dc3980affbc1fa0fb173ed709fa" +source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#d6a4ade138f665c8f773f65c3afd683fa5cae013" dependencies = [ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -945,7 +945,7 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "lyon_geom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder)", + "pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)", ] @@ -953,7 +953,7 @@ dependencies = [ [[package]] name = "pathfinder_path_utils" version = "0.2.0" -source = "git+https://github.com/pcwalton/pathfinder#a518cdac215e3dc3980affbc1fa0fb173ed709fa" +source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#d6a4ade138f665c8f773f65c3afd683fa5cae013" dependencies = [ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1480,8 +1480,8 @@ dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1492,10 +1492,10 @@ dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mozangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pathfinder_font_renderer 0.5.0 (git+https://github.com/pcwalton/pathfinder)", - "pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder)", - "pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder)", - "pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder)", + "pathfinder_font_renderer 0.5.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)", + "pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)", + "pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)", + "pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)", "plane-split 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1532,7 +1532,7 @@ dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1610,7 +1610,7 @@ dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1717,9 +1717,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7caa6cb9e76ddddbea09a03266d6b3bc98cd41e9fb9b017c473e7cca593ec25" "checksum core-foundation-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2a53cce0ddcf7e7e1f998738d757d5a3bf08bf799a180e50ebe50d298f52f5a" "checksum core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e54c4ab33705fa1fc8af375bb7929d68e1c1546c1ecef408966d8c3e49a1d84a" -"checksum core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92801c908ea6301ae619ed842a72e01098085fc321b9c2f3f833dad555bba055" +"checksum core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "62ceafe1622ffc9a332199096841d0ff9912ec8cf8f9cde01e254a7d5217cd10" "checksum core-text 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f59bff773954e5cd058a3f5983406b52bec7cc65202bef340ba64a0c40ac91" -"checksum core-text 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "157ff38a92496dc676ce36d9124554e9ac66f1c1039f952690ac64f71cfa5968" +"checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae" "checksum crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be" "checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" @@ -1797,10 +1797,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69376b761943787ebd5cc85a5bc95958651a22609c5c1c2b65de21786baec72b" "checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" -"checksum pathfinder_font_renderer 0.5.0 (git+https://github.com/pcwalton/pathfinder)" = "" -"checksum pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder)" = "" -"checksum pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder)" = "" -"checksum pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder)" = "" +"checksum pathfinder_font_renderer 0.5.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "" +"checksum pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "" +"checksum pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "" +"checksum pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" "checksum plane-split 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64d766f38b15fe1337bdddfc869ef5c50437323f857aaaadc6490197db80a1b8" diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index 037ce18da8..d2144118f1 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -46,20 +46,24 @@ ws = { optional = true, version = "0.7.3" } [dependencies.pathfinder_font_renderer] git = "https://github.com/pcwalton/pathfinder" +branch = "webrender" optional = true # Uncomment to test FreeType on macOS: # features = ["freetype"] [dependencies.pathfinder_gfx_utils] git = "https://github.com/pcwalton/pathfinder" +branch = "webrender" optional = true [dependencies.pathfinder_partitioner] git = "https://github.com/pcwalton/pathfinder" +branch = "webrender" optional = true [dependencies.pathfinder_path_utils] git = "https://github.com/pcwalton/pathfinder" +branch = "webrender" optional = true [dev-dependencies] @@ -73,5 +77,5 @@ dwrote = "0.4.1" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.6" -core-graphics = "0.16" -core-text = { version = "11", default-features = false } +core-graphics = "0.17.1" +core-text = { version = "13", default-features = false } diff --git a/webrender/src/platform/macos/font.rs b/webrender/src/platform/macos/font.rs index 7e2dd1b6ea..5e14e56d9f 100644 --- a/webrender/src/platform/macos/font.rs +++ b/webrender/src/platform/macos/font.rs @@ -10,21 +10,21 @@ use core_foundation::base::TCFType; use core_foundation::dictionary::CFDictionary; use core_foundation::number::{CFNumber, CFNumberRef}; use core_foundation::string::{CFString, CFStringRef}; -use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGBitmapByteOrder32Little}; -#[cfg(not(feature = "pathfinder"))] -use core_graphics::base::kCGImageAlphaPremultipliedFirst; +use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst}; +use core_graphics::base::{kCGBitmapByteOrder32Little}; use core_graphics::color_space::CGColorSpace; use core_graphics::context::CGContext; #[cfg(not(feature = "pathfinder"))] -use core_graphics::context::CGTextDrawingMode; +use core_graphics::context::{CGBlendMode, CGTextDrawingMode}; use core_graphics::data_provider::CGDataProvider; use core_graphics::font::{CGFont, CGGlyph}; use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize}; #[cfg(not(feature = "pathfinder"))] -use core_graphics::geometry::CGRect; +use core_graphics::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGRect}; use core_text; use core_text::font::{CTFont, CTFontRef}; use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait}; +use euclid::Size2D; use gamma_lut::{ColorLut, GammaLut}; use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey}; #[cfg(feature = "pathfinder")] @@ -35,10 +35,14 @@ use internal_types::{FastHashMap, ResourceCacheError}; use std::collections::hash_map::Entry; use std::sync::Arc; +const INITIAL_CG_CONTEXT_SIDE_LENGTH: u32 = 32; + pub struct FontContext { cg_fonts: FastHashMap, ct_fonts: FastHashMap<(FontKey, Au, Vec), CTFont>, #[allow(dead_code)] + graphics_context: GraphicsContext, + #[allow(dead_code)] gamma_lut: GammaLut, } @@ -116,7 +120,9 @@ fn get_glyph_metrics( } let mut advance = CGSize { width: 0.0, height: 0.0 }; - ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, &mut advance, 1); + unsafe { + ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, &mut advance, 1); + } if bounds.size.width > 0.0 { bounds.size.width += extra_width; @@ -281,6 +287,7 @@ impl FontContext { Ok(FontContext { cg_fonts: FastHashMap::default(), ct_fonts: FastHashMap::default(), + graphics_context: GraphicsContext::new(), gamma_lut: GammaLut::new(contrast, gamma, gamma), }) } @@ -344,12 +351,14 @@ impl FontContext { self.get_ct_font(font_key, Au::from_px(16), &[]) .and_then(|ref ct_font| { - let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1); + unsafe { + let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1); - if result { - Some(glyph as u32) - } else { - None + if result { + Some(glyph as u32) + } else { + None + } } }) } @@ -494,11 +503,17 @@ impl FontContext { None => return GlyphRasterResult::LoadFailed, }; - let bitmap = is_bitmap_font(&ct_font); - let (mut shape, (x_offset, y_offset)) = if bitmap { - (FontTransform::identity(), (0.0, 0.0)) + let glyph_type = if is_bitmap_font(&ct_font) { + GlyphType::Bitmap } else { - (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key)) + GlyphType::Vector + }; + + let (mut shape, (x_offset, y_offset)) = match glyph_type { + GlyphType::Bitmap => (FontTransform::identity(), (0.0, 0.0)), + GlyphType::Vector => { + (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key)) + } }; if font.flags.contains(FontInstanceFlags::FLIP_X) { shape = shape.flip_x(); @@ -526,7 +541,12 @@ impl FontContext { }; let glyph = key.index() as CGGlyph; - let (strike_scale, pixel_step) = if bitmap { (y_scale, 1.0) } else { (x_scale, y_scale / x_scale) }; + let (strike_scale, pixel_step) = if glyph_type == GlyphType::Bitmap { + (y_scale, 1.0) + } else { + (x_scale, y_scale / x_scale) + }; + let extra_strikes = font.get_extra_strikes(strike_scale / scale); let metrics = get_glyph_metrics( &ct_font, @@ -540,43 +560,7 @@ impl FontContext { return GlyphRasterResult::LoadFailed } - // The result of this function, in all render modes, is going to be a - // BGRA surface with white text on transparency using premultiplied - // alpha. For subpixel text, the RGB values will be the mask value for - // the individual components. For bitmap glyphs, the RGB values will be - // the (premultiplied) color of the pixel. For Alpha and Mono, each - // pixel will have R==G==B==A at the end of this function. - // We access the color channels in little-endian order. - // The CGContext will create and own our pixel buffer. - // In the non-Bitmap cases, we will ask CoreGraphics to draw text onto - // an opaque background. In order to hit the most efficient path in CG - // for this, we will tell CG that the CGContext is opaque, by passing - // an "[...]AlphaNone[...]" context flag. This creates a slight - // contradiction to the way we use the buffer after CG is done with it, - // because we will convert it into text-on-transparency. But that's ok; - // we still get four bytes per pixel and CG won't mess with the alpha - // channel after we've stopped calling CG functions. We just need to - // make sure that we don't look at the alpha values of the pixels that - // we get from CG, and compute our own alpha value only from RGB. - // Note that CG requires kCGBitmapByteOrder32Little in order to do - // subpixel AA at all (which we need it to do in both Subpixel and - // Alpha+smoothing mode). But little-endian is what we want anyway, so - // this works out nicely. - let context_flags = if bitmap { - kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst - } else { - kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst - }; - - let mut cg_context = CGContext::create_bitmap_context( - None, - metrics.rasterized_width as usize, - metrics.rasterized_height as usize, - 8, - metrics.rasterized_width as usize * 4, - &CGColorSpace::create_device_rgb(), - context_flags, - ); + let raster_size = Size2D::new(metrics.rasterized_width, metrics.rasterized_height); // If the font render mode is Alpha, we support two different ways to // compute the grayscale mask, depending on the value of the platform @@ -603,74 +587,92 @@ impl FontContext { // the "Alpha without smoothing" and Mono modes. let use_white_on_black = should_use_white_on_black(font.color); let use_font_smoothing = font.flags.contains(FontInstanceFlags::FONT_SMOOTHING); - let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = if bitmap { - (true, false, 0.0, 0.0, 0.0, false) - } else { - match (font.render_mode, use_font_smoothing) { - (FontRenderMode::Subpixel, _) | - (FontRenderMode::Alpha, true) => if use_white_on_black { - (true, true, 1.0, 0.0, 1.0, false) - } else { - (true, true, 0.0, 1.0, 1.0, true) - }, - (FontRenderMode::Alpha, false) => (true, false, 0.0, 1.0, 1.0, true), - (FontRenderMode::Mono, _) => (false, false, 0.0, 1.0, 1.0, true), + let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = match glyph_type { + GlyphType::Bitmap => (true, false, 0.0, 0.0, 0.0, false), + GlyphType::Vector => { + match (font.render_mode, use_font_smoothing) { + (FontRenderMode::Subpixel, _) | + (FontRenderMode::Alpha, true) => if use_white_on_black { + (true, true, 1.0, 0.0, 1.0, false) + } else { + (true, true, 0.0, 1.0, 1.0, true) + }, + (FontRenderMode::Alpha, false) => (true, false, 0.0, 1.0, 1.0, true), + (FontRenderMode::Mono, _) => (false, false, 0.0, 1.0, 1.0, true), + } } }; - // These are always true in Gecko, even for non-AA fonts - cg_context.set_allows_font_subpixel_positioning(true); - cg_context.set_should_subpixel_position_fonts(true); - - // Don't quantize because we're doing it already. - cg_context.set_allows_font_subpixel_quantization(false); - cg_context.set_should_subpixel_quantize_fonts(false); - - cg_context.set_should_smooth_fonts(smooth); - cg_context.set_should_antialias(antialias); - - // Fill the background. This could be opaque white, opaque black, or - // transparency. - cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha); - let rect = CGRect { - origin: CGPoint { x: 0.0, y: 0.0 }, - size: CGSize { - width: metrics.rasterized_width as f64, - height: metrics.rasterized_height as f64, - }, - }; - cg_context.fill_rect(rect); + { + let cg_context = self.graphics_context.get_context(&raster_size, glyph_type); - // Set the text color and draw the glyphs. - cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0); - cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill); + // These are always true in Gecko, even for non-AA fonts + cg_context.set_allows_font_subpixel_positioning(true); + cg_context.set_should_subpixel_position_fonts(true); - // CG Origin is bottom left, WR is top left. Need -y offset - let mut draw_origin = CGPoint { - x: -metrics.rasterized_left as f64 + x_offset, - y: metrics.rasterized_descent as f64 - y_offset, - }; + // Don't quantize because we're doing it already. + cg_context.set_allows_font_subpixel_quantization(false); + cg_context.set_should_subpixel_quantize_fonts(false); - if let Some(transform) = transform { - cg_context.set_text_matrix(&transform); + cg_context.set_should_smooth_fonts(smooth); + cg_context.set_should_antialias(antialias); - draw_origin = draw_origin.apply_transform(&transform.invert()); - } + // Fill the background. This could be opaque white, opaque black, or + // transparency. + cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha); + let rect = CGRect { + origin: CGPoint { x: 0.0, y: 0.0 }, + size: CGSize { + width: metrics.rasterized_width as f64, + height: metrics.rasterized_height as f64, + }, + }; - if extra_strikes > 0 { - let strikes = 1 + extra_strikes; - let glyphs = vec![glyph; strikes]; - let origins = (0..strikes) - .map(|i| CGPoint { x: draw_origin.x + i as f64 * pixel_step, y: draw_origin.y }) - .collect::>(); - ct_font.draw_glyphs(&glyphs, &origins, cg_context.clone()); - } else { - ct_font.draw_glyphs(&[glyph], &[draw_origin], cg_context.clone()); + // Make sure we use the Copy blend mode, or else we'll get the Porter-Duff OVER + // operator, which can't clear to the transparent color! + cg_context.set_blend_mode(CGBlendMode::Copy); + cg_context.fill_rect(rect); + cg_context.set_blend_mode(CGBlendMode::Normal); + + // Set the text color and draw the glyphs. + cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0); + cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill); + + // CG Origin is bottom left, WR is top left. Need -y offset + let mut draw_origin = CGPoint { + x: -metrics.rasterized_left as f64 + x_offset, + y: metrics.rasterized_descent as f64 - y_offset, + }; + + if let Some(transform) = transform { + cg_context.set_text_matrix(&transform); + + draw_origin = draw_origin.apply_transform(&transform.invert()); + } else { + // Make sure to reset this because some previous glyph rasterization might have + // changed it. + cg_context.set_text_matrix(&CG_AFFINE_TRANSFORM_IDENTITY); + } + + if extra_strikes > 0 { + let strikes = 1 + extra_strikes; + let glyphs = vec![glyph; strikes]; + let origins = (0..strikes).map(|i| { + CGPoint { + x: draw_origin.x + i as f64 * pixel_step, + y: draw_origin.y, + } + }).collect::>(); + ct_font.draw_glyphs(&glyphs, &origins, cg_context.clone()); + } else { + ct_font.draw_glyphs(&[glyph], &[draw_origin], cg_context.clone()); + } } - let mut rasterized_pixels = cg_context.data().to_vec(); + let mut rasterized_pixels = self.graphics_context + .get_rasterized_pixels(&raster_size, glyph_type); - if !bitmap { + if glyph_type == GlyphType::Vector { // We rendered text into an opaque surface. The code below needs to // ignore the current value of each pixel's alpha channel. But it's // allowed to write to the alpha channel, because we're done calling @@ -722,8 +724,14 @@ impl FontContext { top: metrics.rasterized_ascent as f32, width: metrics.rasterized_width, height: metrics.rasterized_height, - scale: (if bitmap { scale / y_scale } else { scale }) as f32, - format: if bitmap { GlyphFormat::ColorBitmap } else { font.get_glyph_format() }, + scale: match glyph_type { + GlyphType::Bitmap => (scale / y_scale) as f32, + GlyphType::Vector => scale as f32, + }, + format: match glyph_type { + GlyphType::Bitmap => GlyphFormat::ColorBitmap, + GlyphType::Vector => font.get_glyph_format(), + }, bytes: rasterized_pixels, }) } @@ -735,3 +743,109 @@ impl<'a> Into for NativeFontHandleWrapper<'a> { (self.0).0.clone() } } + +// Avoids taking locks by recycling Core Graphics contexts. +#[allow(dead_code)] +struct GraphicsContext { + vector_context: CGContext, + vector_context_size: Size2D, + bitmap_context: CGContext, + bitmap_context_size: Size2D, +} + +impl GraphicsContext { + fn new() -> GraphicsContext { + let size = Size2D::new(INITIAL_CG_CONTEXT_SIDE_LENGTH, INITIAL_CG_CONTEXT_SIDE_LENGTH); + GraphicsContext { + vector_context: GraphicsContext::create_cg_context(&size, GlyphType::Vector), + vector_context_size: size, + bitmap_context: GraphicsContext::create_cg_context(&size, GlyphType::Bitmap), + bitmap_context_size: size, + } + } + + #[allow(dead_code)] + fn get_context(&mut self, size: &Size2D, glyph_type: GlyphType) + -> &mut CGContext { + let (cached_context, cached_size) = match glyph_type { + GlyphType::Vector => { + (&mut self.vector_context, &mut self.vector_context_size) + } + GlyphType::Bitmap => { + (&mut self.bitmap_context, &mut self.bitmap_context_size) + } + }; + let rounded_size = Size2D::new(size.width.next_power_of_two(), + size.height.next_power_of_two()); + if rounded_size.width > cached_size.width || rounded_size.height > cached_size.height { + *cached_size = Size2D::new(u32::max(cached_size.width, rounded_size.width), + u32::max(cached_size.height, rounded_size.height)); + *cached_context = GraphicsContext::create_cg_context(cached_size, glyph_type); + } + cached_context + } + + #[allow(dead_code)] + fn get_rasterized_pixels(&mut self, size: &Size2D, glyph_type: GlyphType) + -> Vec { + let (cached_context, cached_size) = match glyph_type { + GlyphType::Vector => (&mut self.vector_context, &self.vector_context_size), + GlyphType::Bitmap => (&mut self.bitmap_context, &self.bitmap_context_size), + }; + let cached_data = cached_context.data(); + let cached_stride = cached_size.width as usize * 4; + + let result_len = size.width as usize * size.height as usize * 4; + let mut result = Vec::with_capacity(result_len); + for y in (cached_size.height - size.height)..cached_size.height { + let cached_start = y as usize * cached_stride; + let cached_end = cached_start + size.width as usize * 4; + result.extend_from_slice(&cached_data[cached_start..cached_end]); + } + debug_assert_eq!(result.len(), result_len); + result + } + + fn create_cg_context(size: &Size2D, glyph_type: GlyphType) -> CGContext { + // The result of rasterization, in all render modes, is going to be a + // BGRA surface with white text on transparency using premultiplied + // alpha. For subpixel text, the RGB values will be the mask value for + // the individual components. For bitmap glyphs, the RGB values will be + // the (premultiplied) color of the pixel. For Alpha and Mono, each + // pixel will have R==G==B==A at the end of this function. + // We access the color channels in little-endian order. + // The CGContext will create and own our pixel buffer. + // In the non-Bitmap cases, we will ask CoreGraphics to draw text onto + // an opaque background. In order to hit the most efficient path in CG + // for this, we will tell CG that the CGContext is opaque, by passing + // an "[...]AlphaNone[...]" context flag. This creates a slight + // contradiction to the way we use the buffer after CG is done with it, + // because we will convert it into text-on-transparency. But that's ok; + // we still get four bytes per pixel and CG won't mess with the alpha + // channel after we've stopped calling CG functions. We just need to + // make sure that we don't look at the alpha values of the pixels that + // we get from CG, and compute our own alpha value only from RGB. + // Note that CG requires kCGBitmapByteOrder32Little in order to do + // subpixel AA at all (which we need it to do in both Subpixel and + // Alpha+smoothing mode). But little-endian is what we want anyway, so + // this works out nicely. + let color_type = match glyph_type { + GlyphType::Vector => kCGImageAlphaNoneSkipFirst, + GlyphType::Bitmap => kCGImageAlphaPremultipliedFirst, + }; + + CGContext::create_bitmap_context(None, + size.width as usize, + size.height as usize, + 8, + size.width as usize * 4, + &CGColorSpace::create_device_rgb(), + kCGBitmapByteOrder32Little | color_type) + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +enum GlyphType { + Vector, + Bitmap, +} diff --git a/webrender_api/Cargo.toml b/webrender_api/Cargo.toml index 5410fefbd7..df64db641c 100644 --- a/webrender_api/Cargo.toml +++ b/webrender_api/Cargo.toml @@ -26,7 +26,7 @@ time = "0.1" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.6" -core-graphics = "0.16" +core-graphics = "0.17.1" [target.'cfg(target_os = "windows")'.dependencies] dwrote = "0.4.1" diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml index 789884222d..51cb58b8ad 100644 --- a/wrench/Cargo.toml +++ b/wrench/Cargo.toml @@ -31,7 +31,7 @@ winit = "0.16" serde = {version = "1.0", features = ["derive"] } [target.'cfg(target_os = "macos")'.dependencies] -core-graphics = "0.16" +core-graphics = "0.17.1" core-foundation = "0.6" [features]