1use crate::derives::*;
10use crate::parser::{Parse, ParserContext};
11use crate::values::computed::resolution::Resolution as ComputedResolution;
12use crate::values::computed::{Context, ToComputedValue};
13use crate::values::specified::calc::{CalcNode, CalcNumeric, Leaf};
14use crate::values::tagged_numeric::{NumericUnion, Unpacked};
15use crate::values::CSSFloat;
16use cssparser::{match_ignore_ascii_case, Parser, Token};
17use std::fmt::{self, Write};
18use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
19
20#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
22#[repr(u8)]
23pub enum ResolutionUnit {
24 Dpi,
26 X,
28 Dppx,
30 Dpcm,
32}
33
34impl ResolutionUnit {
35 #[inline]
37 pub fn as_str(self) -> &'static str {
38 match self {
39 Self::Dpi => "dpi",
40 Self::X => "x",
41 Self::Dppx => "dppx",
42 Self::Dpcm => "dpcm",
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
49#[repr(C)]
50pub struct NoCalcResolution {
51 unit: ResolutionUnit,
52 value: CSSFloat,
53}
54
55impl NoCalcResolution {
56 #[inline]
58 pub fn new(unit: ResolutionUnit, value: CSSFloat) -> Self {
59 Self { unit, value }
60 }
61
62 #[inline]
64 pub fn from_dppx(value: CSSFloat) -> Self {
65 Self::new(ResolutionUnit::Dppx, value)
66 }
67
68 #[inline]
70 pub fn from_x(value: CSSFloat) -> Self {
71 Self::new(ResolutionUnit::X, value)
72 }
73
74 pub fn dppx(&self) -> CSSFloat {
76 match self.unit {
77 ResolutionUnit::X | ResolutionUnit::Dppx => self.value,
78 _ => self.dpi() / 96.0,
79 }
80 }
81
82 pub fn dpi(&self) -> CSSFloat {
84 match self.unit {
85 ResolutionUnit::Dpi => self.value,
86 ResolutionUnit::X | ResolutionUnit::Dppx => self.value * 96.0,
87 ResolutionUnit::Dpcm => self.value * 2.54,
88 }
89 }
90
91 #[inline]
93 pub fn resolution_unit(&self) -> ResolutionUnit {
94 self.unit
95 }
96
97 pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Self, ()> {
99 let unit = match_ignore_ascii_case! { &unit,
100 "dpi" => ResolutionUnit::Dpi,
101 "dppx" => ResolutionUnit::Dppx,
102 "dpcm" => ResolutionUnit::Dpcm,
103 "x" => ResolutionUnit::X,
104 _ => return Err(())
105 };
106 Ok(Self::new(unit, value))
107 }
108}
109
110impl ToCss for NoCalcResolution {
111 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
112 where
113 W: Write,
114 {
115 crate::values::serialize_specified_dimension(
116 self.value,
117 self.unit.as_str(),
118 false,
119 dest,
120 )
121 }
122}
123
124impl SpecifiedValueInfo for NoCalcResolution {}
125
126#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
130pub struct Resolution(NumericUnion<ResolutionUnit, f32, CalcNumeric>);
131
132impl ToCss for Resolution {
133 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
134 where
135 W: Write,
136 {
137 match self.0.unpack() {
138 Unpacked::Inline(unit, value) => NoCalcResolution::new(unit, value).to_css(dest),
139 Unpacked::Boxed(calc) => calc.to_css(dest),
140 }
141 }
142}
143
144impl SpecifiedValueInfo for Resolution {}
145
146impl Resolution {
147 #[inline]
149 pub fn new(resolution: NoCalcResolution) -> Self {
150 Self(NumericUnion::inline(resolution.unit, resolution.value))
151 }
152
153 #[inline]
155 pub fn new_calc(calc: Box<CalcNumeric>) -> Self {
156 Self(NumericUnion::boxed(calc))
157 }
158
159 #[inline]
161 pub fn from_dppx(value: CSSFloat) -> Self {
162 Self::new(NoCalcResolution::from_dppx(value))
163 }
164
165 #[inline]
167 pub fn from_x(value: CSSFloat) -> Self {
168 Self::new(NoCalcResolution::from_x(value))
169 }
170
171 #[inline]
173 pub fn is_calc(&self) -> bool {
174 self.0.is_boxed()
175 }
176
177 pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Self, ()> {
179 NoCalcResolution::parse_dimension(value, unit).map(Self::new)
180 }
181}
182
183impl ToComputedValue for Resolution {
184 type ComputedValue = ComputedResolution;
185
186 #[inline]
187 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
188 let dppx = match self.0.unpack() {
189 Unpacked::Inline(unit, value) => NoCalcResolution::new(unit, value).dppx().max(0.0),
190 Unpacked::Boxed(calc) => calc.resolve(context, |result| match result {
191 Ok(Leaf::Resolution(r)) => r.dppx().max(0.0),
192 _ => {
193 debug_assert!(
194 false,
195 "Unexpected Resolution::Calc without resolved resolution"
196 );
197 0.0
198 },
199 }),
200 };
201 ComputedResolution::from_dppx(crate::values::normalize(dppx))
202 }
203
204 #[inline]
205 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
206 Self::from_dppx(computed.dppx())
207 }
208}
209
210impl Parse for Resolution {
211 fn parse<'i, 't>(
212 context: &ParserContext,
213 input: &mut Parser<'i, 't>,
214 ) -> Result<Self, ParseError<'i>> {
215 let location = input.current_source_location();
216 match *input.next()? {
217 Token::Dimension {
218 value, ref unit, ..
219 } if value >= 0. => Self::parse_dimension(value, unit)
220 .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
221 Token::Function(ref name) => {
222 let function = CalcNode::math_function(context, name, location)?;
223 CalcNode::parse_resolution(context, input, function)
224 .map(Box::new)
225 .map(Self::new_calc)
226 },
227 ref t => return Err(location.new_unexpected_token_error(t.clone())),
228 }
229 }
230}