diff --git a/Cargo.lock b/Cargo.lock index d45bac1e0f..c3147a11ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,31 +1,3 @@ -[root] -name = "wrench" -version = "0.2.4" -dependencies = [ - "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bincode 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", - "font-loader 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "osmesa-src 17.2.0-devel (git+https://github.com/servo/osmesa-src)", - "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ron 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "servo-glutin 0.13.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.54.0", - "yaml-rust 0.3.4 (git+https://github.com/vvuk/yaml-rust)", -] - [[package]] name = "adler32" version = "0.3.0" @@ -190,7 +162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -219,7 +191,7 @@ dependencies = [ [[package]] name = "core-graphics" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -234,7 +206,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -851,7 +823,7 @@ dependencies = [ "cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cocoa 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1065,7 +1037,7 @@ dependencies = [ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1098,7 +1070,7 @@ dependencies = [ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1116,6 +1088,34 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wrench" +version = "0.2.4" +dependencies = [ + "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", + "font-loader 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "osmesa-src 17.2.0-devel (git+https://github.com/servo/osmesa-src)", + "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ron 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-glutin 0.13.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.54.0", + "yaml-rust 0.3.4 (git+https://github.com/vvuk/yaml-rust)", +] + [[package]] name = "ws" version = "0.7.3" @@ -1198,7 +1198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d" "checksum core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5909502e547762013619f4c4e01cc7393c20fe2d52d7fa471c1210adb2320dc7" "checksum core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bc9fb3d6cb663e6fd7cf1c63f9b144ee2b1e4a78595a0451dd34bff85b9a3387" -"checksum core-graphics 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "096dff8fda997f2b8a1f3d71d427d2cdf6d7a18559f08c5edb7e332c4414ab4a" +"checksum core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc0a78ab2ac23b6ea7b3fe5fe93b227900dc0956979735b8f68032417976dd4" "checksum core-text 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcad23756dd1dc4b47bf6a914ace27aadb8fa68889db5837af2308d018d0467c" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum deflate 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "24c5f3de3a8e183ab9a169654b652407e5e80bed40986bcca92c2b088b9bfa80" diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index 169887285d..3587f09be3 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -47,5 +47,5 @@ dwrote = "0.4" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.4" -core-graphics = "0.12.2" +core-graphics = "0.12.3" core-text = { version = "8.0", default-features = false } diff --git a/webrender/res/cs_text_run.glsl b/webrender/res/cs_text_run.glsl index 1d7025f5e4..3aa698ed29 100644 --- a/webrender/res/cs_text_run.glsl +++ b/webrender/res/cs_text_run.glsl @@ -25,27 +25,21 @@ void main(void) { GlyphResource res = fetch_glyph_resource(resource_address); - // Glyphs size is already in device-pixels. + // Glyph 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) * res.scale; - vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio; - vec2 origin = prim.task.common_data.task_rect.p0 + - uDevicePixelRatio * (local_pos - prim.task.content_origin); - vec4 local_rect = vec4(origin, size); + vec2 glyph_size = res.uv_rect.zw - res.uv_rect.xy; + vec2 glyph_pos = res.offset + glyph_size * aPosition.xy; + vec2 local_pos = prim.task.common_data.task_rect.p0 + glyph_pos * res.scale + + uDevicePixelRatio * (glyph.offset - prim.task.content_origin); + gl_Position = uTransform * vec4(local_pos, 0.0, 1.0); vec2 texture_size = vec2(textureSize(sColor0, 0)); vec2 st0 = res.uv_rect.xy / texture_size; vec2 st1 = res.uv_rect.zw / texture_size; - vec2 pos = mix(local_rect.xy, - local_rect.xy + local_rect.zw, - aPosition.xy); - vUv = vec3(mix(st0, st1, aPosition.xy), res.layer); vColor = prim.task.color; - - gl_Position = uTransform * vec4(pos, 0.0, 1.0); } #endif diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index d1c35bd9bd..e58b7414cd 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -552,7 +552,6 @@ vec4 get_layer_pos(vec2 pos, Layer layer) { // Compute a snapping offset in world space (adjusted to pixel ratio), // given local position on the layer and a snap rectangle. vec2 compute_snap_offset(vec2 local_pos, - RectWithSize local_clip_rect, Layer layer, RectWithSize snap_rect) { // Ensure that the snap rect is at *least* one device pixel in size. @@ -597,9 +596,9 @@ VertexInfo write_vertex(RectWithSize instance_rect, vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect), layer.local_clip_rect); /// Compute the snapping offset. - vec2 snap_offset = compute_snap_offset(clamped_local_pos, local_clip_rect, layer, snap_rect); + vec2 snap_offset = compute_snap_offset(clamped_local_pos, layer, snap_rect); - // Transform the current vertex to the world cpace. + // Transform the current vertex to world space. vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0); // Convert the world positions to device pixel space. diff --git a/webrender/res/ps_text_run.glsl b/webrender/res/ps_text_run.glsl index 0af4f9ede4..18d0b2922e 100644 --- a/webrender/res/ps_text_run.glsl +++ b/webrender/res/ps_text_run.glsl @@ -8,10 +8,6 @@ flat varying vec4 vColor; varying vec3 vUv; flat varying vec4 vUvBorder; -#ifdef WR_FEATURE_TRANSFORM -varying vec3 vLocalPos; -#endif - #ifdef WR_VERTEX_SHADER #define MODE_ALPHA 0 @@ -23,6 +19,42 @@ varying vec3 vLocalPos; #define MODE_SUBPX_BG_PASS2 6 #define MODE_COLOR_BITMAP 7 +VertexInfo write_text_vertex(vec2 local_pos, + RectWithSize local_clip_rect, + float z, + Layer layer, + PictureTask task, + RectWithSize snap_rect) { + // Clamp to the two local clip rects. + vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect), layer.local_clip_rect); + + // Transform the current vertex to world space. + vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0); + + // Convert the world positions to device pixel space. + vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; + + // Apply offsets for the render task to get correct screen location. + vec2 final_pos = device_pos - + task.content_origin + + task.common_data.task_rect.p0; + +#ifdef WR_FEATURE_GLYPH_TRANSFORM + // For transformed subpixels, we just need to align the glyph origin to a device pixel. + // Only check the layer transform's translation since the scales and axes match. + vec2 world_snap_p0 = snap_rect.p0 + layer.transform[3].xy * uDevicePixelRatio; + final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0; +#elif !defined(WR_FEATURE_TRANSFORM) + // Compute the snapping offset only if the layer transform is axis-aligned. + final_pos += compute_snap_offset(clamped_local_pos, layer, snap_rect); +#endif + + gl_Position = uTransform * vec4(final_pos, z, 1.0); + + VertexInfo vi = VertexInfo(clamped_local_pos, device_pos); + return vi; +} + void main(void) { Primitive prim = load_primitive(); TextRun text = fetch_text_run(prim.specific_prim_address); @@ -35,30 +67,40 @@ void main(void) { text.subpx_dir); GlyphResource res = fetch_glyph_resource(resource_address); - vec2 local_pos = glyph.offset + - text.offset + - vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio; - - RectWithSize local_rect = RectWithSize(local_pos, - (res.uv_rect.zw - res.uv_rect.xy) * res.scale / uDevicePixelRatio); - -#ifdef WR_FEATURE_TRANSFORM - TransformVertexInfo vi = write_transform_vertex(local_rect, - prim.local_clip_rect, - vec4(0.0), - prim.z, - prim.layer, - prim.task); - vLocalPos = vi.local_pos; - vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.p0) / local_rect.size; +#ifdef WR_FEATURE_GLYPH_TRANSFORM + // Transform from local space to glyph space. + mat2 transform = mat2(prim.layer.transform) * uDevicePixelRatio; + + // Compute the glyph rect in glyph space. + RectWithSize glyph_rect = RectWithSize(res.offset + transform * (text.offset + glyph.offset), + res.uv_rect.zw - res.uv_rect.xy); + + // Select the corner of the glyph rect that we are processing. + // Transform it from glyph space into local space. + vec2 local_pos = inverse(transform) * (glyph_rect.p0 + glyph_rect.size * aPosition.xy); +#else + // Scale from glyph space to local space. + float scale = res.scale / uDevicePixelRatio; + + // Compute the glyph rect in local space. + RectWithSize glyph_rect = RectWithSize(scale * res.offset + text.offset + glyph.offset, + scale * (res.uv_rect.zw - res.uv_rect.xy)); + + // Select the corner of the glyph rect that we are processing. + vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy; +#endif + + VertexInfo vi = write_text_vertex(local_pos, + prim.local_clip_rect, + prim.z, + prim.layer, + prim.task, + glyph_rect); + +#ifdef WR_FEATURE_GLYPH_TRANSFORM + vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size; #else - VertexInfo vi = write_vertex(local_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - local_rect); - vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; + vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size; #endif write_clip(vi.screen_pos, prim.clip_area); @@ -98,11 +140,7 @@ void main(void) { vec3 tc = vec3(clamp(vUv.xy, vUvBorder.xy, vUvBorder.zw), vUv.z); vec4 mask = texture(sColor0, tc); - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - init_transform_fs(vLocalPos, alpha); -#endif - alpha *= do_clip(); + float alpha = do_clip(); #ifdef WR_FEATURE_SUBPX_BG_PASS1 mask.rgb = vec3(mask.a) - mask.rgb; diff --git a/webrender/src/glyph_rasterizer.rs b/webrender/src/glyph_rasterizer.rs index 34470eed91..880090171a 100644 --- a/webrender/src/glyph_rasterizer.rs +++ b/webrender/src/glyph_rasterizer.rs @@ -7,7 +7,7 @@ use api::{IdNamespace, LayoutPoint}; use api::{ColorF, ColorU, DevicePoint, DeviceUintSize}; use api::{FontInstancePlatformOptions, FontRenderMode, FontVariation}; use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey, SubpixelDirection}; -use api::{ImageData, ImageDescriptor, ImageFormat}; +use api::{ImageData, ImageDescriptor, ImageFormat, LayerToWorldTransform}; use app_units::Au; use device::TextureFilter; use glyph_cache::{CachedGlyphInfo, GlyphCache}; @@ -17,12 +17,115 @@ use platform::font::FontContext; use profiler::TextureCacheProfileCounters; use rayon::ThreadPool; use rayon::prelude::*; +use std::cmp; use std::collections::hash_map::Entry; +use std::hash::{Hash, Hasher}; use std::mem; use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::mpsc::{channel, Receiver, Sender}; use texture_cache::{TextureCache, TextureCacheHandle}; +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +pub struct FontTransform { + pub scale_x: f32, + pub skew_x: f32, + pub skew_y: f32, + pub scale_y: f32, +} + +// Floats don't impl Hash/Eq/Ord... +impl Eq for FontTransform {} +impl Ord for FontTransform { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.partial_cmp(other).unwrap_or(cmp::Ordering::Equal) + } +} +impl Hash for FontTransform { + fn hash(&self, state: &mut H) { + // Note: this is inconsistent with the Eq impl for -0.0 (don't care). + self.scale_x.to_bits().hash(state); + self.skew_x.to_bits().hash(state); + self.skew_y.to_bits().hash(state); + self.scale_y.to_bits().hash(state); + } +} + +impl FontTransform { + const QUANTIZE_SCALE: f32 = 1024.0; + + pub fn new(scale_x: f32, skew_x: f32, skew_y: f32, scale_y: f32) -> Self { + FontTransform { scale_x, skew_x, skew_y, scale_y } + } + + pub fn identity() -> Self { + FontTransform::new(1.0, 0.0, 0.0, 1.0) + } + + pub fn is_identity(&self) -> bool { + *self == FontTransform::identity() + } + + pub fn quantize(&self) -> Self { + FontTransform::new( + (self.scale_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE, + (self.skew_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE, + (self.skew_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE, + (self.scale_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE, + ) + } + + pub fn determinant(&self) -> f64 { + self.scale_x as f64 * self.scale_y as f64 - self.skew_y as f64 * self.skew_x as f64 + } + + pub fn compute_scale(&self) -> Option<(f64, f64)> { + let det = self.determinant(); + if det != 0.0 { + let major = (self.scale_x as f64).hypot(self.skew_y as f64); + let minor = det.abs() / major; + Some((major, minor)) + } else { + None + } + } + + pub fn pre_scale(&self, scale_x: f32, scale_y: f32) -> Self { + FontTransform::new( + self.scale_x * scale_x, + self.skew_x * scale_y, + self.skew_y * scale_x, + self.scale_y * scale_y, + ) + } + + #[allow(dead_code)] + pub fn inverse(&self) -> Option { + let det = self.determinant(); + if det != 0.0 { + let inv_det = det.recip() as f32; + Some(FontTransform::new( + self.scale_y * inv_det, + -self.skew_x * inv_det, + -self.skew_y * inv_det, + self.scale_x * inv_det + )) + } else { + None + } + } + + #[allow(dead_code)] + pub fn apply(&self, x: f32, y: f32) -> (f32, f32) { + (self.scale_x * x + self.skew_x * y, self.skew_y * x + self.scale_y * y) + } +} + +impl<'a> From<&'a LayerToWorldTransform> for FontTransform { + fn from(xform: &'a LayerToWorldTransform) -> Self { + FontTransform::new(xform.m11, xform.m21, xform.m12, xform.m22) + } +} + #[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] pub struct FontInstance { pub font_key: FontKey, @@ -39,6 +142,7 @@ pub struct FontInstance { pub platform_options: Option, pub variations: Vec, pub synthetic_italics: bool, + pub transform: FontTransform, } impl FontInstance { @@ -63,6 +167,7 @@ impl FontInstance { platform_options, variations, synthetic_italics, + transform: FontTransform::identity(), } } @@ -73,27 +178,29 @@ impl FontInstance { SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()), } } + + pub fn get_subpixel_glyph_format(&self) -> GlyphFormat { + if self.transform.is_identity() { GlyphFormat::Subpixel } else { GlyphFormat::TransformedSubpixel } + } + + #[allow(dead_code)] + pub fn get_glyph_format(&self) -> GlyphFormat { + match self.render_mode { + FontRenderMode::Mono | FontRenderMode::Alpha => GlyphFormat::Alpha, + FontRenderMode::Subpixel => self.get_subpixel_glyph_format(), + FontRenderMode::Bitmap => GlyphFormat::ColorBitmap, + } + } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum GlyphFormat { - Mono, Alpha, Subpixel, + TransformedSubpixel, ColorBitmap, } -impl From for GlyphFormat { - fn from(render_mode: FontRenderMode) -> GlyphFormat { - match render_mode { - FontRenderMode::Mono => GlyphFormat::Mono, - FontRenderMode::Alpha => GlyphFormat::Alpha, - FontRenderMode::Subpixel => GlyphFormat::Subpixel, - FontRenderMode::Bitmap => GlyphFormat::ColorBitmap, - } - } -} - pub struct RasterizedGlyph { pub top: f32, pub left: f32, @@ -396,7 +503,7 @@ impl GlyphRasterizer { }, TextureFilter::Linear, ImageData::Raw(glyph_bytes.clone()), - [glyph.left, glyph.top, glyph.scale], + [glyph.left, -glyph.top, glyph.scale], None, gpu_cache, ); @@ -404,7 +511,7 @@ impl GlyphRasterizer { texture_cache_handle, glyph_bytes, size: DeviceUintSize::new(glyph.width, glyph.height), - offset: DevicePoint::new(glyph.left, glyph.top), + offset: DevicePoint::new(glyph.left, -glyph.top), scale: glyph.scale, format: glyph.format, }) diff --git a/webrender/src/platform/macos/font.rs b/webrender/src/platform/macos/font.rs index 17b8319885..617912c791 100644 --- a/webrender/src/platform/macos/font.rs +++ b/webrender/src/platform/macos/font.rs @@ -17,15 +17,14 @@ use core_graphics::color_space::CGColorSpace; use core_graphics::context::{CGContext, CGTextDrawingMode}; use core_graphics::data_provider::CGDataProvider; use core_graphics::font::{CGFont, CGGlyph}; -use core_graphics::geometry::{CGPoint, CGRect, CGSize}; +use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize}; use core_text; use core_text::font::{CTFont, CTFontRef}; use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait}; use gamma_lut::{ColorLut, GammaLut}; -use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph}; +use glyph_rasterizer::{FontInstance, RasterizedGlyph}; use internal_types::FastHashMap; use std::collections::hash_map::Entry; -use std::ptr; use std::sync::Arc; pub struct FontContext { @@ -81,11 +80,12 @@ fn should_use_white_on_black(color: ColorU) -> bool { fn get_glyph_metrics( ct_font: &CTFont, + transform: Option<&CGAffineTransform>, glyph: CGGlyph, x_offset: f64, y_offset: f64, ) -> GlyphMetrics { - let bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]); + let mut bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]); if bounds.origin.x.is_nan() || bounds.origin.y.is_nan() || bounds.size.width.is_nan() || bounds.size.height.is_nan() @@ -105,6 +105,14 @@ 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); + + if let Some(transform) = transform { + bounds = bounds.apply_transform(transform); + advance = advance.apply_transform(transform); + } + // First round out to pixel boundaries // CG Origin is bottom left let mut left = bounds.origin.x.floor() as i32; @@ -124,16 +132,13 @@ fn get_glyph_metrics( let width = right - left; let height = top - bottom; - let advance = - ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, ptr::null_mut(), 1); - let metrics = GlyphMetrics { rasterized_left: left, rasterized_width: width as u32, rasterized_height: height as u32, rasterized_ascent: top, rasterized_descent: -bottom, - advance: advance as f32, + advance: advance.width as f32, }; metrics @@ -150,9 +155,9 @@ extern { fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef; } -fn new_ct_font_with_variations(cg_font: &CGFont, size: Au, variations: &[FontVariation]) -> CTFont { +fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVariation]) -> CTFont { unsafe { - let ct_font = core_text::font::new_from_CGFont(cg_font, size.to_f64_px()); + let ct_font = core_text::font::new_from_CGFont(cg_font, size); if variations.is_empty() { return ct_font; } @@ -243,7 +248,7 @@ fn new_ct_font_with_variations(cg_font: &CGFont, size: Au, variations: &[FontVar } let vals_dict = CFDictionary::from_CFType_pairs(&vals); let cg_var_font = cg_font.create_copy_from_variations(&vals_dict).unwrap(); - core_text::font::new_from_CGFont(&cg_var_font, size.to_f64_px()) + core_text::font::new_from_CGFont(&cg_var_font, size) } } @@ -317,7 +322,7 @@ impl FontContext { None => return None, Some(cg_font) => cg_font, }; - let ct_font = new_ct_font_with_variations(cg_font, size, variations); + let ct_font = new_ct_font_with_variations(cg_font, size.to_f64_px(), variations); entry.insert(ct_font.clone()); Some(ct_font) } @@ -328,7 +333,7 @@ impl FontContext { let character = ch as u16; let mut glyph = 0; - self.get_ct_font(font_key, Au(16 * 60), &[]) + 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); @@ -349,7 +354,7 @@ impl FontContext { .and_then(|ref ct_font| { let glyph = key.index as CGGlyph; let (x_offset, y_offset) = font.get_subpx_offset(key); - let metrics = get_glyph_metrics(ct_font, glyph, x_offset, y_offset); + let metrics = get_glyph_metrics(ct_font, None, glyph, x_offset, y_offset); if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 { None } else { @@ -447,14 +452,29 @@ impl FontContext { font: &FontInstance, key: &GlyphKey, ) -> Option { - let ct_font = match self.get_ct_font(font.font_key, font.size, &font.variations) { + let (.., minor) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); + let size = font.size.scale_by(minor as f32); + let ct_font = match self.get_ct_font(font.font_key, size, &font.variations) { Some(font) => font, None => return None, }; + let shape = font.transform.pre_scale(minor.recip() as f32, minor.recip() as f32); + let transform = if shape.is_identity() { + None + } else { + Some(CGAffineTransform { + a: shape.scale_x as f64, + b: -shape.skew_y as f64, + c: -shape.skew_x as f64, + d: shape.scale_y as f64, + tx: 0.0, + ty: 0.0 + }) + }; let glyph = key.index as CGGlyph; let (x_offset, y_offset) = font.get_subpx_offset(key); - let metrics = get_glyph_metrics(&ct_font, glyph, x_offset, y_offset); + let metrics = get_glyph_metrics(&ct_font, transform.as_ref(), glyph, x_offset, y_offset); if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 { return None; } @@ -552,12 +572,6 @@ impl FontContext { cg_context.set_allows_antialiasing(antialias); cg_context.set_should_antialias(antialias); - // CG Origin is bottom left, WR is top left. Need -y offset - let rasterization_origin = CGPoint { - x: -metrics.rasterized_left as f64 + x_offset, - y: metrics.rasterized_descent as f64 - y_offset, - }; - // 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); @@ -573,7 +587,20 @@ impl FontContext { // 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); - ct_font.draw_glyphs(&[glyph], &[rasterization_origin], cg_context.clone()); + + // 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()); + } + + ct_font.draw_glyphs(&[glyph], &[draw_origin], cg_context.clone()); let mut rasterized_pixels = cg_context.data().to_vec(); @@ -630,7 +657,7 @@ impl FontContext { width: metrics.rasterized_width, height: metrics.rasterized_height, scale: 1.0, - format: GlyphFormat::from(font.render_mode), + format: font.get_glyph_format(), bytes: rasterized_pixels, }) } diff --git a/webrender/src/platform/unix/font.rs b/webrender/src/platform/unix/font.rs index 3b191f2971..7f3c261300 100644 --- a/webrender/src/platform/unix/font.rs +++ b/webrender/src/platform/unix/font.rs @@ -14,6 +14,7 @@ use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt} use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face}; use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph}; use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size}; +use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform}; 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}; @@ -186,15 +187,33 @@ impl FontContext { load_flags |= FT_LOAD_COLOR; load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + let req_size = font.size.to_f64_px(); 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()) + unsafe { FT_Set_Transform(face.face, ptr::null_mut(), ptr::null_mut()) }; + self.choose_bitmap_size(face.face, req_size) } } 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 (major, minor) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); + let shape = font.transform.pre_scale(major.recip() as f32, minor.recip() as f32); + let mut ft_shape = FT_Matrix { + xx: (shape.scale_x * 65536.0) as FT_Fixed, + xy: (shape.skew_x * -65536.0) as FT_Fixed, + yx: (shape.skew_y * -65536.0) as FT_Fixed, + yy: (shape.scale_y * 65536.0) as FT_Fixed, + }; + unsafe { + FT_Set_Transform(face.face, &mut ft_shape, ptr::null_mut()); + FT_Set_Char_Size( + face.face, + (req_size * major * 64.0 + 0.5) as FT_F26Dot6, + (req_size * minor * 64.0 + 0.5) as FT_F26Dot6, + 0, + 0, + ) + } }; if result.succeeded() { @@ -518,14 +537,14 @@ impl FontContext { let (format, actual_width, actual_height) = match pixel_mode { FT_Pixel_Mode::FT_PIXEL_MODE_LCD => { assert!(bitmap.width % 3 == 0); - (GlyphFormat::Subpixel, (bitmap.width / 3) as i32, bitmap.rows as i32) + (font.get_subpixel_glyph_format(), (bitmap.width / 3) as i32, bitmap.rows as i32) } FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => { assert!(bitmap.rows % 3 == 0); - (GlyphFormat::Subpixel, bitmap.width as i32, (bitmap.rows / 3) as i32) + (font.get_subpixel_glyph_format(), bitmap.width as i32, (bitmap.rows / 3) as i32) } FT_Pixel_Mode::FT_PIXEL_MODE_MONO => { - (GlyphFormat::Mono, bitmap.width as i32, bitmap.rows as i32) + (GlyphFormat::Alpha, bitmap.width as i32, bitmap.rows as i32) } FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => { (GlyphFormat::Alpha, bitmap.width as i32, bitmap.rows as i32) @@ -620,8 +639,8 @@ impl FontContext { } Some(RasterizedGlyph { - left: ((dimensions.left + left) as f32 * scale).round(), - top: ((dimensions.top + top - actual_height) as f32 * scale).round(), + left: (dimensions.left + left) as f32, + top: (dimensions.top + top - actual_height) as f32, width: actual_width as u32, height: actual_height as u32, scale, diff --git a/webrender/src/platform/windows/font.rs b/webrender/src/platform/windows/font.rs index 00f39eb84e..946fe9630b 100644 --- a/webrender/src/platform/windows/font.rs +++ b/webrender/src/platform/windows/font.rs @@ -6,7 +6,7 @@ use api::{FontInstancePlatformOptions, FontKey, FontRenderMode}; use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection}; use dwrote; use gamma_lut::{ColorLut, GammaLut}; -use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph}; +use glyph_rasterizer::{FontInstance, RasterizedGlyph}; use internal_types::FastHashMap; use std::sync::Arc; @@ -161,9 +161,12 @@ impl FontContext { ascenderOffset: 0.0, }; + let (.., minor) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); + let size = (font.size.to_f64_px() * minor) as f32; + let glyph_run = dwrote::DWRITE_GLYPH_RUN { fontFace: unsafe { face.as_ptr() }, - fontEmSize: font.size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels) + fontEmSize: size, // size in DIPs (1/96", same as CSS pixels) glyphCount: 1, glyphIndices: &glyph, glyphAdvances: &advance, @@ -176,25 +179,26 @@ impl FontContext { let dwrite_render_mode = dwrite_render_mode( face, font.render_mode, - font.size.to_f32_px(), + size, dwrite_measure_mode, font.platform_options, ); let (x_offset, y_offset) = font.get_subpx_offset(key); - let transform = Some(dwrote::DWRITE_MATRIX { - m11: 1.0, - m12: 0.0, - m21: 0.0, - m22: 1.0, + let shape = font.transform.pre_scale(minor.recip() as f32, minor.recip() as f32); + let transform = dwrote::DWRITE_MATRIX { + m11: shape.scale_x, + m12: shape.skew_y, + m21: shape.skew_x, + m22: shape.scale_y, dx: x_offset as f32, dy: y_offset as f32, - }); + }; dwrote::GlyphRunAnalysis::create( &glyph_run, 1.0, - transform, + Some(transform), dwrite_render_mode, dwrite_measure_mode, 0.0, @@ -359,7 +363,7 @@ impl FontContext { width, height, scale: 1.0, - format: GlyphFormat::from(font.render_mode), + format: font.get_glyph_format(), bytes: bgra_pixels, }) } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index bbd5797b30..68503e99ca 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -3,16 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect}; -use api::{DevicePoint, ExtendMode, GlyphInstance, GlyphKey}; +use api::{DevicePoint, ExtendMode, FontRenderMode, GlyphInstance, GlyphKey}; use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect}; -use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle}; +use api::{ClipMode, LayerSize, LayerVector2D, LayerToWorldTransform, LineOrientation, LineStyle}; use api::{ClipAndScrollInfo, EdgeAaSegmentMask, PremultipliedColorF, TileOffset}; use api::{ClipId, LayerTransform, PipelineId, YuvColorSpace, YuvFormat}; use border::BorderCornerInstance; use clip_scroll_tree::ClipScrollTree; use clip::{ClipSourcesHandle, ClipStore}; use frame_builder::PrimitiveContext; -use glyph_rasterizer::FontInstance; +use glyph_rasterizer::{FontInstance, FontTransform}; use internal_types::FastHashMap; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; @@ -591,9 +591,20 @@ pub struct TextRunPrimitiveCpu { impl TextRunPrimitiveCpu { - pub fn get_font(&self, device_pixel_ratio: f32) -> FontInstance { + pub fn get_font( + &self, + device_pixel_ratio: f32, + transform: &LayerToWorldTransform, + ) -> FontInstance { let mut font = self.font.clone(); font.size = font.size.scale_by(device_pixel_ratio); + if font.render_mode == FontRenderMode::Subpixel { + if transform.has_perspective_component() || !transform.has_2d_inverse() { + font.render_mode = FontRenderMode::Alpha; + } else { + font.transform = FontTransform::from(transform).quantize(); + } + } font } @@ -601,10 +612,11 @@ impl TextRunPrimitiveCpu { &mut self, resource_cache: &mut ResourceCache, device_pixel_ratio: f32, + transform: &LayerToWorldTransform, display_list: &BuiltDisplayList, gpu_cache: &mut GpuCache, ) { - let font = self.get_font(device_pixel_ratio); + let font = self.get_font(device_pixel_ratio, transform); // Cache the glyph positions, if not in the cache already. // TODO(gw): In the future, remove `glyph_instances` @@ -1110,6 +1122,7 @@ impl PrimitiveStore { text.prepare_for_render( resource_cache, prim_context.device_pixel_ratio, + &prim_context.scroll_node.world_content_transform, prim_context.display_list, gpu_cache, ); diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index e22ad56bde..f6da53abf1 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -271,9 +271,9 @@ impl Into for TextShaderMode { impl From for TextShaderMode { fn from(format: GlyphFormat) -> TextShaderMode { match format { - GlyphFormat::Mono | GlyphFormat::Alpha => TextShaderMode::Alpha, - GlyphFormat::Subpixel => { - panic!("Subpixel glyph format must be handled separately."); + GlyphFormat::Alpha => TextShaderMode::Alpha, + GlyphFormat::Subpixel | GlyphFormat::TransformedSubpixel => { + panic!("Subpixel glyph formats must be handled separately."); } GlyphFormat::ColorBitmap => TextShaderMode::ColorBitmap, } @@ -894,6 +894,7 @@ enum ShaderKind { Cache(VertexArrayKind), ClipCache, Brush, + Text, } struct LazilyCompiledShader { @@ -947,7 +948,7 @@ impl LazilyCompiledShader { if self.program.is_none() { let program = try!{ match self.kind { - ShaderKind::Primitive | ShaderKind::Brush => { + ShaderKind::Primitive | ShaderKind::Brush | ShaderKind::Text => { create_prim_shader(self.name, device, &self.features, @@ -977,11 +978,6 @@ impl LazilyCompiledShader { } } -struct PrimitiveShader { - simple: LazilyCompiledShader, - transform: LazilyCompiledShader, -} - // A brush shader supports two modes: // opaque: // Used for completely opaque primitives, @@ -1055,16 +1051,9 @@ impl BrushShader { } } -struct FileWatcher { - notifier: Box, - result_tx: Sender, -} - -impl FileWatcherHandler for FileWatcher { - fn file_changed(&self, path: PathBuf) { - self.result_tx.send(ResultMsg::RefreshShader(path)).ok(); - self.notifier.wake_up(); - } +struct PrimitiveShader { + simple: LazilyCompiledShader, + transform: LazilyCompiledShader, } impl PrimitiveShader { @@ -1120,6 +1109,87 @@ impl PrimitiveShader { } } +struct TextShader { + simple: LazilyCompiledShader, + transform: LazilyCompiledShader, + glyph_transform: LazilyCompiledShader, +} + +impl TextShader { + fn new( + name: &'static str, + device: &mut Device, + features: &[&'static str], + precache: bool, + ) -> Result { + let simple = try!{ + LazilyCompiledShader::new(ShaderKind::Text, + name, + features, + device, + precache) + }; + + let mut transform_features = features.to_vec(); + transform_features.push("TRANSFORM"); + + let transform = try!{ + LazilyCompiledShader::new(ShaderKind::Text, + name, + &transform_features, + device, + precache) + }; + + let mut glyph_transform_features = features.to_vec(); + glyph_transform_features.push("GLYPH_TRANSFORM"); + + let glyph_transform = try!{ + LazilyCompiledShader::new(ShaderKind::Text, + name, + &glyph_transform_features, + device, + precache) + }; + + Ok(TextShader { simple, transform, glyph_transform }) + } + + fn bind( + &mut self, + device: &mut Device, + glyph_format: GlyphFormat, + transform_kind: TransformedRectKind, + projection: &Transform3D, + mode: M, + renderer_errors: &mut Vec, + ) where M: Into { + match glyph_format { + GlyphFormat::Alpha | + GlyphFormat::Subpixel | + GlyphFormat::ColorBitmap => { + match transform_kind { + TransformedRectKind::AxisAligned => { + self.simple.bind(device, projection, mode, renderer_errors) + } + TransformedRectKind::Complex => { + self.transform.bind(device, projection, mode, renderer_errors) + } + } + } + GlyphFormat::TransformedSubpixel => { + self.glyph_transform.bind(device, projection, mode, renderer_errors) + } + } + } + + fn deinit(self, device: &mut Device) { + self.simple.deinit(device); + self.transform.deinit(device); + self.glyph_transform.deinit(device); + } +} + fn create_prim_shader( name: &'static str, device: &mut Device, @@ -1193,6 +1263,18 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result, + result_tx: Sender, +} + +impl FileWatcherHandler for FileWatcher { + fn file_changed(&self, path: PathBuf) { + self.result_tx.send(ResultMsg::RefreshShader(path)).ok(); + self.notifier.wake_up(); + } +} + #[derive(Clone, Debug, PartialEq)] pub enum ReadPixelsFormat { Rgba8, @@ -1245,8 +1327,8 @@ pub struct Renderer<'a> { // a cache shader (e.g. blur) to the screen. ps_rectangle: PrimitiveShader, ps_rectangle_clip: PrimitiveShader, - ps_text_run: PrimitiveShader, - ps_text_run_subpx_bg_pass1: PrimitiveShader, + ps_text_run: TextShader, + ps_text_run_subpx_bg_pass1: TextShader, ps_image: Vec>, ps_yuv_image: Vec>, ps_border_corner: PrimitiveShader, @@ -1509,17 +1591,17 @@ impl<'a> Renderer<'a> { }; let ps_text_run = try!{ - PrimitiveShader::new("ps_text_run", - &mut device, - &[], - options.precache_shaders) + TextShader::new("ps_text_run", + &mut device, + &[], + options.precache_shaders) }; let ps_text_run_subpx_bg_pass1 = try!{ - PrimitiveShader::new("ps_text_run", - &mut device, - &["SUBPX_BG_PASS1"], - options.precache_shaders) + TextShader::new("ps_text_run", + &mut device, + &["SUBPX_BG_PASS1"], + options.precache_shaders) }; // All image configuration. @@ -2960,6 +3042,7 @@ impl<'a> Renderer<'a> { self.ps_text_run.bind( &mut self.device, + glyph_format, transform_kind, projection, TextShaderMode::from(glyph_format), @@ -2977,6 +3060,7 @@ impl<'a> Renderer<'a> { self.ps_text_run.bind( &mut self.device, + glyph_format, transform_kind, projection, TextShaderMode::SubpixelConstantTextColor, @@ -2998,6 +3082,7 @@ impl<'a> Renderer<'a> { self.ps_text_run.bind( &mut self.device, + glyph_format, transform_kind, projection, TextShaderMode::SubpixelPass0, @@ -3014,6 +3099,7 @@ impl<'a> Renderer<'a> { self.ps_text_run.bind( &mut self.device, + glyph_format, transform_kind, projection, TextShaderMode::SubpixelPass1, @@ -3037,6 +3123,7 @@ impl<'a> Renderer<'a> { self.ps_text_run.bind( &mut self.device, + glyph_format, transform_kind, projection, TextShaderMode::SubpixelWithBgColorPass0, @@ -3053,6 +3140,7 @@ impl<'a> Renderer<'a> { self.ps_text_run_subpx_bg_pass1.bind( &mut self.device, + glyph_format, transform_kind, projection, TextShaderMode::SubpixelWithBgColorPass1, @@ -3070,6 +3158,7 @@ impl<'a> Renderer<'a> { self.ps_text_run.bind( &mut self.device, + glyph_format, transform_kind, projection, TextShaderMode::SubpixelWithBgColorPass2, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index e6b984668e..74eeb09c26 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -543,7 +543,10 @@ fn add_to_batch( let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0]; - let font = text_cpu.get_font(ctx.device_pixel_ratio); + let font = text_cpu.get_font( + ctx.device_pixel_ratio, + &scroll_node.transform, + ); ctx.resource_cache.fetch_glyphs( font, @@ -1487,7 +1490,10 @@ impl RenderTarget for ColorRenderTarget { [sub_metadata.cpu_prim_index.0]; let text_run_cache_prims = &mut self.text_run_cache_prims; - let font = text.get_font(ctx.device_pixel_ratio); + let font = text.get_font( + ctx.device_pixel_ratio, + &LayerToWorldTransform::identity(), + ); ctx.resource_cache.fetch_glyphs( font, diff --git a/webrender/src/util.rs b/webrender/src/util.rs index acee0d5184..96304d1b46 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -21,6 +21,7 @@ pub trait MatrixHelpers { fn is_identity(&self) -> bool; fn preserves_2d_axis_alignment(&self) -> bool; fn has_perspective_component(&self) -> bool; + fn has_2d_inverse(&self) -> bool; fn inverse_project(&self, target: &TypedPoint2D) -> Option>; fn inverse_rect_footprint(&self, rect: &TypedRect) -> TypedRect; fn transform_kind(&self) -> TransformedRectKind; @@ -75,6 +76,10 @@ impl MatrixHelpers for TypedTransform3D { self.m14 != 0.0 || self.m24 != 0.0 || self.m34 != 0.0 || self.m44 != 1.0 } + fn has_2d_inverse(&self) -> bool { + self.m11 * self.m22 - self.m12 * self.m21 != 0.0 + } + fn inverse_project(&self, target: &TypedPoint2D) -> Option> { let m: TypedTransform2D; m = TypedTransform2D::column_major( diff --git a/webrender_api/Cargo.toml b/webrender_api/Cargo.toml index b96124e346..accb88f3a8 100644 --- a/webrender_api/Cargo.toml +++ b/webrender_api/Cargo.toml @@ -21,7 +21,7 @@ time = "0.1" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.4" -core-graphics = "0.12.2" +core-graphics = "0.12.3" [target.'cfg(target_os = "windows")'.dependencies] dwrote = "0.4" diff --git a/wrench/reftests/text/reftest.list b/wrench/reftests/text/reftest.list index 3958f4f369..534fbd0d75 100644 --- a/wrench/reftests/text/reftest.list +++ b/wrench/reftests/text/reftest.list @@ -36,3 +36,6 @@ options(disable-aa) == transparent-no-aa.yaml transparent-no-aa-ref.yaml != diacritics.yaml diacritics-ref.yaml fuzzy(1,1) platform(linux) options(disable-subpixel) == text-masking.yaml text-masking-alpha.png fuzzy(1,44) platform(linux) == text-masking.yaml text-masking-subpx.png +platform(linux) == subpixel-rotate.yaml subpixel-rotate.png +platform(linux) == subpixel-scale.yaml subpixel-scale.png +platform(linux) == subpixel-skew.yaml subpixel-skew.png diff --git a/wrench/reftests/text/subpixel-rotate.png b/wrench/reftests/text/subpixel-rotate.png new file mode 100644 index 0000000000..caaedb0689 Binary files /dev/null and b/wrench/reftests/text/subpixel-rotate.png differ diff --git a/wrench/reftests/text/subpixel-rotate.yaml b/wrench/reftests/text/subpixel-rotate.yaml new file mode 100644 index 0000000000..1edcdbd8d2 --- /dev/null +++ b/wrench/reftests/text/subpixel-rotate.yaml @@ -0,0 +1,10 @@ +root: + items: + - type: stacking-context + bounds: [0, 0, 430, 330] + transform: rotate(30) + items: + - text: "a Bcd Efgh Ijklm Nopqrs Tuvwxyz" + origin: 50 200 + size: 20 + font: "FreeSans.ttf" diff --git a/wrench/reftests/text/subpixel-scale.png b/wrench/reftests/text/subpixel-scale.png new file mode 100644 index 0000000000..245c7cb2ec Binary files /dev/null and b/wrench/reftests/text/subpixel-scale.png differ diff --git a/wrench/reftests/text/subpixel-scale.yaml b/wrench/reftests/text/subpixel-scale.yaml new file mode 100644 index 0000000000..84e7b72bfd --- /dev/null +++ b/wrench/reftests/text/subpixel-scale.yaml @@ -0,0 +1,10 @@ +root: + items: + - type: stacking-context + bounds: [0, 0, 680, 80] + transform: scale-x(1.5) + items: + - text: "a Bcd Efgh Ijklm Nopqrs Tuvwxyz" + origin: 20 50 + size: 20 + font: "FreeSans.ttf" diff --git a/wrench/reftests/text/subpixel-skew.png b/wrench/reftests/text/subpixel-skew.png new file mode 100644 index 0000000000..a261dc336b Binary files /dev/null and b/wrench/reftests/text/subpixel-skew.png differ diff --git a/wrench/reftests/text/subpixel-skew.yaml b/wrench/reftests/text/subpixel-skew.yaml new file mode 100644 index 0000000000..7d06f722b2 --- /dev/null +++ b/wrench/reftests/text/subpixel-skew.yaml @@ -0,0 +1,10 @@ +root: + items: + - type: stacking-context + bounds: [0, 0, 480, 80] + transform: skew-x(30) + items: + - text: "a Bcd Efgh Ijklm Nopqrs Tuvwxyz" + origin: 20 50 + size: 20 + font: "FreeSans.ttf" diff --git a/wrench/src/yaml_helper.rs b/wrench/src/yaml_helper.rs index 709c00a9bc..de9e8b16fa 100644 --- a/wrench/src/yaml_helper.rs +++ b/wrench/src/yaml_helper.rs @@ -158,6 +158,15 @@ fn make_rotation( pre_transform.pre_mul(&transform).pre_mul(&post_transform) } +// Create a skew matrix, specified in degrees. +fn make_skew( + skew_x: f32, + skew_y: f32, +) -> LayoutTransform { + let alpha = Radians::new(skew_x.to_radians()); + let beta = Radians::new(skew_y.to_radians()); + LayoutTransform::create_skew(alpha, beta) +} impl YamlHelper for Yaml { fn as_f32(&self) -> Option { @@ -335,6 +344,34 @@ impl YamlHelper for Yaml { "rotate-y" if args.len() == 1 => { make_rotation(transform_origin, args[0].parse().unwrap(), 0.0, 1.0, 0.0) } + "scale" if args.len() >= 1 => { + let x = args[0].parse().unwrap(); + // Default to uniform X/Y scale if Y unspecified. + let y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(x); + // Default to no Z scale if unspecified. + let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(1.0); + LayoutTransform::create_scale(x, y, z) + } + "scale-x" if args.len() == 1 => { + LayoutTransform::create_scale(args[0].parse().unwrap(), 1.0, 1.0) + } + "scale-y" if args.len() == 1 => { + LayoutTransform::create_scale(1.0, args[0].parse().unwrap(), 1.0) + } + "scale-z" if args.len() == 1 => { + LayoutTransform::create_scale(1.0, 1.0, args[0].parse().unwrap()) + } + "skew" if args.len() >= 1 => { + // Default to no Y skew if unspecified. + let skew_y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(0.0); + make_skew(args[0].parse().unwrap(), skew_y) + } + "skew-x" if args.len() == 1 => { + make_skew(args[0].parse().unwrap(), 0.0) + } + "skew-y" if args.len() == 1 => { + make_skew(0.0, args[0].parse().unwrap()) + } "perspective" if args.len() == 1 => { LayoutTransform::create_perspective(args[0].parse().unwrap()) }