diff options
Diffstat (limited to 'servo/components/style/values/computed/image.rs')
-rw-r--r-- | servo/components/style/values/computed/image.rs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/servo/components/style/values/computed/image.rs b/servo/components/style/values/computed/image.rs new file mode 100644 index 0000000000..266caca845 --- /dev/null +++ b/servo/components/style/values/computed/image.rs @@ -0,0 +1,209 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! CSS handling for the computed value of +//! [`image`][image]s +//! +//! [image]: https://drafts.csswg.org/css-images/#image-values + +use crate::values::computed::percentage::Percentage; +use crate::values::computed::position::Position; +use crate::values::computed::url::ComputedImageUrl; +#[cfg(feature = "gecko")] +use crate::values::computed::NumberOrPercentage; +use crate::values::computed::{Angle, Color, Context}; +use crate::values::computed::{ + AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, + Resolution, ToComputedValue, +}; +use crate::values::generics::image::{self as generic, GradientCompatMode}; +use crate::values::specified::image as specified; +use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword}; +use std::f32::consts::PI; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ToCss}; + +pub use specified::ImageRendering; + +/// Computed values for an image according to CSS-IMAGES. +/// <https://drafts.csswg.org/css-images/#image-values> +pub type Image = + generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl, Color, Percentage, Resolution>; + +// Images should remain small, see https://github.com/servo/servo/pull/18430 +size_of_test!(Image, 16); + +/// Computed values for a CSS gradient. +/// <https://drafts.csswg.org/css-images/#gradients> +pub type Gradient = generic::GenericGradient< + LineDirection, + LengthPercentage, + NonNegativeLength, + NonNegativeLengthPercentage, + Position, + Angle, + AngleOrPercentage, + Color, +>; + +/// Computed values for CSS cross-fade +/// <https://drafts.csswg.org/css-images-4/#cross-fade-function> +pub type CrossFade = generic::CrossFade<Image, Color, Percentage>; + +/// A computed radial gradient ending shape. +pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>; + +/// A computed gradient line direction. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)] +#[repr(C, u8)] +pub enum LineDirection { + /// An angle. + Angle(Angle), + /// A horizontal direction. + Horizontal(HorizontalPositionKeyword), + /// A vertical direction. + Vertical(VerticalPositionKeyword), + /// A corner. + Corner(HorizontalPositionKeyword, VerticalPositionKeyword), +} + +/// The computed value for an `image-set()` image. +pub type ImageSet = generic::GenericImageSet<Image, Resolution>; + +impl ToComputedValue for specified::ImageSet { + type ComputedValue = ImageSet; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + let items = self.items.to_computed_value(context); + let dpr = context.device().device_pixel_ratio().get(); + + let mut supported_image = false; + let mut selected_index = std::usize::MAX; + let mut selected_resolution = items[0].resolution.dppx(); + + for (i, item) in items.iter().enumerate() { + // If the MIME type is not supported, we discard the ImageSetItem + if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) { + continue; + } + + let candidate_resolution = item.resolution.dppx(); + + // https://drafts.csswg.org/css-images-4/#image-set-notation: + // + // Make a UA-specific choice of which to load, based on whatever + // criteria deemed relevant (such as the resolution of the + // display, connection speed, etc). + // + // For now, select the lowest resolution greater than display + // density, otherwise the greatest resolution available + let better_candidate = || { + if selected_resolution < dpr && candidate_resolution > selected_resolution { + return true; + } + if candidate_resolution < selected_resolution && candidate_resolution >= dpr { + return true; + } + false + }; + + // The first item with a supported MIME type is obviously the current best candidate + if !supported_image || better_candidate() { + supported_image = true; + selected_index = i; + selected_resolution = candidate_resolution; + } + } + + ImageSet { + selected_index, + items, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + Self { + selected_index: std::usize::MAX, + items: ToComputedValue::from_computed_value(&computed.items), + } + } +} + +/// Computed values for `-moz-image-rect(...)`. +#[cfg(feature = "gecko")] +pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, ComputedImageUrl>; + +/// Empty enum on non-gecko +#[cfg(not(feature = "gecko"))] +pub type MozImageRect = specified::MozImageRect; + +impl generic::LineDirection for LineDirection { + fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool { + match *self { + LineDirection::Angle(angle) => angle.radians() == PI, + LineDirection::Vertical(VerticalPositionKeyword::Bottom) => { + compat_mode == GradientCompatMode::Modern + }, + LineDirection::Vertical(VerticalPositionKeyword::Top) => { + compat_mode != GradientCompatMode::Modern + }, + _ => false, + } + } + + fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result + where + W: Write, + { + match *self { + LineDirection::Angle(ref angle) => angle.to_css(dest), + LineDirection::Horizontal(x) => { + if compat_mode == GradientCompatMode::Modern { + dest.write_str("to ")?; + } + x.to_css(dest) + }, + LineDirection::Vertical(y) => { + if compat_mode == GradientCompatMode::Modern { + dest.write_str("to ")?; + } + y.to_css(dest) + }, + LineDirection::Corner(x, y) => { + if compat_mode == GradientCompatMode::Modern { + dest.write_str("to ")?; + } + x.to_css(dest)?; + dest.write_char(' ')?; + y.to_css(dest) + }, + } + } +} + +impl ToComputedValue for specified::LineDirection { + type ComputedValue = LineDirection; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + specified::LineDirection::Angle(ref angle) => { + LineDirection::Angle(angle.to_computed_value(context)) + }, + specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x), + specified::LineDirection::Vertical(y) => LineDirection::Vertical(y), + specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y), + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + LineDirection::Angle(ref angle) => { + specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle)) + }, + LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x), + LineDirection::Vertical(y) => specified::LineDirection::Vertical(y), + LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y), + } + } +} |