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