style: Move fixed-point font types to Rust

Now that cbindgen and rust support const generics, it seems more simple.

This centralizes all the relevant font constants etc in rust and avoids
conversions when going from rust to C++ and vice versa.

Differential Revision: https://phabricator.services.mozilla.com/D148847
This commit is contained in:
Emilio Cobos Álvarez 2022-06-13 00:59:23 +00:00 committed by Martin Robinson
parent 48749641d3
commit dcafbde256
10 changed files with 401 additions and 326 deletions

View file

@ -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(),

View file

@ -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<T: PartialOrd>(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!(),
}
}

View file

@ -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")}

View file

@ -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::<nsFont>::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(

View file

@ -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);
}
}

View file

@ -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<Self, ()> {
Ok(FontWeight::normal())
}
}
/// <https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def>
impl Animate for FontVariationSettings {
#[inline]

View file

@ -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<T, const FRACTION_BITS: u16> {
value: T,
}
impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
where
T: num_traits::cast::AsPrimitive<f32>,
f32: num_traits::cast::AsPrimitive<T>,
{
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<u16, FONT_WEIGHT_FRACTION_BITS>;
/// 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<H: Hasher>(&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<W>(&self, dest: &mut CssWriter<W>) -> 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 <angle> 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<H: Hasher>(&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<i16, FONT_STYLE_FRACTION_BITS>;
/// The computed value of `font-style`.
///
/// FIXME(emilio): Angle should be a custom type to handle clamping during
/// animation.
pub type FontStyle = generics::FontStyle<FontStyleAngle>;
/// - Define out of range values min value (-128.0) as meaning 'normal'
/// - Define max value (127.99609375) as 'italic'
/// - Other values represent 'oblique <angle>'
/// - 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) => {
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 ")?;
// 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(' ')?;
let angle = Angle::from_degrees(self.oblique_degrees());
angle.to_css(dest)?;
}
Ok(())
},
}
}
impl ToAnimatedValue for FontStyle {
type AnimatedValue = generics::FontStyle<Angle>;
#[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<u16, FONT_STRETCH_FRACTION_BITS>;
/// 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<specified::FontStretchKeyword> {
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<W>(&self, dest: &mut CssWriter<W>) -> 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<H: Hasher>(&self, hasher: &mut H) {
hasher.write_u64((self.value() * 10000.).trunc() as u64);
Self::from_percentage(animated.0)
}
}

View file

@ -50,6 +50,13 @@ impl ComputeSquaredDistance for u16 {
}
}
impl ComputeSquaredDistance for i16 {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))
}
}
impl ComputeSquaredDistance for i32 {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {

View file

@ -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<str>);

View file

@ -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,32 +289,23 @@ 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:
///
@ -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<Self> {
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<Self> {
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()
)))
}
}