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, 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 LengthPercentage,
42 NonNegativeLength,
43 NonNegativeLengthPercentage,
44 Position,
45 Angle,
46 AngleOrPercentage,
47 Color,
48>;
49
50pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
53
54pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
56
57#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
59#[repr(C, u8)]
60pub enum LineDirection {
61 Angle(Angle),
63 Horizontal(HorizontalPositionKeyword),
65 Vertical(VerticalPositionKeyword),
67 Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
69}
70
71pub type ImageSet = generic::GenericImageSet<Image, Resolution>;
73
74impl 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 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 continue;
99 }
100
101 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 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
140impl 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
184impl 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
210impl 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}