image.rs (9695B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! CSS handling for the computed value of 6 //! [`image`][image]s 7 //! 8 //! [image]: https://drafts.csswg.org/css-images/#image-values 9 10 use crate::derives::*; 11 use crate::values::computed::percentage::Percentage; 12 use crate::values::computed::position::Position; 13 use crate::values::computed::url::ComputedUrl; 14 use crate::values::computed::{Angle, Color, Context}; 15 use crate::values::computed::{ 16 AngleOrPercentage, Length, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, 17 Resolution, ToComputedValue, 18 }; 19 use crate::values::generics::image::{self as generic, GradientCompatMode}; 20 use crate::values::specified::image as specified; 21 use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword}; 22 use std::f32::consts::PI; 23 use std::fmt::{self, Write}; 24 use style_traits::{CssWriter, ToCss}; 25 26 pub use specified::ImageRendering; 27 28 /// Computed values for an image according to CSS-IMAGES. 29 /// <https://drafts.csswg.org/css-images/#image-values> 30 pub type Image = generic::GenericImage<Gradient, ComputedUrl, Color, Percentage, Resolution>; 31 32 // Images should remain small, see https://github.com/servo/servo/pull/18430 33 #[cfg(feature = "gecko")] 34 size_of_test!(Image, 16); 35 #[cfg(feature = "servo")] 36 size_of_test!(Image, 24); 37 38 /// Computed values for a CSS gradient. 39 /// <https://drafts.csswg.org/css-images/#gradients> 40 pub type Gradient = generic::GenericGradient< 41 LineDirection, 42 Length, 43 LengthPercentage, 44 Position, 45 Angle, 46 AngleOrPercentage, 47 Color, 48 >; 49 50 /// Computed values for CSS cross-fade 51 /// <https://drafts.csswg.org/css-images-4/#cross-fade-function> 52 pub type CrossFade = generic::CrossFade<Image, Color, Percentage>; 53 54 /// A computed radial gradient ending shape. 55 pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>; 56 57 /// A computed gradient line direction. 58 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)] 59 #[repr(C, u8)] 60 pub enum LineDirection { 61 /// An angle. 62 Angle(Angle), 63 /// A horizontal direction. 64 Horizontal(HorizontalPositionKeyword), 65 /// A vertical direction. 66 Vertical(VerticalPositionKeyword), 67 /// A corner. 68 Corner(HorizontalPositionKeyword, VerticalPositionKeyword), 69 } 70 71 /// The computed value for an `image-set()` image. 72 pub type ImageSet = generic::GenericImageSet<Image, Resolution>; 73 74 impl ToComputedValue for specified::ImageSet { 75 type ComputedValue = ImageSet; 76 77 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 78 let items = self.items.to_computed_value(context); 79 let dpr = context.device().device_pixel_ratio().get(); 80 81 let mut supported_image = false; 82 let mut selected_index = std::usize::MAX; 83 let mut selected_resolution = 0.0; 84 85 for (i, item) in items.iter().enumerate() { 86 if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) { 87 // If the MIME type is not supported, we discard the ImageSetItem. 88 continue; 89 } 90 91 let candidate_resolution = item.resolution.dppx(); 92 debug_assert!( 93 candidate_resolution >= 0.0, 94 "Resolutions should be non-negative" 95 ); 96 if candidate_resolution == 0.0 { 97 // If the resolution is 0, we also treat it as an invalid image. 98 continue; 99 } 100 101 // https://drafts.csswg.org/css-images-4/#image-set-notation: 102 // 103 // Make a UA-specific choice of which to load, based on whatever criteria deemed 104 // relevant (such as the resolution of the display, connection speed, etc). 105 // 106 // For now, select the lowest resolution greater than display density, otherwise the 107 // greatest resolution available. 108 let better_candidate = || { 109 if selected_resolution < dpr && candidate_resolution > selected_resolution { 110 return true; 111 } 112 if candidate_resolution < selected_resolution && candidate_resolution >= dpr { 113 return true; 114 } 115 false 116 }; 117 118 // The first item with a supported MIME type is obviously the current best candidate 119 if !supported_image || better_candidate() { 120 supported_image = true; 121 selected_index = i; 122 selected_resolution = candidate_resolution; 123 } 124 } 125 126 ImageSet { 127 selected_index, 128 items, 129 } 130 } 131 132 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 133 Self { 134 selected_index: std::usize::MAX, 135 items: ToComputedValue::from_computed_value(&computed.items), 136 } 137 } 138 } 139 140 impl generic::LineDirection for LineDirection { 141 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool { 142 match *self { 143 LineDirection::Angle(angle) => angle.radians() == PI, 144 LineDirection::Vertical(VerticalPositionKeyword::Bottom) => { 145 compat_mode == GradientCompatMode::Modern 146 }, 147 LineDirection::Vertical(VerticalPositionKeyword::Top) => { 148 compat_mode != GradientCompatMode::Modern 149 }, 150 _ => false, 151 } 152 } 153 154 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result 155 where 156 W: Write, 157 { 158 match *self { 159 LineDirection::Angle(ref angle) => angle.to_css(dest), 160 LineDirection::Horizontal(x) => { 161 if compat_mode == GradientCompatMode::Modern { 162 dest.write_str("to ")?; 163 } 164 x.to_css(dest) 165 }, 166 LineDirection::Vertical(y) => { 167 if compat_mode == GradientCompatMode::Modern { 168 dest.write_str("to ")?; 169 } 170 y.to_css(dest) 171 }, 172 LineDirection::Corner(x, y) => { 173 if compat_mode == GradientCompatMode::Modern { 174 dest.write_str("to ")?; 175 } 176 x.to_css(dest)?; 177 dest.write_char(' ')?; 178 y.to_css(dest) 179 }, 180 } 181 } 182 } 183 184 impl ToComputedValue for specified::LineDirection { 185 type ComputedValue = LineDirection; 186 187 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 188 match *self { 189 specified::LineDirection::Angle(ref angle) => { 190 LineDirection::Angle(angle.to_computed_value(context)) 191 }, 192 specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x), 193 specified::LineDirection::Vertical(y) => LineDirection::Vertical(y), 194 specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y), 195 } 196 } 197 198 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 199 match *computed { 200 LineDirection::Angle(ref angle) => { 201 specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle)) 202 }, 203 LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x), 204 LineDirection::Vertical(y) => specified::LineDirection::Vertical(y), 205 LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y), 206 } 207 } 208 } 209 210 impl ToComputedValue for specified::Image { 211 type ComputedValue = Image; 212 213 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 214 match self { 215 Self::None => Image::None, 216 Self::Url(u) => Image::Url(u.to_computed_value(context)), 217 Self::Gradient(g) => Image::Gradient(g.to_computed_value(context)), 218 #[cfg(feature = "gecko")] 219 Self::Element(e) => Image::Element(e.to_computed_value(context)), 220 #[cfg(feature = "gecko")] 221 Self::MozSymbolicIcon(e) => Image::MozSymbolicIcon(e.to_computed_value(context)), 222 #[cfg(feature = "servo")] 223 Self::PaintWorklet(w) => Image::PaintWorklet(w.to_computed_value(context)), 224 Self::CrossFade(f) => Image::CrossFade(f.to_computed_value(context)), 225 Self::ImageSet(s) => Image::ImageSet(s.to_computed_value(context)), 226 Self::LightDark(ld) => ld.compute(context), 227 } 228 } 229 230 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 231 match computed { 232 Image::None => Self::None, 233 Image::Url(u) => Self::Url(ToComputedValue::from_computed_value(u)), 234 Image::Gradient(g) => Self::Gradient(ToComputedValue::from_computed_value(g)), 235 #[cfg(feature = "gecko")] 236 Image::Element(e) => Self::Element(ToComputedValue::from_computed_value(e)), 237 #[cfg(feature = "gecko")] 238 Image::MozSymbolicIcon(e) => { 239 Self::MozSymbolicIcon(ToComputedValue::from_computed_value(e)) 240 }, 241 #[cfg(feature = "servo")] 242 Image::PaintWorklet(w) => Self::PaintWorklet(ToComputedValue::from_computed_value(w)), 243 Image::CrossFade(f) => Self::CrossFade(ToComputedValue::from_computed_value(f)), 244 Image::ImageSet(s) => Self::ImageSet(ToComputedValue::from_computed_value(s)), 245 Image::LightDark(_) => unreachable!("Shouldn't have computed image-set values"), 246 } 247 } 248 }