1use cssparser::Parser;
4use markup5ever::{ExpandedName, LocalName, Namespace, expanded_name, local_name, ns};
5
6use crate::color::{Color, resolve_color};
7use crate::coord_units;
8use crate::coord_units::CoordUnits;
9use crate::document::{AcquiredNodes, NodeId, NodeStack};
10use crate::drawing_ctx::Viewport;
11use crate::element::{ElementData, ElementTrait, set_attribute};
12use crate::error::*;
13use crate::href::{is_href, set_href};
14use crate::length::*;
15use crate::node::{CascadedValues, Node, NodeBorrow};
16use crate::parse_identifiers;
17use crate::parsers::{Parse, ParseValue};
18use crate::rect::{Rect, rect_to_transform};
19use crate::rsvg_log;
20use crate::session::Session;
21use crate::transform::{Transform, TransformAttribute};
22use crate::unit_interval::UnitInterval;
23use crate::xml::Attributes;
24
25#[derive(Copy, Clone)]
27pub struct ColorStop {
28 pub offset: UnitInterval,
30
31 pub color: Color,
33}
34
35coord_units!(GradientUnits, CoordUnits::ObjectBoundingBox);
37
38#[derive(Debug, Default, Copy, Clone, PartialEq)]
40pub enum SpreadMethod {
41 #[default]
42 Pad,
43 Reflect,
44 Repeat,
45}
46
47impl Parse for SpreadMethod {
48 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<SpreadMethod, ParseError<'i>> {
49 Ok(parse_identifiers!(
50 parser,
51 "pad" => SpreadMethod::Pad,
52 "reflect" => SpreadMethod::Reflect,
53 "repeat" => SpreadMethod::Repeat,
54 )?)
55 }
56}
57
58#[derive(Default)]
60pub struct Stop {
61 offset: UnitInterval,
63 }
66
67impl ElementTrait for Stop {
68 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
69 for (attr, value) in attrs.iter() {
70 if attr.expanded() == expanded_name!("", "offset") {
71 set_attribute(&mut self.offset, attr.parse(value), session);
72 }
73 }
74 }
75}
76
77#[derive(Copy, Clone)]
82enum UnresolvedVariant {
83 Linear {
84 x1: Option<Length<Horizontal>>,
85 y1: Option<Length<Vertical>>,
86 x2: Option<Length<Horizontal>>,
87 y2: Option<Length<Vertical>>,
88 },
89
90 Radial {
91 cx: Option<Length<Horizontal>>,
92 cy: Option<Length<Vertical>>,
93 r: Option<Length<Both>>,
94 fx: Option<Length<Horizontal>>,
95 fy: Option<Length<Vertical>>,
96 fr: Option<Length<Both>>,
97 },
98}
99
100#[derive(Clone)]
102enum ResolvedGradientVariant {
103 Linear {
104 x1: Length<Horizontal>,
105 y1: Length<Vertical>,
106 x2: Length<Horizontal>,
107 y2: Length<Vertical>,
108 },
109
110 Radial {
111 cx: Length<Horizontal>,
112 cy: Length<Vertical>,
113 r: Length<Both>,
114 fx: Length<Horizontal>,
115 fy: Length<Vertical>,
116 fr: Length<Both>,
117 },
118}
119
120pub enum GradientVariant {
122 Linear {
123 x1: f64,
124 y1: f64,
125 x2: f64,
126 y2: f64,
127 },
128
129 Radial {
130 cx: f64,
131 cy: f64,
132 r: f64,
133 fx: f64,
134 fy: f64,
135 fr: f64,
136 },
137}
138
139impl UnresolvedVariant {
140 fn into_resolved(self) -> ResolvedGradientVariant {
141 assert!(self.is_resolved());
142
143 match self {
144 UnresolvedVariant::Linear { x1, y1, x2, y2 } => ResolvedGradientVariant::Linear {
145 x1: x1.unwrap(),
146 y1: y1.unwrap(),
147 x2: x2.unwrap(),
148 y2: y2.unwrap(),
149 },
150
151 UnresolvedVariant::Radial {
152 cx,
153 cy,
154 r,
155 fx,
156 fy,
157 fr,
158 } => ResolvedGradientVariant::Radial {
159 cx: cx.unwrap(),
160 cy: cy.unwrap(),
161 r: r.unwrap(),
162 fx: fx.unwrap(),
163 fy: fy.unwrap(),
164 fr: fr.unwrap(),
165 },
166 }
167 }
168
169 fn is_resolved(&self) -> bool {
170 match *self {
171 UnresolvedVariant::Linear { x1, y1, x2, y2 } => {
172 x1.is_some() && y1.is_some() && x2.is_some() && y2.is_some()
173 }
174
175 UnresolvedVariant::Radial {
176 cx,
177 cy,
178 r,
179 fx,
180 fy,
181 fr,
182 } => {
183 cx.is_some()
184 && cy.is_some()
185 && r.is_some()
186 && fx.is_some()
187 && fy.is_some()
188 && fr.is_some()
189 }
190 }
191 }
192
193 fn resolve_from_fallback(&self, fallback: &UnresolvedVariant) -> UnresolvedVariant {
194 match (*self, *fallback) {
195 (
196 UnresolvedVariant::Linear { x1, y1, x2, y2 },
197 UnresolvedVariant::Linear {
198 x1: fx1,
199 y1: fy1,
200 x2: fx2,
201 y2: fy2,
202 },
203 ) => UnresolvedVariant::Linear {
204 x1: x1.or(fx1),
205 y1: y1.or(fy1),
206 x2: x2.or(fx2),
207 y2: y2.or(fy2),
208 },
209
210 (
211 UnresolvedVariant::Radial {
212 cx,
213 cy,
214 r,
215 fx,
216 fy,
217 fr,
218 },
219 UnresolvedVariant::Radial {
220 cx: f_cx,
221 cy: f_cy,
222 r: f_r,
223 fx: f_fx,
224 fy: f_fy,
225 fr: f_fr,
226 },
227 ) => UnresolvedVariant::Radial {
228 cx: cx.or(f_cx),
229 cy: cy.or(f_cy),
230 r: r.or(f_r),
231 fx: fx.or(f_fx),
232 fy: fy.or(f_fy),
233 fr: fr.or(f_fr),
234 },
235
236 _ => *self, }
238 }
239
240 fn resolve_from_defaults(&self) -> UnresolvedVariant {
243 match self {
244 UnresolvedVariant::Linear { x1, y1, x2, y2 } => UnresolvedVariant::Linear {
245 x1: x1.or_else(|| Some(Length::<Horizontal>::parse_str("0%").unwrap())),
246 y1: y1.or_else(|| Some(Length::<Vertical>::parse_str("0%").unwrap())),
247 x2: x2.or_else(|| Some(Length::<Horizontal>::parse_str("100%").unwrap())),
248 y2: y2.or_else(|| Some(Length::<Vertical>::parse_str("0%").unwrap())),
249 },
250
251 UnresolvedVariant::Radial {
252 cx,
253 cy,
254 r,
255 fx,
256 fy,
257 fr,
258 } => {
259 let cx = cx.or_else(|| Some(Length::<Horizontal>::parse_str("50%").unwrap()));
260 let cy = cy.or_else(|| Some(Length::<Vertical>::parse_str("50%").unwrap()));
261 let r = r.or_else(|| Some(Length::<Both>::parse_str("50%").unwrap()));
262
263 let fx = fx.or(cx);
265 let fy = fy.or(cy);
266 let fr = fr.or_else(|| Some(Length::<Both>::parse_str("0%").unwrap()));
267
268 UnresolvedVariant::Radial {
269 cx,
270 cy,
271 r,
272 fx,
273 fy,
274 fr,
275 }
276 }
277 }
278 }
279}
280
281#[derive(Default)]
283struct Common {
284 units: Option<GradientUnits>,
285 transform: Option<TransformAttribute>,
286 spread: Option<SpreadMethod>,
287
288 fallback: Option<NodeId>,
289}
290
291#[derive(Default)]
293pub struct LinearGradient {
294 common: Common,
295
296 x1: Option<Length<Horizontal>>,
297 y1: Option<Length<Vertical>>,
298 x2: Option<Length<Horizontal>>,
299 y2: Option<Length<Vertical>>,
300}
301
302#[derive(Default)]
304pub struct RadialGradient {
305 common: Common,
306
307 cx: Option<Length<Horizontal>>,
308 cy: Option<Length<Vertical>>,
309 r: Option<Length<Both>>,
310 fx: Option<Length<Horizontal>>,
311 fy: Option<Length<Vertical>>,
312 fr: Option<Length<Both>>,
313}
314
315struct UnresolvedGradient {
320 units: Option<GradientUnits>,
321 transform: Option<TransformAttribute>,
322 spread: Option<SpreadMethod>,
323 stops: Option<Vec<ColorStop>>,
324
325 variant: UnresolvedVariant,
326}
327
328#[derive(Clone)]
330pub struct ResolvedGradient {
331 units: GradientUnits,
332 transform: TransformAttribute,
333 spread: SpreadMethod,
334 stops: Vec<ColorStop>,
335
336 variant: ResolvedGradientVariant,
337}
338
339pub struct UserSpaceGradient {
341 pub transform: Transform,
342 pub spread: SpreadMethod,
343 pub stops: Vec<ColorStop>,
344
345 pub variant: GradientVariant,
346}
347
348impl UnresolvedGradient {
349 fn into_resolved(self) -> ResolvedGradient {
350 assert!(self.is_resolved());
351
352 let UnresolvedGradient {
353 units,
354 transform,
355 spread,
356 stops,
357 variant,
358 } = self;
359
360 match variant {
361 UnresolvedVariant::Linear { .. } => ResolvedGradient {
362 units: units.unwrap(),
363 transform: transform.unwrap(),
364 spread: spread.unwrap(),
365 stops: stops.unwrap(),
366
367 variant: variant.into_resolved(),
368 },
369
370 UnresolvedVariant::Radial { .. } => ResolvedGradient {
371 units: units.unwrap(),
372 transform: transform.unwrap(),
373 spread: spread.unwrap(),
374 stops: stops.unwrap(),
375
376 variant: variant.into_resolved(),
377 },
378 }
379 }
380
381 fn add_color_stop(&mut self, offset: UnitInterval, color: Color) {
383 if self.stops.is_none() {
384 self.stops = Some(Vec::<ColorStop>::new());
385 }
386
387 if let Some(ref mut stops) = self.stops {
388 let last_offset = if !stops.is_empty() {
389 stops[stops.len() - 1].offset
390 } else {
391 UnitInterval(0.0)
392 };
393
394 let offset = if offset > last_offset {
395 offset
396 } else {
397 last_offset
398 };
399
400 stops.push(ColorStop { offset, color });
401 } else {
402 unreachable!();
403 }
404 }
405
406 fn add_color_stops_from_node(&mut self, node: &Node, opacity: UnitInterval) {
409 assert!(matches!(
410 *node.borrow_element_data(),
411 ElementData::LinearGradient(_) | ElementData::RadialGradient(_)
412 ));
413
414 for child in node.children().filter(|c| c.is_element()) {
415 if let ElementData::Stop(stop) = &*child.borrow_element_data() {
416 let cascaded = CascadedValues::new_from_node(&child);
417 let values = cascaded.get();
418
419 let UnitInterval(stop_opacity) = values.stop_opacity().0;
420 let UnitInterval(o) = opacity;
421
422 let composed_opacity = UnitInterval(stop_opacity * o);
423
424 let stop_color =
425 resolve_color(&values.stop_color().0, composed_opacity, &values.color().0);
426
427 self.add_color_stop(stop.offset, stop_color);
428 }
429 }
430 }
431
432 fn is_resolved(&self) -> bool {
433 self.units.is_some()
434 && self.transform.is_some()
435 && self.spread.is_some()
436 && self.stops.is_some()
437 && self.variant.is_resolved()
438 }
439
440 fn resolve_from_fallback(&self, fallback: &UnresolvedGradient) -> UnresolvedGradient {
441 let units = self.units.or(fallback.units);
442 let transform = self.transform.or(fallback.transform);
443 let spread = self.spread.or(fallback.spread);
444 let stops = self.stops.clone().or_else(|| fallback.stops.clone());
445 let variant = self.variant.resolve_from_fallback(&fallback.variant);
446
447 UnresolvedGradient {
448 units,
449 transform,
450 spread,
451 stops,
452 variant,
453 }
454 }
455
456 fn resolve_from_defaults(&self) -> UnresolvedGradient {
457 let units = self.units.or_else(|| Some(GradientUnits::default()));
458 let transform = self
459 .transform
460 .or_else(|| Some(TransformAttribute::default()));
461 let spread = self.spread.or_else(|| Some(SpreadMethod::default()));
462 let stops = self.stops.clone().or_else(|| Some(Vec::<ColorStop>::new()));
463 let variant = self.variant.resolve_from_defaults();
464
465 UnresolvedGradient {
466 units,
467 transform,
468 spread,
469 stops,
470 variant,
471 }
472 }
473}
474
475struct Unresolved {
481 gradient: UnresolvedGradient,
482 fallback: Option<NodeId>,
483}
484
485impl LinearGradient {
486 fn get_unresolved_variant(&self) -> UnresolvedVariant {
487 UnresolvedVariant::Linear {
488 x1: self.x1,
489 y1: self.y1,
490 x2: self.x2,
491 y2: self.y2,
492 }
493 }
494}
495
496impl RadialGradient {
497 fn get_unresolved_variant(&self) -> UnresolvedVariant {
498 UnresolvedVariant::Radial {
499 cx: self.cx,
500 cy: self.cy,
501 r: self.r,
502 fx: self.fx,
503 fy: self.fy,
504 fr: self.fr,
505 }
506 }
507}
508
509impl Common {
510 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
511 for (attr, value) in attrs.iter() {
512 match attr.expanded() {
513 expanded_name!("", "gradientUnits") => {
514 set_attribute(&mut self.units, attr.parse(value), session)
515 }
516 expanded_name!("", "gradientTransform") => {
517 set_attribute(&mut self.transform, attr.parse(value), session);
518 }
519 expanded_name!("", "spreadMethod") => {
520 set_attribute(&mut self.spread, attr.parse(value), session)
521 }
522 ref a if is_href(a) => {
523 let mut href = None;
524 set_attribute(
525 &mut href,
526 NodeId::parse(value).map(Some).attribute(attr.clone()),
527 session,
528 );
529 set_href(a, &mut self.fallback, href);
530 }
531 _ => (),
532 }
533 }
534 }
535}
536
537impl ElementTrait for LinearGradient {
538 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
539 self.common.set_attributes(attrs, session);
540
541 for (attr, value) in attrs.iter() {
542 match attr.expanded() {
543 expanded_name!("", "x1") => set_attribute(&mut self.x1, attr.parse(value), session),
544 expanded_name!("", "y1") => set_attribute(&mut self.y1, attr.parse(value), session),
545 expanded_name!("", "x2") => set_attribute(&mut self.x2, attr.parse(value), session),
546 expanded_name!("", "y2") => set_attribute(&mut self.y2, attr.parse(value), session),
547
548 _ => (),
549 }
550 }
551 }
552}
553
554macro_rules! impl_gradient {
555 ($gradient_type:ident, $other_type:ident) => {
556 impl $gradient_type {
557 fn get_unresolved(&self, node: &Node, opacity: UnitInterval) -> Unresolved {
558 let mut gradient = UnresolvedGradient {
559 units: self.common.units,
560 transform: self.common.transform,
561 spread: self.common.spread,
562 stops: None,
563 variant: self.get_unresolved_variant(),
564 };
565
566 gradient.add_color_stops_from_node(node, opacity);
567
568 Unresolved {
569 gradient,
570 fallback: self.common.fallback.clone(),
571 }
572 }
573
574 pub fn resolve(
575 &self,
576 node: &Node,
577 acquired_nodes: &mut AcquiredNodes<'_>,
578 opacity: UnitInterval,
579 session: &Session,
580 ) -> Result<ResolvedGradient, AcquireError> {
581 let Unresolved {
582 mut gradient,
583 mut fallback,
584 } = self.get_unresolved(node, opacity);
585
586 let mut stack = NodeStack::new();
587
588 while !gradient.is_resolved() {
589 if let Some(node_id) = fallback {
590 let node_name = format!("{node}");
591 let acquired = acquired_nodes.acquire(&node_name, &node_id)?;
592 let acquired_node = acquired.get();
593
594 if stack.contains(acquired_node) {
595 return Err(AcquireError::CircularReference(acquired_node.clone()));
596 }
597
598 let unresolved = match *acquired_node.borrow_element_data() {
599 ElementData::$gradient_type(ref g) => {
600 g.get_unresolved(&acquired_node, opacity)
601 }
602 ElementData::$other_type(ref g) => {
603 g.get_unresolved(&acquired_node, opacity)
604 }
605 _ => {
606 rsvg_log!(session, "{acquired_node} is not a gradient; ignoring");
607 return Err(AcquireError::InvalidLinkType(node_id.clone()));
608 }
609 };
610
611 gradient = gradient.resolve_from_fallback(&unresolved.gradient);
612 fallback = unresolved.fallback;
613
614 stack.push(acquired_node);
615 } else {
616 gradient = gradient.resolve_from_defaults();
617 break;
618 }
619 }
620
621 Ok(gradient.into_resolved())
622 }
623 }
624 };
625}
626
627impl_gradient!(LinearGradient, RadialGradient);
628impl_gradient!(RadialGradient, LinearGradient);
629
630impl ElementTrait for RadialGradient {
631 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
632 self.common.set_attributes(attrs, session);
633
634 let expanded_name_fr = ExpandedName {
636 ns: &Namespace::from(""),
637 local: &LocalName::from("fr"),
638 };
639
640 for (attr, value) in attrs.iter() {
641 let attr_expanded = attr.expanded();
642 match attr_expanded {
643 expanded_name!("", "cx") => set_attribute(&mut self.cx, attr.parse(value), session),
644 expanded_name!("", "cy") => set_attribute(&mut self.cy, attr.parse(value), session),
645 expanded_name!("", "r") => set_attribute(&mut self.r, attr.parse(value), session),
646 expanded_name!("", "fx") => set_attribute(&mut self.fx, attr.parse(value), session),
647 expanded_name!("", "fy") => set_attribute(&mut self.fy, attr.parse(value), session),
648 a if a == expanded_name_fr => {
649 set_attribute(&mut self.fr, attr.parse(value), session)
650 }
651
652 _ => (),
653 }
654 }
655 }
656}
657
658impl ResolvedGradient {
659 pub fn to_user_space(
660 &self,
661 object_bbox: &Option<Rect>,
662 viewport: &Viewport,
663 values: &NormalizeValues,
664 ) -> Option<UserSpaceGradient> {
665 let units = self.units.0;
666 let transform = rect_to_transform(object_bbox, units).ok()?;
667 let viewport = viewport.with_units(units);
668 let params = NormalizeParams::from_values(values, &viewport);
669
670 let gradient_transform = self.transform.to_transform();
671 let transform = transform.pre_transform(&gradient_transform).invert()?;
672
673 let variant = match self.variant {
674 ResolvedGradientVariant::Linear { x1, y1, x2, y2 } => GradientVariant::Linear {
675 x1: x1.to_user(¶ms),
676 y1: y1.to_user(¶ms),
677 x2: x2.to_user(¶ms),
678 y2: y2.to_user(¶ms),
679 },
680
681 ResolvedGradientVariant::Radial {
682 cx,
683 cy,
684 r,
685 fx,
686 fy,
687 fr,
688 } => GradientVariant::Radial {
689 cx: cx.to_user(¶ms),
690 cy: cy.to_user(¶ms),
691 r: r.to_user(¶ms),
692 fx: fx.to_user(¶ms),
693 fy: fy.to_user(¶ms),
694 fr: fr.to_user(¶ms),
695 },
696 };
697
698 Some(UserSpaceGradient {
699 transform,
700 spread: self.spread,
701 stops: self.stops.clone(),
702 variant,
703 })
704 }
705}
706
707#[cfg(test)]
708mod tests {
709 use super::*;
710
711 use markup5ever::{QualName, ns};
712
713 use crate::borrow_element_as;
714 use crate::node::{Node, NodeData};
715
716 #[test]
717 fn parses_spread_method() {
718 assert_eq!(SpreadMethod::parse_str("pad").unwrap(), SpreadMethod::Pad);
719 assert_eq!(
720 SpreadMethod::parse_str("reflect").unwrap(),
721 SpreadMethod::Reflect
722 );
723 assert_eq!(
724 SpreadMethod::parse_str("repeat").unwrap(),
725 SpreadMethod::Repeat
726 );
727 assert!(SpreadMethod::parse_str("foobar").is_err());
728 }
729
730 #[test]
731 fn gradient_resolved_from_defaults_is_really_resolved() {
732 let session = Session::default();
733
734 let node = Node::new(NodeData::new_element(
735 &session,
736 &QualName::new(None, ns!(svg), local_name!("linearGradient")),
737 Attributes::new(),
738 ));
739
740 let unresolved = borrow_element_as!(node, LinearGradient)
741 .get_unresolved(&node, UnitInterval::clamp(1.0));
742 let gradient = unresolved.gradient.resolve_from_defaults();
743 assert!(gradient.is_resolved());
744
745 let node = Node::new(NodeData::new_element(
746 &session,
747 &QualName::new(None, ns!(svg), local_name!("radialGradient")),
748 Attributes::new(),
749 ));
750
751 let unresolved = borrow_element_as!(node, RadialGradient)
752 .get_unresolved(&node, UnitInterval::clamp(1.0));
753 let gradient = unresolved.gradient.resolve_from_defaults();
754 assert!(gradient.is_resolved());
755 }
756}