diff --git a/components/derive_common/cg.rs b/components/derive_common/cg.rs index 23165e7b622..106cea95c9d 100644 --- a/components/derive_common/cg.rs +++ b/components/derive_common/cg.rs @@ -143,7 +143,10 @@ pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: & let ident = &data.ident; GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output)) }, - ref arg => panic!("arguments {:?} cannot be mapped yet", arg), + &GenericParam::Const(ref inner) => { + let ident = &inner.ident; + GenericArgument::Const(parse_quote!(#ident)) + }, }) .collect(), colon2_token: Default::default(), diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 5016f91182c..87d5ac23a34 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -12,14 +12,14 @@ use crate::parser::{Parse, ParserContext}; use crate::properties::longhands::font_language_override; use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; -use crate::values::computed::font::FamilyName; +use crate::values::computed::font::{FamilyName, FontStretch}; use crate::values::generics::font::FontStyle as GenericFontStyle; #[cfg(feature = "gecko")] use crate::values::specified::font::SpecifiedFontFeatureSettings; use crate::values::specified::font::SpecifiedFontStyle; #[cfg(feature = "gecko")] use crate::values::specified::font::SpecifiedFontVariationSettings; -use crate::values::specified::font::{AbsoluteFontWeight, FontStretch}; +use crate::values::specified::font::{AbsoluteFontWeight, FontStretch as SpecifiedFontStretch}; #[cfg(feature = "gecko")] use crate::values::specified::font::MetricsOverride; use crate::values::specified::url::SpecifiedUrl; @@ -174,7 +174,7 @@ fn sort_range(a: T, b: T) -> (T, T) { impl FontWeightRange { /// Returns a computed font-stretch range. pub fn compute(&self) -> ComputedFontWeightRange { - let (min, max) = sort_range(self.0.compute().0, self.1.compute().0); + let (min, max) = sort_range(self.0.compute().value(), self.1.compute().value()); ComputedFontWeightRange(min, max) } } @@ -183,23 +183,23 @@ impl FontWeightRange { /// /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-stretch #[derive(Clone, Debug, PartialEq, ToShmem)] -pub struct FontStretchRange(pub FontStretch, pub FontStretch); -impl_range!(FontStretchRange, FontStretch); +pub struct FontStretchRange(pub SpecifiedFontStretch, pub SpecifiedFontStretch); +impl_range!(FontStretchRange, SpecifiedFontStretch); -/// The computed representation of the above, so that -/// Gecko can read them easily. +/// The computed representation of the above, so that Gecko can read them +/// easily. #[repr(C)] #[allow(missing_docs)] -pub struct ComputedFontStretchRange(f32, f32); +pub struct ComputedFontStretchRange(FontStretch, FontStretch); impl FontStretchRange { /// Returns a computed font-stretch range. pub fn compute(&self) -> ComputedFontStretchRange { - fn compute_stretch(s: &FontStretch) -> f32 { + fn compute_stretch(s: &SpecifiedFontStretch) -> FontStretch { match *s { - FontStretch::Keyword(ref kw) => kw.compute().0, - FontStretch::Stretch(ref p) => p.0.get(), - FontStretch::System(..) => unreachable!(), + SpecifiedFontStretch::Keyword(ref kw) => kw.compute(), + SpecifiedFontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()), + SpecifiedFontStretch::System(..) => unreachable!(), } } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 9588d0e6147..c3c7e4523da 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -895,57 +895,9 @@ fn static_assert() { } } - pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) { - unsafe { bindings::Gecko_FontWeight_SetFloat(&mut self.gecko.mFont.weight, v.0) }; - } - ${impl_simple_copy('font_weight', 'mFont.weight')} - - pub fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T { - let weight: f32 = unsafe { - bindings::Gecko_FontWeight_ToFloat(self.gecko.mFont.weight) - }; - longhands::font_weight::computed_value::T(weight) - } - - pub fn set_font_stretch(&mut self, v: longhands::font_stretch::computed_value::T) { - unsafe { - bindings::Gecko_FontStretch_SetFloat( - &mut self.gecko.mFont.stretch, - v.value(), - ) - }; - } - ${impl_simple_copy('font_stretch', 'mFont.stretch')} - pub fn clone_font_stretch(&self) -> longhands::font_stretch::computed_value::T { - use crate::values::computed::font::FontStretch; - use crate::values::computed::Percentage; - use crate::values::generics::NonNegative; - - let stretch = - unsafe { bindings::Gecko_FontStretch_ToFloat(self.gecko.mFont.stretch) }; - debug_assert!(stretch >= 0.); - - FontStretch(NonNegative(Percentage(stretch))) - } - - pub fn set_font_style(&mut self, v: longhands::font_style::computed_value::T) { - use crate::values::generics::font::FontStyle; - let s = &mut self.gecko.mFont.style; - unsafe { - match v { - FontStyle::Normal => bindings::Gecko_FontSlantStyle_SetNormal(s), - FontStyle::Italic => bindings::Gecko_FontSlantStyle_SetItalic(s), - FontStyle::Oblique(ref angle) => { - bindings::Gecko_FontSlantStyle_SetOblique(s, angle.0.degrees()) - } - } - } - } - ${impl_simple_copy('font_style', 'mFont.style')} - pub fn clone_font_style(&self) -> longhands::font_style::computed_value::T { - use crate::values::computed::font::FontStyle; - FontStyle::from_gecko(self.gecko.mFont.style) - } + ${impl_simple('font_weight', 'mFont.weight')} + ${impl_simple('font_stretch', 'mFont.stretch')} + ${impl_simple('font_style', 'mFont.style')} ${impl_simple_type_with_conversion("font_synthesis", "mFont.synthesis")} diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs index 8c296980fe7..322a82e00a4 100644 --- a/components/style/properties/longhands/font.mako.rs +++ b/components/style/properties/longhands/font.mako.rs @@ -352,11 +352,10 @@ pub mod system_font { fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue { use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs::nsFont; - use std::mem; - use crate::values::computed::Percentage; + use crate::values::computed::font::FontSize; use crate::values::specified::font::KeywordInfo; - use crate::values::computed::font::{FontSize, FontStretch, FontStyle}; use crate::values::generics::NonNegative; + use std::mem; let mut system = mem::MaybeUninit::::uninit(); let system = unsafe { @@ -368,20 +367,15 @@ pub mod system_font { ); &mut *system.as_mut_ptr() }; - let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight); - let font_stretch = FontStretch(NonNegative(Percentage(unsafe { - bindings::Gecko_FontStretch_ToFloat(system.stretch) - }))); - let font_style = FontStyle::from_gecko(system.style); let ret = ComputedSystemFont { font_family: system.family.clone(), font_size: FontSize { size: NonNegative(cx.maybe_zoom_text(system.size.0)), keyword_info: KeywordInfo::none() }, - font_weight, - font_stretch, - font_style, + font_weight: system.weight, + font_stretch: system.stretch, + font_style: system.style, font_size_adjust: system.sizeAdjust, % for kwprop in kw_font_props: ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword( diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index 660bdd6045b..9ad47db0ab4 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -353,12 +353,11 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { #[cfg(feature = "gecko")] fn adjust_for_mathvariant(&mut self) { use crate::properties::longhands::_moz_math_variant::computed_value::T as MozMathVariant; - use crate::properties::longhands::font_weight::computed_value::T as FontWeight; - use crate::values::generics::font::FontStyle; + use crate::values::computed::font::{FontWeight, FontStyle}; if self.style.get_font().clone__moz_math_variant() != MozMathVariant::None { let font_style = self.style.mutate_font(); - font_style.set_font_weight(FontWeight::normal()); - font_style.set_font_style(FontStyle::Normal); + font_style.set_font_weight(FontWeight::NORMAL); + font_style.set_font_style(FontStyle::NORMAL); } } diff --git a/components/style/values/animated/font.rs b/components/style/values/animated/font.rs index 1f0eb749fc7..f890a3b2bd3 100644 --- a/components/style/values/animated/font.rs +++ b/components/style/values/animated/font.rs @@ -5,18 +5,11 @@ //! Animation implementation for various font-related types. use super::{Animate, Procedure, ToAnimatedZero}; -use crate::values::computed::font::{FontVariationSettings, FontWeight}; +use crate::values::computed::font::FontVariationSettings; use crate::values::computed::Number; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::font::{FontSettings as GenericFontSettings, FontTag, VariationValue}; -impl ToAnimatedZero for FontWeight { - #[inline] - fn to_animated_zero(&self) -> Result { - Ok(FontWeight::normal()) - } -} - /// impl Animate for FontVariationSettings { #[inline] diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 275bbbc0eba..9f8176d0fc6 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -4,27 +4,23 @@ //! Computed values for font properties -#[cfg(feature = "gecko")] -use crate::gecko_bindings::{bindings, structs}; use crate::parser::{Parse, ParserContext}; use crate::values::animated::ToAnimatedValue; use crate::values::computed::{ - Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, NonNegativePercentage, + Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, + Number, Percentage, ToComputedValue }; -use crate::values::computed::{Number, Percentage, ToComputedValue}; use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue}; use crate::values::generics::{font as generics, NonNegative}; use crate::values::specified::font::{ self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT, }; use crate::values::specified::length::{FontBaseSize, NoCalcLength}; -use crate::values::CSSFloat; use crate::Atom; use cssparser::{serialize_identifier, CssStringWriter, Parser}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use std::fmt::{self, Write}; -use std::hash::{Hash, Hasher}; use style_traits::{CssWriter, ParseError, ToCss}; pub use crate::values::computed::Length as MozScriptMinSize; @@ -32,34 +28,159 @@ pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier} pub use crate::values::specified::font::{XLang, XTextZoom}; pub use crate::values::specified::Integer as SpecifiedInteger; +/// Generic template for font property type classes that use a fixed-point +/// internal representation with `FRACTION_BITS` for the fractional part. +/// +/// Values are constructed from and exposed as floating-point, but stored +/// internally as fixed point, so there will be a quantization effect on +/// fractional values, depending on the number of fractional bits used. +/// +/// Using (16-bit) fixed-point types rather than floats for these style +/// attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle; it +/// will also tend to reduce the number of distinct font instances that get +/// created, particularly when styles are animated or set to arbitrary values +/// (e.g. by sliders in the UI), which should reduce pressure on graphics +/// resources and improve cache hit rates. +/// +/// cbindgen:derive-lt +/// cbindgen:derive-lte +/// cbindgen:derive-gt +/// cbindgen:derive-gte +#[repr(C)] +#[derive( + Clone, ComputeSquaredDistance, Copy, Debug, Hash, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue, +)] +pub struct FixedPoint { + value: T, +} + +impl FixedPoint +where + T: num_traits::cast::AsPrimitive, + f32: num_traits::cast::AsPrimitive, +{ + const SCALE: u16 = 1 << FRACTION_BITS; + const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32; + + /// Returns a fixed-point bit from a floating-point context. + fn from_float(v: f32) -> Self { + use num_traits::cast::AsPrimitive; + Self { + value: (v * Self::SCALE as f32).round().as_(), + } + } + + /// Returns the floating-point representation. + fn to_float(&self) -> f32 { + self.value.as_() * Self::INVERSE_SCALE + } +} + +/// font-weight: range 1..1000, fractional values permitted; keywords +/// 'normal', 'bold' aliased to 400, 700 respectively. +/// +/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375) +pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6; + +/// This is an alias which is useful mostly as a cbindgen / C++ inference +/// workaround. +pub type FontWeightFixedPoint = FixedPoint; + /// A value for the font-weight property per: /// /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight /// -/// This is effectively just a `Number`. +/// cbindgen:derive-lt +/// cbindgen:derive-lte +/// cbindgen:derive-gt +/// cbindgen:derive-gte #[derive( - Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, + Clone, ComputeSquaredDistance, Copy, Debug, Hash, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -pub struct FontWeight(pub Number); - -impl Hash for FontWeight { - fn hash(&self, hasher: &mut H) { - hasher.write_u64((self.0 * 10000.).trunc() as u64); - } -} - +#[repr(C)] +pub struct FontWeight(FontWeightFixedPoint); impl ToAnimatedValue for FontWeight { type AnimatedValue = Number; #[inline] fn to_animated_value(self) -> Self::AnimatedValue { - self.0 + self.value() } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { - FontWeight(animated.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT)) + FontWeight::from_float(animated) + } +} + +impl ToCss for FontWeight { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write, + { + self.value().to_css(dest) + } +} + +impl FontWeight { + /// The `normal` keyword. + pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint { value: 400 << FONT_WEIGHT_FRACTION_BITS }); + + /// The `bold` value. + pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint { value: 700 << FONT_WEIGHT_FRACTION_BITS }); + + /// The threshold from which we consider a font bold. + pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint { value: 600 << FONT_WEIGHT_FRACTION_BITS }); + + /// Returns the `normal` keyword value. + pub fn normal() -> Self { + Self::NORMAL + } + + /// Weither this weight is bold + pub fn is_bold(&self) -> bool { + *self >= Self::BOLD_THRESHOLD + } + + /// Returns the value as a float. + pub fn value(&self) -> f32 { + self.0.to_float() + } + + /// Construct a valid weight from a float value. + pub fn from_float(v: f32) -> Self { + Self(FixedPoint::from_float(v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT))) + } + + /// Return the bolder weight. + /// + /// See the table in: + /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values + pub fn bolder(self) -> Self { + let value = self.value(); + if value < 350. { + return Self::NORMAL; + } + if value < 550. { + return Self::BOLD; + } + Self::from_float(value.max(900.)) + } + + /// Return the lighter weight. + /// + /// See the table in: + /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values + pub fn lighter(self) -> Self { + let value = self.value(); + if value < 550. { + return Self::from_float(value.min(100.)) + } + if value < 750. { + return Self::NORMAL; + } + Self::BOLD } } @@ -85,60 +206,6 @@ pub struct FontSize { pub keyword_info: KeywordInfo, } -impl FontWeight { - /// Value for normal - pub fn normal() -> Self { - FontWeight(400.) - } - - /// Value for bold - pub fn bold() -> Self { - FontWeight(700.) - } - - /// Convert from an Gecko weight - #[cfg(feature = "gecko")] - pub fn from_gecko_weight(weight: structs::FontWeight) -> Self { - // we allow a wider range of weights than is parseable - // because system fonts may provide custom values - let weight = unsafe { bindings::Gecko_FontWeight_ToFloat(weight) }; - FontWeight(weight) - } - - /// Weither this weight is bold - pub fn is_bold(&self) -> bool { - self.0 > 500. - } - - /// Return the bolder weight. - /// - /// See the table in: - /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values - pub fn bolder(self) -> Self { - if self.0 < 350. { - FontWeight(400.) - } else if self.0 < 550. { - FontWeight(700.) - } else { - FontWeight(self.0.max(900.)) - } - } - - /// Return the lighter weight. - /// - /// See the table in: - /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values - pub fn lighter(self) -> Self { - if self.0 < 550. { - FontWeight(self.0.min(100.)) - } else if self.0 < 750. { - FontWeight(400.) - } else { - FontWeight(700.) - } - } -} - impl FontSize { /// The actual computed font size. #[inline] @@ -812,76 +879,66 @@ impl ToComputedValue for specified::MathDepth { } } -/// A wrapper over an `Angle`, that handles clamping to the appropriate range -/// for `font-style` animation. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -pub struct FontStyleAngle(pub Angle); +/// - Use a signed 8.8 fixed-point value (representable range -128.0..128) +/// +/// Values of below -90 or above 90 not permitted, so we use out of +/// range values to represent normal | oblique +pub const FONT_STYLE_FRACTION_BITS: u16 = 8; -impl ToAnimatedValue for FontStyleAngle { - type AnimatedValue = Angle; - - #[inline] - fn to_animated_value(self) -> Self::AnimatedValue { - self.0 - } - - #[inline] - fn from_animated_value(animated: Self::AnimatedValue) -> Self { - FontStyleAngle(Angle::from_degrees( - animated - .degrees() - .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES) - .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES), - )) - } -} - -impl Hash for FontStyleAngle { - fn hash(&self, hasher: &mut H) { - hasher.write_u64((self.0.degrees() * 10000.).trunc() as u64); - } -} +/// This is an alias which is useful mostly as a cbindgen / C++ inference +/// workaround. +pub type FontStyleFixedPoint = FixedPoint; /// The computed value of `font-style`. /// -/// FIXME(emilio): Angle should be a custom type to handle clamping during -/// animation. -pub type FontStyle = generics::FontStyle; +/// - Define out of range values min value (-128.0) as meaning 'normal' +/// - Define max value (127.99609375) as 'italic' +/// - Other values represent 'oblique ' +/// - Note that 'oblique 0deg' is distinct from 'normal' (should it be?) +/// +/// cbindgen:derive-lt +/// cbindgen:derive-lte +/// cbindgen:derive-gt +/// cbindgen:derive-gte +#[derive( + Clone, ComputeSquaredDistance, Copy, Debug, Hash, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue, +)] +#[repr(C)] +pub struct FontStyle(FontStyleFixedPoint); impl FontStyle { + /// The normal keyword. + pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint { value: 100 << FONT_STYLE_FRACTION_BITS }); + /// The italic keyword. + pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint { value: 101 << FONT_STYLE_FRACTION_BITS }); + + /// The default angle for `font-style: oblique`. + /// See also https://github.com/w3c/csswg-drafts/issues/2295 + pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14; + + /// The `oblique` keyword with the default degrees. + pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint { value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS }); + /// The `normal` value. #[inline] pub fn normal() -> Self { - generics::FontStyle::Normal + Self::NORMAL } - /// The default angle for font-style: oblique. This is 20deg per spec: - /// - /// https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle - #[inline] - pub fn default_angle() -> FontStyleAngle { - FontStyleAngle(Angle::from_degrees( - specified::DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES, + /// Returns the oblique angle for this style. + pub fn oblique(degrees: f32) -> Self { + Self(FixedPoint::from_float( + degrees + .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES) + .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES) )) } - /// Get the font style from Gecko's nsFont struct. - #[cfg(feature = "gecko")] - pub fn from_gecko(style: structs::FontSlantStyle) -> Self { - let mut angle = 0.; - let mut italic = false; - let mut normal = false; - unsafe { - bindings::Gecko_FontSlantStyle_Get(style, &mut normal, &mut italic, &mut angle); - } - if normal { - return generics::FontStyle::Normal; - } - if italic { - return generics::FontStyle::Italic; - } - generics::FontStyle::Oblique(FontStyleAngle(Angle::from_degrees(angle))) + /// Returns the oblique angle for this style. + pub fn oblique_degrees(&self) -> f32 { + debug_assert_ne!(*self, Self::NORMAL); + debug_assert_ne!(*self, Self::ITALIC); + self.0.to_float() } } @@ -890,43 +947,173 @@ impl ToCss for FontStyle { where W: fmt::Write, { - match *self { - generics::FontStyle::Normal => dest.write_str("normal"), - generics::FontStyle::Italic => dest.write_str("italic"), - generics::FontStyle::Oblique(ref angle) => { - dest.write_str("oblique")?; - // Use `degrees` instead of just comparing Angle because - // `degrees` can return slightly different values due to - // floating point conversions. - if angle.0.degrees() != Self::default_angle().0.degrees() { - dest.write_char(' ')?; - angle.to_css(dest)?; - } - Ok(()) - }, + if *self == Self::NORMAL { + return dest.write_str("normal") + } + if *self == Self::ITALIC { + return dest.write_str("italic") + } + if *self == Self::OBLIQUE { + return dest.write_str("oblique"); + } + dest.write_str("oblique ")?; + let angle = Angle::from_degrees(self.oblique_degrees()); + angle.to_css(dest)?; + Ok(()) + } +} + +impl ToAnimatedValue for FontStyle { + type AnimatedValue = generics::FontStyle; + + #[inline] + fn to_animated_value(self) -> Self::AnimatedValue { + if self == Self::NORMAL { + return generics::FontStyle::Normal + } + if self == Self::ITALIC { + return generics::FontStyle::Italic + } + generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees())) + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + match animated { + generics::FontStyle::Normal => Self::NORMAL, + generics::FontStyle::Italic => Self::ITALIC, + generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()), } } } + +/// font-stretch is a percentage relative to normal. +/// +/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375) +/// +/// We arbitrarily limit here to 1000%. (If that becomes a problem, we could +/// reduce the number of fractional bits and increase the limit.) +pub const FONT_STRETCH_FRACTION_BITS: u16 = 6; + +/// This is an alias which is useful mostly as a cbindgen / C++ inference +/// workaround. +pub type FontStretchFixedPoint = FixedPoint; + + /// A value for the font-stretch property per: /// /// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch +/// +/// cbindgen:derive-lt +/// cbindgen:derive-lte +/// cbindgen:derive-gt +/// cbindgen:derive-gte #[derive( - Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, + Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue, )] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -pub struct FontStretch(pub NonNegativePercentage); +#[repr(C)] +pub struct FontStretch(pub FontStretchFixedPoint); impl FontStretch { + /// The fraction bits, as an easy-to-access-constant. + pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS; + /// 0.5 in our floating point representation. + pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1); + + /// The `ultra-condensed` keyword. + pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { value: 50 << Self::FRACTION_BITS }); + /// The `extra-condensed` keyword. + pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { value: (62 << Self::FRACTION_BITS) + Self::HALF }); + /// The `condensed` keyword. + pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { value: 75 << Self::FRACTION_BITS }); + /// The `semi-condensed` keyword. + pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint { value: (87 << Self::FRACTION_BITS) + Self::HALF }); + /// The `normal` keyword. + pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint { value: 100 << Self::FRACTION_BITS }); + /// The `semi-expanded` keyword. + pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { value: (112 << Self::FRACTION_BITS) + Self::HALF }); + /// The `expanded` keyword. + pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { value: 125 << Self::FRACTION_BITS }); + /// The `extra-expanded` keyword. + pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { value: 150 << Self::FRACTION_BITS }); + /// The `ultra-expanded` keyword. + pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint { value: 200 << Self::FRACTION_BITS }); + /// 100% pub fn hundred() -> Self { - FontStretch(NonNegativePercentage::hundred()) + Self::NORMAL } - /// The float value of the percentage + /// Converts to a computed percentage. #[inline] - pub fn value(&self) -> CSSFloat { - ((self.0).0).0 + pub fn to_percentage(&self) -> Percentage { + Percentage(self.0.to_float() / 100.0) + } + + /// Converts from a computed percentage value. + pub fn from_percentage(p: f32) -> Self { + Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0))) + } + + /// Returns a relevant stretch value from a keyword. + /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop + pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self { + use specified::FontStretchKeyword::*; + match kw { + UltraCondensed => Self::ULTRA_CONDENSED, + ExtraCondensed => Self::EXTRA_CONDENSED, + Condensed => Self::CONDENSED, + SemiCondensed => Self::SEMI_CONDENSED, + Normal => Self::NORMAL, + SemiExpanded => Self::SEMI_EXPANDED, + Expanded => Self::EXPANDED, + ExtraExpanded => Self::EXTRA_EXPANDED, + UltraExpanded => Self::ULTRA_EXPANDED, + } + } + + /// Returns the stretch keyword if we map to one of the relevant values. + pub fn as_keyword(&self) -> Option { + use specified::FontStretchKeyword::*; + // TODO: Can we use match here? + if *self == Self::ULTRA_CONDENSED { + return Some(UltraCondensed); + } + if *self == Self::EXTRA_CONDENSED { + return Some(ExtraCondensed); + } + if *self == Self::CONDENSED { + return Some(Condensed); + } + if *self == Self::SEMI_CONDENSED { + return Some(SemiCondensed); + } + if *self == Self::NORMAL { + return Some(Normal); + } + if *self == Self::SEMI_EXPANDED { + return Some(SemiExpanded); + } + if *self == Self::EXPANDED { + return Some(Expanded); + } + if *self == Self::EXTRA_EXPANDED { + return Some(ExtraExpanded); + } + if *self == Self::ULTRA_EXPANDED { + return Some(UltraExpanded); + } + None + } +} + +impl ToCss for FontStretch { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write, + { + self.to_percentage().to_css(dest) } } @@ -935,17 +1122,11 @@ impl ToAnimatedValue for FontStretch { #[inline] fn to_animated_value(self) -> Self::AnimatedValue { - self.0.to_animated_value() + self.to_percentage() } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { - FontStretch(NonNegativePercentage::from_animated_value(animated)) - } -} - -impl Hash for FontStretch { - fn hash(&self, hasher: &mut H) { - hasher.write_u64((self.value() * 10000.).trunc() as u64); + Self::from_percentage(animated.0) } } diff --git a/components/style/values/distance.rs b/components/style/values/distance.rs index a4259ce8c6b..fef376cf5f7 100644 --- a/components/style/values/distance.rs +++ b/components/style/values/distance.rs @@ -50,6 +50,13 @@ impl ComputeSquaredDistance for u16 { } } +impl ComputeSquaredDistance for i16 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64)) + } +} + impl ComputeSquaredDistance for i32 { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { diff --git a/components/style/values/resolved/mod.rs b/components/style/values/resolved/mod.rs index 4ec84c2ae14..817585002bb 100644 --- a/components/style/values/resolved/mod.rs +++ b/components/style/values/resolved/mod.rs @@ -63,11 +63,12 @@ macro_rules! trivial_to_resolved_value { trivial_to_resolved_value!(()); trivial_to_resolved_value!(bool); trivial_to_resolved_value!(f32); -trivial_to_resolved_value!(i32); trivial_to_resolved_value!(u8); trivial_to_resolved_value!(i8); trivial_to_resolved_value!(u16); +trivial_to_resolved_value!(i16); trivial_to_resolved_value!(u32); +trivial_to_resolved_value!(i32); trivial_to_resolved_value!(usize); trivial_to_resolved_value!(String); trivial_to_resolved_value!(Box); diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 40a559a20be..9701f3f5542 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -7,10 +7,10 @@ #[cfg(feature = "gecko")] use crate::context::QuirksMode; use crate::parser::{Parse, ParserContext}; -use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily}; +use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily}; use crate::values::computed::FontSizeAdjust as ComputedFontSizeAdjust; use crate::values::computed::{font as computed, Length, NonNegativeLength}; -use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage}; +use crate::values::computed::{Percentage as ComputedPercentage}; use crate::values::computed::{CSSPixelLength, Context, ToComputedValue}; use crate::values::generics::font::VariationValue; use crate::values::generics::font::{ @@ -185,7 +185,7 @@ impl ToComputedValue for FontWeight { #[inline] fn from_computed_value(computed: &computed::FontWeight) -> Self { FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value( - &computed.0, + &computed.value(), ))) } } @@ -210,10 +210,10 @@ impl AbsoluteFontWeight { pub fn compute(&self) -> computed::FontWeight { match *self { AbsoluteFontWeight::Weight(weight) => { - computed::FontWeight(weight.get().max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT)) + computed::FontWeight::from_float(weight.get()) }, - AbsoluteFontWeight::Normal => computed::FontWeight::normal(), - AbsoluteFontWeight::Bold => computed::FontWeight::bold(), + AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL, + AbsoluteFontWeight::Bold => computed::FontWeight::BOLD, } } } @@ -289,33 +289,24 @@ impl ToComputedValue for SpecifiedFontStyle { fn to_computed_value(&self, _: &Context) -> Self::ComputedValue { match *self { - generics::FontStyle::Normal => generics::FontStyle::Normal, - generics::FontStyle::Italic => generics::FontStyle::Italic, - generics::FontStyle::Oblique(ref angle) => { - generics::FontStyle::Oblique(FontStyleAngle(Self::compute_angle(angle))) - }, + Self::Normal => computed::FontStyle::NORMAL, + Self::Italic => computed::FontStyle::ITALIC, + Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()), } } fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - generics::FontStyle::Normal => generics::FontStyle::Normal, - generics::FontStyle::Italic => generics::FontStyle::Italic, - generics::FontStyle::Oblique(ref angle) => { - generics::FontStyle::Oblique(Angle::from_computed_value(&angle.0)) - }, + if *computed == computed::FontStyle::NORMAL { + return Self::Normal; } + if *computed == computed::FontStyle::ITALIC { + return Self::Italic; + } + let degrees = computed.oblique_degrees(); + generics::FontStyle::Oblique(Angle::from_degrees(degrees, /* was_calc = */ false)) } } -/// The default angle for `font-style: oblique`. -/// -/// NOTE(emilio): As of right now this diverges from the spec, which specifies -/// 20, because it's not updated yet to account for the resolution in: -/// -/// https://github.com/w3c/csswg-drafts/issues/2295 -pub const DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES: f32 = 14.; - /// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle: /// /// Values less than -90deg or values greater than 90deg are @@ -336,10 +327,6 @@ impl SpecifiedFontStyle { .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES) } - fn compute_angle(angle: &Angle) -> ComputedAngle { - ComputedAngle::from_degrees(Self::compute_angle_degrees(angle)) - } - /// Parse a suitable angle for font-style: oblique. pub fn parse_angle<'i, 't>( context: &ParserContext, @@ -362,7 +349,7 @@ impl SpecifiedFontStyle { /// The default angle for `font-style: oblique`. pub fn default_angle() -> Angle { Angle::from_degrees( - DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES, + computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32, /* was_calc = */ false, ) } @@ -411,7 +398,6 @@ impl ToComputedValue for FontStyle { #[derive( Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, )] -#[repr(u8)] pub enum FontStretch { Stretch(NonNegativePercentage), Keyword(FontStretchKeyword), @@ -437,57 +423,15 @@ pub enum FontStretchKeyword { } impl FontStretchKeyword { - /// Resolves the value of the keyword as specified in: - /// - /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop - pub fn compute(&self) -> ComputedPercentage { - use self::FontStretchKeyword::*; - ComputedPercentage(match *self { - UltraCondensed => 0.5, - ExtraCondensed => 0.625, - Condensed => 0.75, - SemiCondensed => 0.875, - Normal => 1., - SemiExpanded => 1.125, - Expanded => 1.25, - ExtraExpanded => 1.5, - UltraExpanded => 2., - }) + /// Turns the keyword into a computed value. + pub fn compute(&self) -> computed::FontStretch { + computed::FontStretch::from_keyword(*self) } /// Does the opposite operation to `compute`, in order to serialize keywords /// if possible. - pub fn from_percentage(percentage: f32) -> Option { - use self::FontStretchKeyword::*; - // NOTE(emilio): Can't use `match` because of rust-lang/rust#41620. - if percentage == 0.5 { - return Some(UltraCondensed); - } - if percentage == 0.625 { - return Some(ExtraCondensed); - } - if percentage == 0.75 { - return Some(Condensed); - } - if percentage == 0.875 { - return Some(SemiCondensed); - } - if percentage == 1. { - return Some(Normal); - } - if percentage == 1.125 { - return Some(SemiExpanded); - } - if percentage == 1.25 { - return Some(Expanded); - } - if percentage == 1.5 { - return Some(ExtraExpanded); - } - if percentage == 2. { - return Some(UltraExpanded); - } - None + pub fn from_percentage(p: f32) -> Option { + computed::FontStretch::from_percentage(p).as_keyword() } } @@ -506,16 +450,17 @@ impl ToComputedValue for FontStretch { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { FontStretch::Stretch(ref percentage) => { - computed::FontStretch(percentage.to_computed_value(context)) + let percentage = percentage.to_computed_value(context).0; + computed::FontStretch::from_percentage(percentage.0) }, - FontStretch::Keyword(ref kw) => computed::FontStretch(NonNegative(kw.compute())), + FontStretch::Keyword(ref kw) => kw.compute(), FontStretch::System(_) => self.compute_system(context), } } fn from_computed_value(computed: &Self::ComputedValue) -> Self { FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative( - (computed.0).0, + computed.to_percentage() ))) } }