From e11297a240b29bd57154d37ecb402d0b9aa299e6 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Thu, 5 Oct 2017 14:26:28 -0400 Subject: [PATCH] add more per-platform font options to FontInstancePlatformOptions --- Cargo.lock | 8 +- webrender/Cargo.toml | 2 +- webrender/res/cs_text_run.glsl | 2 +- webrender/res/prim_shared.glsl | 3 +- webrender/res/ps_text_run.glsl | 11 +- webrender/src/frame_builder.rs | 45 +-- webrender/src/glyph_cache.rs | 1 + webrender/src/glyph_rasterizer.rs | 38 +- webrender/src/platform/macos/font.rs | 15 +- webrender/src/platform/unix/font.rs | 477 ++++++++++++++++++------- webrender/src/platform/windows/font.rs | 43 ++- webrender/src/prim_store.rs | 40 ++- webrender/src/renderer.rs | 6 +- webrender/src/resource_cache.rs | 47 ++- webrender/src/texture_cache.rs | 14 +- webrender/src/tiling.rs | 14 +- webrender_api/Cargo.toml | 2 +- webrender_api/src/font.rs | 114 +++++- wrench/src/wrench.rs | 3 +- 19 files changed, 596 insertions(+), 289 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4bf395d629..60fbe42604 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "servo-glutin 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.52.0", + "webrender 0.52.1", "yaml-rust 0.3.4 (git+https://github.com/vvuk/yaml-rust)", ] @@ -1037,7 +1037,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "webrender" -version = "0.52.0" +version = "0.52.1" dependencies = [ "angle 0.5.0 (git+https://github.com/servo/angle?branch=servo)", "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1066,13 +1066,13 @@ dependencies = [ "servo-glutin 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_api 0.52.0", + "webrender_api 0.52.1", "ws 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "webrender_api" -version = "0.52.0" +version = "0.52.1" dependencies = [ "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index ad297b50f6..3f58783105 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender" -version = "0.52.0" +version = "0.52.1" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" diff --git a/webrender/res/cs_text_run.glsl b/webrender/res/cs_text_run.glsl index 9eaf6e0b0a..772f8510f8 100644 --- a/webrender/res/cs_text_run.glsl +++ b/webrender/res/cs_text_run.glsl @@ -38,7 +38,7 @@ void main(void) { // Glyphs size is already in device-pixels. // The render task origin is in device-pixels. Offset that by // the glyph offset, relative to its primitive bounding rect. - vec2 size = res.uv_rect.zw - res.uv_rect.xy; + vec2 size = (res.uv_rect.zw - res.uv_rect.xy) * res.scale; vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio; vec2 origin = prim.task.render_target_origin + uDevicePixelRatio * (local_pos + shadow.offset - shadow_geom.local_rect.p0); diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index f568f224ae..9156da91dc 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -627,11 +627,12 @@ struct GlyphResource { vec4 uv_rect; float layer; vec2 offset; + float scale; }; GlyphResource fetch_glyph_resource(int address) { vec4 data[2] = fetch_from_resource_cache_2(address); - return GlyphResource(data[0], data[1].x, data[1].yz); + return GlyphResource(data[0], data[1].x, data[1].yz, data[1].w); } struct ImageResource { diff --git a/webrender/res/ps_text_run.glsl b/webrender/res/ps_text_run.glsl index 971d601d1b..92f5cdcafb 100644 --- a/webrender/res/ps_text_run.glsl +++ b/webrender/res/ps_text_run.glsl @@ -30,7 +30,7 @@ void main(void) { vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio; RectWithSize local_rect = RectWithSize(local_pos, - (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio); + (res.uv_rect.zw - res.uv_rect.xy) * res.scale / uDevicePixelRatio); #ifdef WR_FEATURE_TRANSFORM TransformVertexInfo vi = write_transform_vertex(local_rect, @@ -57,7 +57,7 @@ void main(void) { vec2 st0 = res.uv_rect.xy / texture_size; vec2 st1 = res.uv_rect.zw / texture_size; - vColor = text.color; + vColor = vec4(text.color.rgb * text.color.a, text.color.a); vUv = vec3(mix(st0, st1, f), res.layer); vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; } @@ -71,13 +71,14 @@ void main(void) { oFragColor = texture(sColor0, tc); #else vec4 color = texture(sColor0, tc) * vColor; + float alpha = 1.0; #ifdef WR_FEATURE_TRANSFORM float a = 0.0; init_transform_fs(vLocalPos, a); - color.a *= a; + alpha *= a; #endif - color.a = min(color.a, do_clip()); - oFragColor = color; + alpha = min(alpha, do_clip()); + oFragColor = color * alpha; #endif } #endif diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 0efd819029..b9a30de858 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -10,7 +10,7 @@ use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect}; use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation}; use api::{LineStyle, LocalClip, POINT_RELATIVE_TO_PIPELINE_VIEWPORT, PipelineId, RepeatMode}; -use api::{ScrollSensitivity, SubpixelDirection, Shadow, TileOffset, TransformStyle}; +use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle}; use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length}; use app_units::Au; use border::ImageBorderSegment; @@ -1132,19 +1132,18 @@ impl FrameBuilder { // TODO(gw): Use a proper algorithm to select // whether this item should be rendered with // subpixel AA! - let mut default_render_mode = self.config + let mut render_mode = self.config .default_font_render_mode .limit_by(font.render_mode); if let Some(options) = glyph_options { - default_render_mode = default_render_mode.limit_by(options.render_mode); + render_mode = render_mode.limit_by(options.render_mode); } // There are some conditions under which we can't use // subpixel text rendering, even if enabled. - let mut normal_render_mode = default_render_mode; - if normal_render_mode == FontRenderMode::Subpixel { + if render_mode == FontRenderMode::Subpixel { if color.a != 1.0 { - normal_render_mode = FontRenderMode::Alpha; + render_mode = FontRenderMode::Alpha; } // text on a stacking context that has filters @@ -1155,36 +1154,17 @@ impl FrameBuilder { if let Some(sc_index) = self.stacking_context_stack.last() { let stacking_context = &self.stacking_context_store[sc_index.0]; if stacking_context.composite_ops.count() > 0 { - normal_render_mode = FontRenderMode::Alpha; + render_mode = FontRenderMode::Alpha; } } } - let color = match font.render_mode { - FontRenderMode::Bitmap => ColorF::new(1.0, 1.0, 1.0, 1.0), - FontRenderMode::Subpixel | - FontRenderMode::Alpha | - FontRenderMode::Mono => *color, - }; - - // Shadows never use subpixel AA, but need to respect the alpha/mono flag - // for reftests. - let (shadow_render_mode, subpx_dir) = match default_render_mode { - FontRenderMode::Subpixel | FontRenderMode::Alpha => { - // TODO(gw): Expose subpixel direction in API once WR supports - // vertical text runs. - (FontRenderMode::Alpha, font.subpx_dir) - } - FontRenderMode::Mono => (FontRenderMode::Mono, SubpixelDirection::None), - FontRenderMode::Bitmap => (FontRenderMode::Bitmap, font.subpx_dir), - }; - let prim_font = FontInstance::new( font.font_key, font.size, - color, - normal_render_mode, - subpx_dir, + *color, + render_mode, + font.subpx_dir, font.platform_options, font.variations.clone(), font.synthetic_italics, @@ -1195,9 +1175,7 @@ impl FrameBuilder { glyph_count, glyph_gpu_blocks: Vec::new(), glyph_keys: Vec::new(), - shadow_render_mode, offset: run_offset, - color: color, }; // Text shadows that have a blur radius of 0 need to be rendered as normal @@ -1215,16 +1193,13 @@ impl FrameBuilder { let shadow = picture_prim.as_shadow(); if shadow.blur_radius == 0.0 { let mut text_prim = prim.clone(); - if font.render_mode != FontRenderMode::Bitmap { - text_prim.font.color = shadow.color.into(); - } + text_prim.font.color = shadow.color.into(); // If we have translucent text, we need to ensure it won't go // through the subpixel blend mode, which doesn't work with // traditional alpha blending. if shadow.color.a != 1.0 { text_prim.font.render_mode = text_prim.font.render_mode.limit_by(FontRenderMode::Alpha); } - text_prim.color = shadow.color; text_prim.offset += shadow.offset; fast_shadow_prims.push(text_prim); } diff --git a/webrender/src/glyph_cache.rs b/webrender/src/glyph_cache.rs index c06ea005cb..b5d91ffd59 100644 --- a/webrender/src/glyph_cache.rs +++ b/webrender/src/glyph_cache.rs @@ -13,6 +13,7 @@ pub struct CachedGlyphInfo { pub glyph_bytes: Arc>, pub size: DeviceUintSize, pub offset: DevicePoint, + pub scale: f32, } pub type GlyphKeyCache = ResourceClassCache>; diff --git a/webrender/src/glyph_rasterizer.rs b/webrender/src/glyph_rasterizer.rs index 31b097da9c..4c76cf8af7 100644 --- a/webrender/src/glyph_rasterizer.rs +++ b/webrender/src/glyph_rasterizer.rs @@ -3,10 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #[cfg(test)] -use api::{ColorF, FontRenderMode, IdNamespace, LayoutPoint, SubpixelDirection}; +use api::{ColorF, IdNamespace, LayoutPoint}; use api::{DevicePoint, DeviceUintSize, FontInstance}; -use api::{FontKey, FontTemplate}; -use api::{GlyphDimensions, GlyphKey}; +use api::{FontKey, FontTemplate, FontRenderMode, ColorU}; +use api::{GlyphDimensions, GlyphKey, SubpixelDirection}; use api::{ImageData, ImageDescriptor, ImageFormat}; #[cfg(test)] use app_units::Au; @@ -144,6 +144,29 @@ impl GlyphRasterizer { self.fonts_to_remove.push(font_key); } + pub fn prepare_font(&self, font: &mut FontInstance) { + // In alpha/mono mode, the color of the font is irrelevant. + // Forcing it to black in those cases saves rasterizing glyphs + // of different colors when not needed. + match font.render_mode { + FontRenderMode::Mono | FontRenderMode::Bitmap => { + font.color = ColorU::new(255, 255, 255, 255); + // Subpixel positioning is disabled in mono and bitmap modes. + font.subpx_dir = SubpixelDirection::None; + } + FontRenderMode::Alpha => { + font.color = ColorU::new(255, 255, 255, 255); + } + FontRenderMode::Subpixel => { + // In subpixel mode, we only actually need the color if preblending + // is used in the font backend. + if !FontContext::has_gamma_correct_subpixel_aa() { + font.color = ColorU::new(255, 255, 255, 255); + } + } + } + } + pub fn request_glyphs( &mut self, glyph_cache: &mut GlyphCache, @@ -183,7 +206,7 @@ impl GlyphRasterizer { }, TextureFilter::Linear, ImageData::Raw(glyph_info.glyph_bytes.clone()), - [glyph_info.offset.x, glyph_info.offset.y], + [glyph_info.offset.x, glyph_info.offset.y, glyph_info.scale], None, gpu_cache, ); @@ -246,10 +269,10 @@ impl GlyphRasterizer { .get_glyph_dimensions(font, glyph_key) } - pub fn is_bitmap_font(&self, font_key: FontKey) -> bool { + pub fn is_bitmap_font(&self, font: &FontInstance) -> bool { self.font_contexts .lock_shared_context() - .is_bitmap_font(font_key) + .is_bitmap_font(font) } pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option { @@ -312,7 +335,7 @@ impl GlyphRasterizer { }, TextureFilter::Linear, ImageData::Raw(glyph_bytes.clone()), - [glyph.left, glyph.top], + [glyph.left, glyph.top, glyph.scale], None, gpu_cache, ); @@ -321,6 +344,7 @@ impl GlyphRasterizer { glyph_bytes, size: DeviceUintSize::new(glyph.width, glyph.height), offset: DevicePoint::new(glyph.left, glyph.top), + scale: glyph.scale, }) } else { None diff --git a/webrender/src/platform/macos/font.rs b/webrender/src/platform/macos/font.rs index bcd3a2cc25..4053d77137 100644 --- a/webrender/src/platform/macos/font.rs +++ b/webrender/src/platform/macos/font.rs @@ -42,6 +42,7 @@ pub struct RasterizedGlyph { pub left: f32, pub width: u32, pub height: u32, + pub scale: f32, pub bytes: Vec, } @@ -52,6 +53,7 @@ impl RasterizedGlyph { left: 0.0, width: 0, height: 0, + scale: 1.0, bytes: vec![], } } @@ -422,18 +424,20 @@ impl FontContext { } } - pub fn is_bitmap_font(&mut self, font_key: FontKey) -> bool { - match self.get_ct_font(font_key, Au(16 * 60), &[]) { + pub fn is_bitmap_font(&mut self, font: &FontInstance) -> bool { + match self.get_ct_font(font.font_key, font.size, &font.variations) { Some(ref ct_font) => { let traits = ct_font.symbolic_traits(); (traits & kCTFontColorGlyphsTrait) != 0 } - None => { - false - } + None => false, } } + pub fn has_gamma_correct_subpixel_aa() -> bool { + true + } + pub fn rasterize_glyph( &mut self, font: &FontInstance, @@ -585,6 +589,7 @@ impl FontContext { top: metrics.rasterized_ascent as f32, width: metrics.rasterized_width, height: metrics.rasterized_height, + scale: 1.0, bytes: rasterized_pixels, }) } diff --git a/webrender/src/platform/unix/font.rs b/webrender/src/platform/unix/font.rs index e9173597be..730a21a51b 100644 --- a/webrender/src/platform/unix/font.rs +++ b/webrender/src/platform/unix/font.rs @@ -3,29 +3,33 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{FontInstance, FontKey, FontRenderMode, GlyphDimensions}; -use api::{NativeFontHandle, SubpixelDirection}; -use api::GlyphKey; +use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting}; +use api::{NativeFontHandle, SubpixelDirection, GlyphKey}; +use api::{FONT_FORCE_AUTOHINT, FONT_NO_AUTOHINT, FONT_EMBEDDED_BITMAP}; +use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR}; use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode}; use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32}; use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos}; use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt}; -use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Memory_Face, FT_Outline_Transform}; +use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Memory_Face}; use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph}; -use freetype::freetype::{FT_Library, FT_Matrix, FT_Outline_Get_CBox, FT_Set_Char_Size}; +use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size}; +use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT}; +use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT}; +use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT}; +use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES, FT_Err_Cannot_Render_Glyph}; use internal_types::FastHashMap; -use std::{mem, ptr, slice}; +use std::{cmp, mem, ptr, slice}; use std::sync::Arc; -// This constant is not present in the freetype +// These constants are not present in the freetype // bindings due to bindgen not handling the way -// the macro is defined. -const FT_LOAD_TARGET_LIGHT: FT_Int32 = 1 << 16; - -// Default to slight hinting, which is what most -// Linux distros use by default, and is a better -// default than no hinting. -// TODO(gw): Make this configurable. -const GLYPH_LOAD_FLAGS: FT_Int32 = FT_LOAD_TARGET_LIGHT; +// the macros are defined. +//const FT_LOAD_TARGET_NORMAL: FT_UInt = 0 << 16; +const FT_LOAD_TARGET_LIGHT: FT_UInt = 1 << 16; +const FT_LOAD_TARGET_MONO: FT_UInt = 2 << 16; +const FT_LOAD_TARGET_LCD: FT_UInt = 3 << 16; +const FT_LOAD_TARGET_LCD_V: FT_UInt = 4 << 16; struct Face { face: FT_Face, @@ -50,17 +54,25 @@ pub struct RasterizedGlyph { pub left: f32, pub width: u32, pub height: u32, + pub scale: f32, pub bytes: Vec, } -const SUCCESS: FT_Error = FT_Error(0); +extern "C" { + fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot); + fn FT_GlyphSlot_Oblique(slot: FT_GlyphSlot); +} impl FontContext { pub fn new() -> FontContext { let mut lib: FT_Library = ptr::null_mut(); - // Per Skia, using a filter adds one full pixel to each side. - let mut lcd_extra_pixels = 1; + // Using an LCD filter may add one full pixel to each side if support is built in. + // As of FreeType 2.8.1, an LCD filter is always used regardless of settings + // if support for the patent-encumbered LCD filter algorithms is not built in. + // Thus, the only reasonable way to guess padding is to unconditonally add it if + // subpixel AA is used. + let lcd_extra_pixels = 1; unsafe { let result = FT_Init_FreeType(&mut lib); @@ -69,22 +81,12 @@ impl FontContext { "Unable to initialize FreeType library {:?}", result ); - - // TODO(gw): Check result of this to determine if freetype build supports subpixel. - let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT); - - if !result.succeeded() { - println!( - "WARN: Initializing a FreeType library build without subpixel AA enabled!" - ); - lcd_extra_pixels = 0; - } } FontContext { lib, faces: FastHashMap::default(), - lcd_extra_pixels: lcd_extra_pixels, + lcd_extra_pixels, } } @@ -132,25 +134,71 @@ impl FontContext { fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option { debug_assert!(self.faces.contains_key(&font.font_key)); let face = self.faces.get(&font.font_key).unwrap(); - let char_size = font.size.to_f64_px() * 64.0 + 0.5; - assert_eq!(SUCCESS, unsafe { - FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0) - }); + let mut load_flags = FT_LOAD_DEFAULT; + let FontInstancePlatformOptions { flags, hinting, .. } = font.platform_options.unwrap_or_default(); + match (hinting, font.render_mode) { + (FontHinting::None, _) => load_flags |= FT_LOAD_NO_HINTING, + (FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO, + (FontHinting::Light, _) => load_flags = FT_LOAD_TARGET_LIGHT, + (FontHinting::LCD, FontRenderMode::Subpixel) => { + load_flags = match font.subpx_dir { + SubpixelDirection::Vertical => FT_LOAD_TARGET_LCD_V, + _ => FT_LOAD_TARGET_LCD, + }; + if (flags & FONT_FORCE_AUTOHINT) != 0 { + load_flags |= FT_LOAD_FORCE_AUTOHINT; + } + } + _ => { + if (flags & FONT_FORCE_AUTOHINT) != 0 { + load_flags |= FT_LOAD_FORCE_AUTOHINT; + } + } + } + + if (flags & FONT_NO_AUTOHINT) != 0 { + load_flags |= FT_LOAD_NO_AUTOHINT; + } + if (flags & FONT_EMBEDDED_BITMAP) == 0 { + load_flags |= FT_LOAD_NO_BITMAP; + } + if (flags & FONT_VERTICAL_LAYOUT) != 0 { + load_flags |= FT_LOAD_VERTICAL_LAYOUT; + } + + load_flags |= FT_LOAD_COLOR; + load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + + let mut result = if font.render_mode == FontRenderMode::Bitmap { + if (load_flags & FT_LOAD_NO_BITMAP) != 0 { + FT_Error(FT_Err_Cannot_Render_Glyph as i32) + } else { + self.choose_bitmap_size(face.face, font.size.to_f64_px()) + } + } else { + let char_size = font.size.to_f64_px() * 64.0 + 0.5; + unsafe { FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0) } + }; - let result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, GLYPH_LOAD_FLAGS) }; + if result.succeeded() { + result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, load_flags as FT_Int32) }; + }; - if result == SUCCESS { + if result.succeeded() { let slot = unsafe { (*face.face).glyph }; assert!(slot != ptr::null_mut()); - // TODO(gw): We use the FT_Outline_* APIs to manage sub-pixel offsets. - // We will need a custom code path for bitmap fonts (which - // are very rare). - match unsafe { (*slot).format } { - FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => Some(slot), + if (flags & FONT_EMBOLDEN) != 0 { + unsafe { FT_GlyphSlot_Embolden(slot) }; + } + + let format = unsafe { (*slot).format }; + match format { + FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE | + FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => Some(slot), _ => { - error!("TODO: Support bitmap fonts!"); + error!("Unsupported {:?}", format); None } } @@ -178,6 +226,11 @@ impl FontContext { // Get the estimated bounding box from FT (control points). unsafe { FT_Outline_Get_CBox(&(*slot).outline, &mut cbox); + + // For spaces and other non-printable characters, early out. + if (*slot).outline.n_contours == 0 { + return cbox; + } } // Convert the subpixel offset to floats. @@ -185,13 +238,11 @@ impl FontContext { // Apply extra pixel of padding for subpixel AA, due to the filter. let padding = match font.render_mode { - FontRenderMode::Subpixel => self.lcd_extra_pixels * 64, + FontRenderMode::Subpixel => (self.lcd_extra_pixels * 64) as FT_Pos, FontRenderMode::Alpha | FontRenderMode::Mono | - FontRenderMode::Bitmap => 0, + FontRenderMode::Bitmap => 0 as FT_Pos, }; - cbox.xMin -= padding as FT_Pos; - cbox.xMax += padding as FT_Pos; // Offset the bounding box by subpixel positioning. // Convert to 26.6 fixed point format for FT. @@ -199,13 +250,13 @@ impl FontContext { SubpixelDirection::None => {} SubpixelDirection::Horizontal => { let dx = (dx * 64.0 + 0.5) as FT_Long; - cbox.xMin += dx; - cbox.xMax += dx; + cbox.xMin += dx - padding; + cbox.xMax += dx + padding; } SubpixelDirection::Vertical => { let dy = (dy * 64.0 + 0.5) as FT_Long; - cbox.yMin += dy; - cbox.yMax += dy; + cbox.yMin += dy - padding; + cbox.yMax += dy + padding; } } @@ -223,23 +274,58 @@ impl FontContext { slot: FT_GlyphSlot, font: &FontInstance, glyph: &GlyphKey, + scale_bitmaps: bool, ) -> Option { let metrics = unsafe { &(*slot).metrics }; // If there's no advance, no need to consider this glyph // for layout. if metrics.horiAdvance == 0 { - None - } else { - let cbox = self.get_bounding_box(slot, font, glyph); - - Some(GlyphDimensions { - left: (cbox.xMin >> 6) as i32, - top: (cbox.yMax >> 6) as i32, - width: ((cbox.xMax - cbox.xMin) >> 6) as u32, - height: ((cbox.yMax - cbox.yMin) >> 6) as u32, - advance: metrics.horiAdvance as f32 / 64.0, - }) + return None + } + + let advance = metrics.horiAdvance as f32 / 64.0; + match unsafe { (*slot).format } { + FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => { + let left = unsafe { (*slot).bitmap_left }; + let top = unsafe { (*slot).bitmap_top }; + let width = unsafe { (*slot).bitmap.width }; + let height = unsafe { (*slot).bitmap.rows }; + if scale_bitmaps { + let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem }; + let scale = font.size.to_f32_px() / y_size as f32; + let x0 = left as f32 * scale; + let x1 = width as f32 * scale + x0; + let y1 = top as f32 * scale; + let y0 = y1 - height as f32 * scale; + Some(GlyphDimensions { + left: x0.round() as i32, + top: y1.round() as i32, + width: (x1.ceil() - x0.floor()) as u32, + height: (y1.ceil() - y0.floor()) as u32, + advance: advance * scale, + }) + } else { + Some(GlyphDimensions { + left, + top, + width, + height, + advance, + }) + } + } + FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => { + let cbox = self.get_bounding_box(slot, font, glyph); + Some(GlyphDimensions { + left: (cbox.xMin >> 6) as i32, + top: (cbox.yMax >> 6) as i32, + width: ((cbox.xMax - cbox.xMin) >> 6) as u32, + height: ((cbox.yMax - cbox.yMin) >> 6) as u32, + advance, + }) + } + _ => None, } } @@ -261,42 +347,54 @@ impl FontContext { key: &GlyphKey, ) -> Option { let slot = self.load_glyph(font, key); - slot.and_then(|slot| self.get_glyph_dimensions_impl(slot, font, key)) + slot.and_then(|slot| self.get_glyph_dimensions_impl(slot, font, key, true)) + } + + pub fn is_bitmap_font(&mut self, font: &FontInstance) -> bool { + debug_assert!(self.faces.contains_key(&font.font_key)); + let face = self.faces.get(&font.font_key).unwrap(); + let face_flags = unsafe { (*face.face).face_flags }; + // If the face has embedded bitmaps, they should only be used if either + // embedded bitmaps are explicitly requested or if the face has no outline. + if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 { + let FontInstancePlatformOptions { flags, .. } = font.platform_options.unwrap_or_default(); + if (flags & FONT_EMBEDDED_BITMAP) != 0 { + return true; + } + (face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0 + } else { + false + } } - pub fn is_bitmap_font(&mut self, _font_key: FontKey) -> bool { - // TODO(gw): Support bitmap fonts in Freetype. + fn choose_bitmap_size(&self, face: FT_Face, requested_size: f64) -> FT_Error { + let mut best_dist = unsafe { *(*face).available_sizes.offset(0) }.y_ppem as f64 / 64.0 - requested_size; + let mut best_size = 0; + let num_fixed_sizes = unsafe { (*face).num_fixed_sizes }; + for i in 1 .. num_fixed_sizes { + // Distance is positive if strike is larger than desired size, + // or negative if smaller. If previously a found smaller strike, + // then prefer a larger strike. Otherwise, minimize distance. + let dist = unsafe { *(*face).available_sizes.offset(i as isize) }.y_ppem as f64 / 64.0 - requested_size; + if (best_dist < 0.0 && dist >= best_dist) || dist.abs() <= best_dist { + best_dist = dist; + best_size = i; + } + } + unsafe { FT_Select_Size(face, best_size) } + } + + pub fn has_gamma_correct_subpixel_aa() -> bool { + // We don't do any preblending with FreeType currently, so the color is not used. false } - pub fn rasterize_glyph( + fn rasterize_glyph_outline( &mut self, + slot: FT_GlyphSlot, font: &FontInstance, key: &GlyphKey, - ) -> Option { - let slot = match self.load_glyph(font, key) { - Some(slot) => slot, - None => return None, - }; - - let render_mode = match font.render_mode { - FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO, - FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL, - FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD, - FontRenderMode::Bitmap => FT_Render_Mode::FT_RENDER_MODE_NORMAL, - }; - - // Get dimensions of the glyph, to see if we need to rasterize it. - let dimensions = match self.get_glyph_dimensions_impl(slot, font, key) { - Some(val) => val, - None => return None, - }; - - // For spaces and other non-printable characters, early out. - if dimensions.width == 0 || dimensions.height == 0 { - return None; - } - + ) -> bool { // Get the subpixel offsets in FT 26.6 format. let (dx, dy) = font.get_subpx_offset(key); let dx = (dx * 64.0 + 0.5) as FT_Long; @@ -315,93 +413,204 @@ impl FontContext { ); if font.synthetic_italics { - // These magic numbers are pre-encoded fixed point - // values that apply ~12 degree shear. Borrowed - // from the Freetype implementation of the - // FT_GlyphSlot_Oblique function. - let transform = FT_Matrix { - xx: 0x10000, - yx: 0x00000, - xy: 0x0366A, - yy: 0x10000, - }; - FT_Outline_Transform(outline, &transform); + FT_GlyphSlot_Oblique(slot); } } + if font.render_mode == FontRenderMode::Subpixel { + let FontInstancePlatformOptions { lcd_filter, .. } = font.platform_options.unwrap_or_default(); + let filter = match lcd_filter { + FontLCDFilter::None => FT_LcdFilter::FT_LCD_FILTER_NONE, + FontLCDFilter::Default => FT_LcdFilter::FT_LCD_FILTER_DEFAULT, + FontLCDFilter::Light => FT_LcdFilter::FT_LCD_FILTER_LIGHT, + FontLCDFilter::Legacy => FT_LcdFilter::FT_LCD_FILTER_LEGACY, + }; + unsafe { FT_Library_SetLcdFilter(self.lib, filter) }; + } + let render_mode = match (font.render_mode, font.subpx_dir) { + (FontRenderMode::Mono, _) => FT_Render_Mode::FT_RENDER_MODE_MONO, + (FontRenderMode::Alpha, _) | (FontRenderMode::Bitmap, _) => FT_Render_Mode::FT_RENDER_MODE_NORMAL, + (FontRenderMode::Subpixel, SubpixelDirection::Vertical) => FT_Render_Mode::FT_RENDER_MODE_LCD_V, + (FontRenderMode::Subpixel, _) => FT_Render_Mode::FT_RENDER_MODE_LCD, + }; let result = unsafe { FT_Render_Glyph(slot, render_mode) }; - if result != SUCCESS { + if !result.succeeded() { error!( "Unable to rasterize {:?} with {:?}, {:?}", key, render_mode, result ); + false + } else { + true + } + } + + pub fn rasterize_glyph( + &mut self, + font: &FontInstance, + key: &GlyphKey, + ) -> Option { + let slot = match self.load_glyph(font, key) { + Some(slot) => slot, + None => return None, + }; + + // Get dimensions of the glyph, to see if we need to rasterize it. + let dimensions = match self.get_glyph_dimensions_impl(slot, font, key, false) { + Some(val) => val, + None => return None, + }; + + // For spaces and other non-printable characters, early out. + if dimensions.width == 0 || dimensions.height == 0 { return None; } + let format = unsafe { (*slot).format }; + let mut scale = 1.0; + match format { + FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => { + let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem }; + scale = font.size.to_f32_px() / y_size as f32; + } + FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => { + if !self.rasterize_glyph_outline(slot, font, key) { + return None; + } + } + _ => { + error!("Unsupported {:?}", format); + return None; + } + } + let bitmap = unsafe { &(*slot).bitmap }; let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) }; info!( "Rasterizing {:?} as {:?} with dimensions {:?}", key, - render_mode, + font.render_mode, dimensions ); - let actual_width = match pixel_mode { + let (actual_width, actual_height) = match pixel_mode { FT_Pixel_Mode::FT_PIXEL_MODE_LCD => { assert!(bitmap.width % 3 == 0); - bitmap.width / 3 + ((bitmap.width / 3) as i32, bitmap.rows as i32) } - FT_Pixel_Mode::FT_PIXEL_MODE_MONO | FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => bitmap.width, - _ => { - panic!("Unexpected pixel mode!"); + FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => { + assert!(bitmap.rows % 3 == 0); + (bitmap.width as i32, (bitmap.rows / 3) as i32) } - } as i32; - - let actual_height = bitmap.rows as i32; - let top = unsafe { (*slot).bitmap_top }; - let left = unsafe { (*slot).bitmap_left }; + FT_Pixel_Mode::FT_PIXEL_MODE_MONO | FT_Pixel_Mode::FT_PIXEL_MODE_GRAY | FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => { + (bitmap.width as i32, bitmap.rows as i32) + } + _ => panic!("Unsupported {:?}", pixel_mode), + }; + let (left, top) = unsafe { ((*slot).bitmap_left, (*slot).bitmap_top) }; let mut final_buffer = vec![0; (actual_width * actual_height * 4) as usize]; // Extract the final glyph from FT format into RGBA8 format, which is // what WR expects. - for y in 0 .. actual_height { - // Get pointer to the bytes for this row. - let mut src = unsafe { bitmap.buffer.offset((y * bitmap.pitch) as isize) }; - - for x in 0 .. actual_width { - let value = match pixel_mode { - FT_Pixel_Mode::FT_PIXEL_MODE_MONO => { - let mask = 0x80 >> (x & 0x7); - let byte = unsafe { *src.offset((x >> 3) as isize) }; - let alpha = if byte & mask != 0 { 0xff } else { 0 }; - [0xff, 0xff, 0xff, alpha] + let FontInstancePlatformOptions { flags, .. } = font.platform_options.unwrap_or_default(); + let subpixel_bgr = (flags & FONT_SUBPIXEL_BGR) != 0; + let mut src_row = bitmap.buffer; + let mut dest: usize = 0; + while dest < final_buffer.len() { + let mut src = src_row; + let row_end = dest + actual_width as usize * 4; + match pixel_mode { + FT_Pixel_Mode::FT_PIXEL_MODE_MONO => { + while dest < row_end { + // Cast the byte to signed so that we can left shift each bit into + // the top bit, then right shift to fill out the bits with 0s or 1s. + let mut byte: i8 = unsafe { *src as i8 }; + src = unsafe { src.offset(1) }; + let byte_end = cmp::min(row_end, dest + 8 * 4); + while dest < byte_end { + let alpha = (byte >> 7) as u8; + final_buffer[dest + 0] = alpha; + final_buffer[dest + 1] = alpha; + final_buffer[dest + 2] = alpha; + final_buffer[dest + 3] = alpha; + dest += 4; + byte <<= 1; + } } - FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => { + } + FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => { + while dest < row_end { let alpha = unsafe { *src }; + final_buffer[dest + 0] = alpha; + final_buffer[dest + 1] = alpha; + final_buffer[dest + 2] = alpha; + final_buffer[dest + 3] = alpha; src = unsafe { src.offset(1) }; - [0xff, 0xff, 0xff, alpha] + dest += 4; } - FT_Pixel_Mode::FT_PIXEL_MODE_LCD => { - let t = unsafe { slice::from_raw_parts(src, 3) }; - src = unsafe { src.offset(3) }; - [t[2], t[1], t[0], 0xff] + } + FT_Pixel_Mode::FT_PIXEL_MODE_LCD => { + if subpixel_bgr { + while dest < row_end { + final_buffer[dest + 0] = unsafe { *src }; + final_buffer[dest + 1] = unsafe { *src.offset(1) }; + final_buffer[dest + 2] = unsafe { *src.offset(2) }; + final_buffer[dest + 3] = 0xff; + src = unsafe { src.offset(3) }; + dest += 4; + } + } else { + while dest < row_end { + final_buffer[dest + 2] = unsafe { *src }; + final_buffer[dest + 1] = unsafe { *src.offset(1) }; + final_buffer[dest + 0] = unsafe { *src.offset(2) }; + final_buffer[dest + 3] = 0xff; + src = unsafe { src.offset(3) }; + dest += 4; + } } - _ => panic!("Unsupported {:?}", pixel_mode), - }; - let i = 4 * (y * actual_width + x) as usize; - let dest = &mut final_buffer[i .. i + 4]; - dest.clone_from_slice(&value); + } + FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => { + if subpixel_bgr { + while dest < row_end { + final_buffer[dest + 0] = unsafe { *src }; + final_buffer[dest + 1] = unsafe { *src.offset(bitmap.pitch as isize) }; + final_buffer[dest + 2] = unsafe { *src.offset((2 * bitmap.pitch) as isize) }; + final_buffer[dest + 3] = 0xff; + src = unsafe { src.offset(1) }; + dest += 4; + } + } else { + while dest < row_end { + final_buffer[dest + 2] = unsafe { *src }; + final_buffer[dest + 1] = unsafe { *src.offset(bitmap.pitch as isize) }; + final_buffer[dest + 0] = unsafe { *src.offset((2 * bitmap.pitch) as isize) }; + final_buffer[dest + 3] = 0xff; + src = unsafe { src.offset(1) }; + dest += 4; + } + } + src_row = unsafe { src_row.offset((2 * bitmap.pitch) as isize) }; + } + FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => { + // The source is premultiplied BGRA data. + let dest_slice = &mut final_buffer[dest .. row_end]; + let src_slice = unsafe { slice::from_raw_parts(src, dest_slice.len()) }; + dest_slice.copy_from_slice(src_slice); + } + _ => panic!("Unsupported {:?}", pixel_mode), } + src_row = unsafe { src_row.offset(bitmap.pitch as isize) }; } Some(RasterizedGlyph { - left: (dimensions.left + left) as f32, - top: (dimensions.top + top - actual_height) as f32, + left: ((dimensions.left + left) as f32 * scale).round(), + top: ((dimensions.top + top - actual_height) as f32 * scale).round(), width: actual_width as u32, height: actual_height as u32, + scale, bytes: final_buffer, }) } diff --git a/webrender/src/platform/windows/font.rs b/webrender/src/platform/windows/font.rs index 13287aeb12..d251106268 100644 --- a/webrender/src/platform/windows/font.rs +++ b/webrender/src/platform/windows/font.rs @@ -33,6 +33,7 @@ pub struct RasterizedGlyph { pub left: f32, pub width: u32, pub height: u32, + pub scale: f32, pub bytes: Vec, } @@ -304,11 +305,15 @@ impl FontContext { } } - pub fn is_bitmap_font(&mut self, _font_key: FontKey) -> bool { + pub fn is_bitmap_font(&mut self, _font: &FontInstance) -> bool { // TODO(gw): Support bitmap fonts in DWrite. false } + pub fn has_gamma_correct_subpixel_aa() -> bool { + true + } + pub fn rasterize_glyph( &mut self, font: &FontInstance, @@ -329,22 +334,25 @@ impl FontContext { let mut pixels = analysis.create_alpha_texture(texture_type, bounds); - if font.render_mode != FontRenderMode::Mono { - let lut_correction = match font.platform_options { - Some(option) => if option.force_gdi_rendering { - &self.gdi_gamma_lut - } else { - &self.gamma_lut - }, - None => &self.gamma_lut, - }; - - lut_correction.preblend_rgb( - &mut pixels, - width, - height, - ColorLut::new(font.color.r, font.color.g, font.color.b, font.color.a), - ); + match font.render_mode { + FontRenderMode::Mono | FontRenderMode::Bitmap => {} + FontRenderMode::Alpha | FontRenderMode::Subpixel => { + let lut_correction = match font.platform_options { + Some(option) => if option.force_gdi_rendering { + &self.gdi_gamma_lut + } else { + &self.gamma_lut + }, + None => &self.gamma_lut, + }; + + lut_correction.preblend_rgb( + &mut pixels, + width, + height, + ColorLut::new(font.color.r, font.color.g, font.color.b, font.color.a), + ); + } } let rgba_pixels = self.convert_to_rgba(&mut pixels, font.render_mode); @@ -354,6 +362,7 @@ impl FontContext { top: -bounds.top as f32, width: width as u32, height: height as u32, + scale: 1.0, bytes: rgba_pixels, }) } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 62b8a08af3..9d2b7c2064 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -523,8 +523,6 @@ pub struct TextRunPrimitiveCpu { pub glyph_count: usize, pub glyph_keys: Vec, pub glyph_gpu_blocks: Vec, - pub shadow_render_mode: FontRenderMode, - pub color: ColorF, } #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -534,6 +532,23 @@ pub enum TextRunMode { } impl TextRunPrimitiveCpu { + pub fn get_font(&self, + run_mode: TextRunMode, + device_pixel_ratio: f32, + ) -> FontInstance { + let mut font = self.font.clone(); + match run_mode { + TextRunMode::Normal => {} + TextRunMode::Shadow => { + // Shadows never use subpixel AA, but need to respect the alpha/mono flag + // for reftests. + font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha); + } + }; + font.size = font.size.scale_by(device_pixel_ratio); + font + } + fn prepare_for_render( &mut self, resource_cache: &mut ResourceCache, @@ -542,33 +557,22 @@ impl TextRunPrimitiveCpu { run_mode: TextRunMode, gpu_cache: &mut GpuCache, ) { - let mut font = self.font.clone(); - font.size = font.size.scale_by(device_pixel_ratio); - match run_mode { - TextRunMode::Shadow => { - font.render_mode = self.shadow_render_mode; - } - TextRunMode::Normal => {} - } - - if run_mode == TextRunMode::Shadow { - font.render_mode = self.shadow_render_mode; - } + let font = self.get_font(run_mode, device_pixel_ratio); // Cache the glyph positions, if not in the cache already. // TODO(gw): In the future, remove `glyph_instances` // completely, and just reference the glyphs // directly from the display list. if self.glyph_keys.is_empty() { + let subpx_dir = font.subpx_dir.limit_by(font.render_mode); let src_glyphs = display_list.get(self.glyph_range); // TODO(gw): If we support chunks() on AuxIter // in the future, this code below could // be much simpler... let mut gpu_block = GpuBlockData::empty(); - for (i, src) in src_glyphs.enumerate() { - let key = GlyphKey::new(src.index, src.point, font.render_mode, font.subpx_dir); + let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir); self.glyph_keys.push(key); // Two glyphs are packed per GPU block. @@ -594,11 +598,11 @@ impl TextRunPrimitiveCpu { } fn write_gpu_blocks(&self, request: &mut GpuDataRequest) { - request.push(self.color); + request.push(ColorF::from(self.font.color)); request.push([ self.offset.x, self.offset.y, - self.font.subpx_dir as u32 as f32, + self.font.subpx_dir.limit_by(self.font.render_mode) as u32 as f32, 0.0, ]); request.extend_from_slice(&self.glyph_gpu_blocks); diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 5f88b89d85..16041fb8fc 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -10,7 +10,7 @@ //! [renderer]: struct.Renderer.html use api::{channel, BlobImageRenderer, FontRenderMode}; -use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier}; +use api::{ColorF, ColorU, Epoch, PipelineId, RenderApiSender, RenderNotifier}; use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize}; use api::{ExternalImageId, ExternalImageType, ImageFormat}; use api::{YUV_COLOR_SPACES, YUV_FORMATS}; @@ -627,7 +627,7 @@ pub enum BlendMode { PremultipliedAlpha, // Use the color of the text itself as a constant color blend factor. - Subpixel(ColorF), + Subpixel(ColorU), } // Tracks the state of each row in the GPU cache texture. @@ -2747,7 +2747,7 @@ impl Renderer { } BlendMode::Subpixel(color) => { self.device.set_blend(true); - self.device.set_blend_mode_subpixel(color); + self.device.set_blend_mode_subpixel(color.into()); } } prev_blend_mode = batch.key.blend_mode; diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index aaca81c1c9..aae4ae4605 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -4,7 +4,7 @@ use api::{AddFont, BlobImageData, BlobImageResources, ResourceUpdate, ResourceUpdates}; use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest}; -use api::{ColorF, FontRenderMode, SubpixelDirection}; +use api::{ColorF, FontRenderMode}; use api::{DevicePoint, DeviceUintRect, DeviceUintSize}; use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate}; use api::{ExternalImageData, ExternalImageType}; @@ -337,29 +337,26 @@ impl ResourceCache { platform_options: Option, variations: Vec, ) { - let mut requested_render_mode = FontRenderMode::Subpixel; - let mut subpx_dir = SubpixelDirection::Horizontal; - if let Some(options) = options { - if let Some(render_mode) = options.render_mode { - requested_render_mode = render_mode; - } - } - if self.glyph_rasterizer.is_bitmap_font(font_key) { - requested_render_mode = requested_render_mode.limit_by(FontRenderMode::Bitmap); - } - if requested_render_mode == FontRenderMode::Mono { - subpx_dir = SubpixelDirection::None; - } - let instance = FontInstance::new( + let FontInstanceOptions { + render_mode, + subpx_dir, + synthetic_italics, + .. + } = options.unwrap_or_default(); + assert!(render_mode != FontRenderMode::Bitmap); + let mut instance = FontInstance::new( font_key, glyph_size, ColorF::new(0.0, 0.0, 0.0, 1.0), - requested_render_mode, + render_mode, subpx_dir, platform_options, variations, - options.map_or(false, |opts| opts.synthetic_italics), + synthetic_italics, ); + if self.glyph_rasterizer.is_bitmap_font(&instance) { + instance.render_mode = instance.render_mode.limit_by(FontRenderMode::Bitmap); + } self.resources.font_instances.insert(instance_key, instance); } @@ -564,12 +561,13 @@ impl ResourceCache { pub fn request_glyphs( &mut self, - font: FontInstance, + mut font: FontInstance, glyph_keys: &[GlyphKey], gpu_cache: &mut GpuCache, ) { debug_assert_eq!(self.state, State::AddResources); + self.glyph_rasterizer.prepare_font(&mut font); self.glyph_rasterizer.request_glyphs( &mut self.cached_glyphs, font, @@ -585,7 +583,7 @@ impl ResourceCache { pub fn fetch_glyphs( &self, - font: FontInstance, + mut font: FontInstance, glyph_keys: &[GlyphKey], fetch_buffer: &mut Vec, gpu_cache: &GpuCache, @@ -594,17 +592,16 @@ impl ResourceCache { F: FnMut(SourceTexture, &[GlyphFetchResult]), { debug_assert_eq!(self.state, State::QueryResources); + + self.glyph_rasterizer.prepare_font(&mut font); let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font); let mut current_texture_id = SourceTexture::Invalid; debug_assert!(fetch_buffer.is_empty()); for (loop_index, key) in glyph_keys.iter().enumerate() { - let glyph = glyph_key_cache.get(key); - let cache_item = glyph - .as_ref() - .map(|info| self.texture_cache.get(&info.texture_cache_handle)); - if let Some(cache_item) = cache_item { + if let Some(ref glyph) = *glyph_key_cache.get(key) { + let cache_item = self.texture_cache.get(&glyph.texture_cache_handle); if current_texture_id != cache_item.texture_id { if !fetch_buffer.is_empty() { f(current_texture_id, fetch_buffer); @@ -825,7 +822,7 @@ impl ResourceCache { descriptor, filter, image_data, - [0.0; 2], + [0.0; 3], image_template.dirty_rect, gpu_cache, ); diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 91113685a9..4d98e1230f 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -84,7 +84,7 @@ struct CacheEntry { // Details specific to standalone or shared items. kind: EntryKind, // Arbitrary user data associated with this item. - user_data: [f32; 2], + user_data: [f32; 3], // The last frame this item was requested for rendering. last_access: FrameId, // Handle to the resource rect in the GPU cache. @@ -101,7 +101,7 @@ impl CacheEntry { texture_id: CacheTextureId, size: DeviceUintSize, format: ImageFormat, - user_data: [f32; 2], + user_data: [f32; 3], last_access: FrameId, ) -> CacheEntry { CacheEntry { @@ -135,7 +135,7 @@ impl CacheEntry { (origin.x + self.size.width) as f32, (origin.y + self.size.height) as f32, ]); - request.push([layer_index, self.user_data[0], self.user_data[1], 0.0]); + request.push([layer_index, self.user_data[0], self.user_data[1], self.user_data[2]]); } } } @@ -275,7 +275,7 @@ impl TextureCache { descriptor: ImageDescriptor, filter: TextureFilter, data: ImageData, - user_data: [f32; 2], + user_data: [f32; 3], mut dirty_rect: Option, gpu_cache: &mut GpuCache, ) { @@ -512,7 +512,7 @@ impl TextureCache { fn allocate_from_shared_cache( &mut self, descriptor: &ImageDescriptor, - user_data: [f32; 2], + user_data: [f32; 3], ) -> Option { // Work out which cache it goes in, based on format. let texture_array = match descriptor.format { @@ -561,7 +561,7 @@ impl TextureCache { handle: &mut TextureCacheHandle, descriptor: ImageDescriptor, filter: TextureFilter, - user_data: [f32; 2], + user_data: [f32; 3], ) { assert!(descriptor.width > 0 && descriptor.height > 0); @@ -855,7 +855,7 @@ impl TextureArray { &mut self, width: u32, height: u32, - user_data: [f32; 2], + user_data: [f32; 3], frame_id: FrameId, ) -> Option { // Lazily allocate the regions if not already created. diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 341f188f58..0453951d07 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -16,7 +16,7 @@ use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveIn use internal_types::{FastHashMap, SourceTexture}; use internal_types::BatchTextures; use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; -use prim_store::DeferredResolve; +use prim_store::{DeferredResolve, TextRunMode}; use profiler::FrameProfileCounters; use render_task::{AlphaRenderItem, ClipWorkItem, MaskGeometryKind, MaskSegment}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind}; @@ -55,10 +55,10 @@ impl AlphaBatchHelpers for PrimitiveStore { PrimitiveKind::TextRun => { let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0]; match text_run_cpu.font.render_mode { - FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.color), + FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.font.color), FontRenderMode::Alpha | FontRenderMode::Mono | - FontRenderMode::Bitmap => BlendMode::Alpha, + FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha, } } PrimitiveKind::Image | @@ -508,8 +508,7 @@ impl AlphaRenderItem { let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0]; - let mut font = text_cpu.font.clone(); - font.size = font.size.scale_by(ctx.device_pixel_ratio); + let font = text_cpu.get_font(TextRunMode::Normal, ctx.device_pixel_ratio); ctx.resource_cache.fetch_glyphs( font, @@ -529,6 +528,7 @@ impl AlphaRenderItem { transform_kind, TransformBatchKind::TextRun, ); + let key = BatchKey::new(kind, blend_mode, textures); let batch = batch_list.get_suitable_batch(key, item_bounding_rect); @@ -1170,9 +1170,7 @@ impl RenderTarget for ColorRenderTarget { [sub_metadata.cpu_prim_index.0]; let text_run_cache_prims = &mut self.text_run_cache_prims; - let mut font = text.font.clone(); - font.size = font.size.scale_by(ctx.device_pixel_ratio); - font.render_mode = text.shadow_render_mode; + let font = text.get_font(TextRunMode::Shadow, ctx.device_pixel_ratio); ctx.resource_cache.fetch_glyphs( font, diff --git a/webrender_api/Cargo.toml b/webrender_api/Cargo.toml index dcf051e23a..34e2e11b89 100644 --- a/webrender_api/Cargo.toml +++ b/webrender_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender_api" -version = "0.52.0" +version = "0.52.1" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" diff --git a/webrender_api/src/font.rs b/webrender_api/src/font.rs index 224a17a318..dbb525f36c 100644 --- a/webrender_api/src/font.rs +++ b/webrender_api/src/font.rs @@ -137,6 +137,16 @@ impl FontRenderMode { } } +impl SubpixelDirection { + // Limit the subpixel direction to what is supported by the render mode. + pub fn limit_by(self, render_mode: FontRenderMode) -> SubpixelDirection { + match render_mode { + FontRenderMode::Mono | FontRenderMode::Bitmap => SubpixelDirection::None, + FontRenderMode::Alpha | FontRenderMode::Subpixel => self, + } + } +} + #[repr(u8)] #[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)] pub enum SubpixelOffset { @@ -196,18 +206,103 @@ pub struct GlyphOptions { #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct FontInstanceOptions { - pub render_mode: Option, + pub render_mode: FontRenderMode, + pub subpx_dir: SubpixelDirection, pub synthetic_italics: bool, } +impl Default for FontInstanceOptions { + fn default() -> FontInstanceOptions { + FontInstanceOptions { + render_mode: FontRenderMode::Subpixel, + subpx_dir: SubpixelDirection::Horizontal, + synthetic_italics: false, + } + } +} + +#[cfg(target_os = "windows")] #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct FontInstancePlatformOptions { - // These are currently only used on windows for dwrite fonts. pub use_embedded_bitmap: bool, pub force_gdi_rendering: bool, } +#[cfg(target_os = "windows")] +impl Default for FontInstancePlatformOptions { + fn default() -> FontInstancePlatformOptions { + FontInstancePlatformOptions { + use_embedded_bitmap: false, + force_gdi_rendering: false, + } + } +} + +#[cfg(target_os = "macos")] +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] +pub struct FontInstancePlatformOptions { + pub unused: u32, +} + +#[cfg(target_os = "macos")] +impl Default for FontInstancePlatformOptions { + fn default() -> FontInstancePlatformOptions { + FontInstancePlatformOptions { + unused: 0, + } + } +} + +pub const FONT_FORCE_AUTOHINT: u16 = 0b1; +pub const FONT_NO_AUTOHINT: u16 = 0b10; +pub const FONT_EMBEDDED_BITMAP: u16 = 0b100; +pub const FONT_EMBOLDEN: u16 = 0b1000; +pub const FONT_VERTICAL_LAYOUT: u16 = 0b10000; +pub const FONT_SUBPIXEL_BGR: u16 = 0b100000; + +#[cfg(not(any(target_os = "macos", target_os = "windows")))] +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)] +pub enum FontLCDFilter { + None, + Default, + Light, + Legacy, +} + +#[cfg(not(any(target_os = "macos", target_os = "windows")))] +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)] +pub enum FontHinting { + None, + Mono, + Light, + Normal, + LCD, +} + +#[cfg(not(any(target_os = "macos", target_os = "windows")))] +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] +pub struct FontInstancePlatformOptions { + pub flags: u16, + pub lcd_filter: FontLCDFilter, + pub hinting: FontHinting, +} + +#[cfg(not(any(target_os = "macos", target_os = "windows")))] +impl Default for FontInstancePlatformOptions { + fn default() -> FontInstancePlatformOptions { + FontInstancePlatformOptions { + flags: 0, + lcd_filter: FontLCDFilter::Default, + hinting: FontHinting::LCD, + } + } +} + #[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] pub struct FontInstance { pub font_key: FontKey, @@ -229,26 +324,13 @@ impl FontInstance { pub fn new( font_key: FontKey, size: Au, - mut color: ColorF, + color: ColorF, render_mode: FontRenderMode, subpx_dir: SubpixelDirection, platform_options: Option, variations: Vec, synthetic_italics: bool, ) -> FontInstance { - // In alpha/mono mode, the color of the font is irrelevant. - // Forcing it to black in those cases saves rasterizing glyphs - // of different colors when not needed. - match render_mode { - FontRenderMode::Alpha | FontRenderMode::Mono => { - color = ColorF::new(0.0, 0.0, 0.0, 1.0); - } - FontRenderMode::Bitmap => { - color = ColorF::new(1.0, 1.0, 1.0, 1.0); - } - FontRenderMode::Subpixel => {} - } - FontInstance { font_key, size, diff --git a/wrench/src/wrench.rs b/wrench/src/wrench.rs index 0b87eadcb1..f816049e0c 100644 --- a/wrench/src/wrench.rs +++ b/wrench/src/wrench.rs @@ -376,8 +376,9 @@ impl Wrench { let key = self.api.generate_font_instance_key(); let mut update = ResourceUpdates::new(); let options = FontInstanceOptions { - render_mode, + render_mode: render_mode.unwrap_or(FontRenderMode::Subpixel), synthetic_italics, + ..Default::default() }; update.add_font_instance(key, font_key, size, Some(options), None, Vec::new()); self.api.update_resources(update);