1use crate::values::computed::percentage::Percentage;
11use crate::values::computed::position::Position;
12use crate::values::computed::url::ComputedUrl;
13use crate::values::computed::{Angle, Color, Context};
14use crate::values::computed::{
15 AngleOrPercentage, Length, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
16 Resolution, ToComputedValue,
17};
18use crate::values::generics::image::{self as generic, GradientCompatMode};
19use crate::values::specified::image as specified;
20use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};
21use std::f32::consts::PI;
22use std::fmt::{self, Write};
23use style_traits::{CssWriter, ToCss};
24
25pub use specified::ImageRendering;
26
27pub type Image = generic::GenericImage<Gradient, ComputedUrl, Color, Percentage, Resolution>;
30
31#[cfg(feature = "gecko")]
33size_of_test!(Image, 16);
34#[cfg(feature = "servo")]
35size_of_test!(Image, 24);
36
37pub type Gradient = generic::GenericGradient<
40 LineDirection,
41 Length,
42 LengthPercentage,
43 Position,
44 Angle,
45 AngleOrPercentage,
46 Color,
47>;
48
49pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
52
53pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
55
56#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
58#[repr(C, u8)]
59pub enum LineDirection {
60 Angle(Angle),
62 Horizontal(HorizontalPositionKeyword),
64 Vertical(VerticalPositionKeyword),
66 Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
68}
69
70pub type ImageSet = generic::GenericImageSet<Image, Resolution>;
72
73impl ToComputedValue for specified::ImageSet {
74 type ComputedValue = ImageSet;
75
76 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
77 let items = self.items.to_computed_value(context);
78 let dpr = context.device().device_pixel_ratio().get();
79
80 let mut supported_image = false;
81 let mut selected_index = std::usize::MAX;
82 let mut selected_resolution = 0.0;
83
84 for (i, item) in items.iter().enumerate() {
85 if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {
86 continue;
88 }
89
90 let candidate_resolution = item.resolution.dppx();
91 debug_assert!(
92 candidate_resolution >= 0.0,
93 "Resolutions should be non-negative"
94 );
95 if candidate_resolution == 0.0 {
96 continue;
98 }
99
100 let better_candidate = || {
108 if selected_resolution < dpr && candidate_resolution > selected_resolution {
109 return true;
110 }
111 if candidate_resolution < selected_resolution && candidate_resolution >= dpr {
112 return true;
113 }
114 false
115 };
116
117 if !supported_image || better_candidate() {
119 supported_image = true;
120 selected_index = i;
121 selected_resolution = candidate_resolution;
122 }
123 }
124
125 ImageSet {
126 selected_index,
127 items,
128 }
129 }
130
131 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
132 Self {
133 selected_index: std::usize::MAX,
134 items: ToComputedValue::from_computed_value(&computed.items),
135 }
136 }
137}
138
139impl generic::LineDirection for LineDirection {
140 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {
141 match *self {
142 LineDirection::Angle(angle) => angle.radians() == PI,
143 LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {
144 compat_mode == GradientCompatMode::Modern
145 },
146 LineDirection::Vertical(VerticalPositionKeyword::Top) => {
147 compat_mode != GradientCompatMode::Modern
148 },
149 _ => false,
150 }
151 }
152
153 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
154 where
155 W: Write,
156 {
157 match *self {
158 LineDirection::Angle(ref angle) => angle.to_css(dest),
159 LineDirection::Horizontal(x) => {
160 if compat_mode == GradientCompatMode::Modern {
161 dest.write_str("to ")?;
162 }
163 x.to_css(dest)
164 },
165 LineDirection::Vertical(y) => {
166 if compat_mode == GradientCompatMode::Modern {
167 dest.write_str("to ")?;
168 }
169 y.to_css(dest)
170 },
171 LineDirection::Corner(x, y) => {
172 if compat_mode == GradientCompatMode::Modern {
173 dest.write_str("to ")?;
174 }
175 x.to_css(dest)?;
176 dest.write_char(' ')?;
177 y.to_css(dest)
178 },
179 }
180 }
181}
182
183impl ToComputedValue for specified::LineDirection {
184 type ComputedValue = LineDirection;
185
186 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
187 match *self {
188 specified::LineDirection::Angle(ref angle) => {
189 LineDirection::Angle(angle.to_computed_value(context))
190 },
191 specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x),
192 specified::LineDirection::Vertical(y) => LineDirection::Vertical(y),
193 specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y),
194 }
195 }
196
197 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
198 match *computed {
199 LineDirection::Angle(ref angle) => {
200 specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle))
201 },
202 LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x),
203 LineDirection::Vertical(y) => specified::LineDirection::Vertical(y),
204 LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y),
205 }
206 }
207}
208
209impl ToComputedValue for specified::Image {
210 type ComputedValue = Image;
211
212 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
213 match self {
214 Self::None => Image::None,
215 Self::Url(u) => Image::Url(u.to_computed_value(context)),
216 Self::Gradient(g) => Image::Gradient(g.to_computed_value(context)),
217 #[cfg(feature = "gecko")]
218 Self::Element(e) => Image::Element(e.to_computed_value(context)),
219 #[cfg(feature = "gecko")]
220 Self::MozSymbolicIcon(e) => Image::MozSymbolicIcon(e.to_computed_value(context)),
221 #[cfg(feature = "servo")]
222 Self::PaintWorklet(w) => Image::PaintWorklet(w.to_computed_value(context)),
223 Self::CrossFade(f) => Image::CrossFade(f.to_computed_value(context)),
224 Self::ImageSet(s) => Image::ImageSet(s.to_computed_value(context)),
225 Self::LightDark(ld) => ld.compute(context),
226 }
227 }
228
229 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
230 match computed {
231 Image::None => Self::None,
232 Image::Url(u) => Self::Url(ToComputedValue::from_computed_value(u)),
233 Image::Gradient(g) => Self::Gradient(ToComputedValue::from_computed_value(g)),
234 #[cfg(feature = "gecko")]
235 Image::Element(e) => Self::Element(ToComputedValue::from_computed_value(e)),
236 #[cfg(feature = "gecko")]
237 Image::MozSymbolicIcon(e) => {
238 Self::MozSymbolicIcon(ToComputedValue::from_computed_value(e))
239 },
240 #[cfg(feature = "servo")]
241 Image::PaintWorklet(w) => Self::PaintWorklet(ToComputedValue::from_computed_value(w)),
242 Image::CrossFade(f) => Self::CrossFade(ToComputedValue::from_computed_value(f)),
243 Image::ImageSet(s) => Self::ImageSet(ToComputedValue::from_computed_value(s)),
244 Image::LightDark(_) => unreachable!("Shouldn't have computed image-set values"),
245 }
246 }
247}