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