1pub mod filter;
5mod geom;
6mod text;
7
8use std::fmt::Display;
9use std::sync::Arc;
10
11pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
12
13pub use tiny_skia_path;
14
15pub use self::geom::*;
16pub use self::text::*;
17
18use crate::OptionLog;
19
20pub type Opacity = NormalizedF32;
22
23#[derive(Debug)]
25pub(crate) struct NonEmptyString(String);
26
27impl NonEmptyString {
28 pub(crate) fn new(string: String) -> Option<Self> {
29 if string.trim().is_empty() {
30 return None;
31 }
32
33 Some(NonEmptyString(string))
34 }
35
36 pub(crate) fn get(&self) -> &str {
37 &self.0
38 }
39
40 pub(crate) fn take(self) -> String {
41 self.0
42 }
43}
44
45#[derive(Clone, Copy, Debug)]
49pub struct NonZeroF32(f32);
50
51impl NonZeroF32 {
52 #[inline]
54 pub fn new(n: f32) -> Option<Self> {
55 if n.approx_eq_ulps(&0.0, 4) {
56 None
57 } else {
58 Some(NonZeroF32(n))
59 }
60 }
61
62 #[inline]
64 pub fn get(&self) -> f32 {
65 self.0
66 }
67}
68
69#[derive(Clone, Copy, PartialEq, Debug)]
70pub(crate) enum Units {
71 UserSpaceOnUse,
72 ObjectBoundingBox,
73}
74
75impl std::fmt::Display for Units {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 Units::UserSpaceOnUse => write!(f, "userSpaceOnUse"),
81 Units::ObjectBoundingBox => write!(f, "objectBoundingBox"),
82 }
83 }
84}
85
86#[allow(missing_docs)]
90#[derive(Clone, Copy, PartialEq, Debug)]
91pub(crate) enum Visibility {
92 Visible,
93 Hidden,
94 Collapse,
95}
96
97impl Default for Visibility {
98 fn default() -> Self {
99 Self::Visible
100 }
101}
102
103#[derive(Clone, Copy, PartialEq, Debug)]
107#[allow(missing_docs)]
108pub enum ShapeRendering {
109 OptimizeSpeed,
110 CrispEdges,
111 GeometricPrecision,
112}
113
114impl ShapeRendering {
115 pub fn use_shape_antialiasing(self) -> bool {
117 match self {
118 ShapeRendering::OptimizeSpeed => false,
119 ShapeRendering::CrispEdges => false,
120 ShapeRendering::GeometricPrecision => true,
121 }
122 }
123}
124
125impl Default for ShapeRendering {
126 fn default() -> Self {
127 Self::GeometricPrecision
128 }
129}
130
131impl std::str::FromStr for ShapeRendering {
132 type Err = &'static str;
133
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
135 match s {
136 "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
137 "crispEdges" => Ok(ShapeRendering::CrispEdges),
138 "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
139 _ => Err("invalid"),
140 }
141 }
142}
143
144#[allow(missing_docs)]
148#[derive(Clone, Copy, PartialEq, Debug)]
149pub enum TextRendering {
150 OptimizeSpeed,
151 OptimizeLegibility,
152 GeometricPrecision,
153}
154
155impl Default for TextRendering {
156 fn default() -> Self {
157 Self::OptimizeLegibility
158 }
159}
160
161impl std::str::FromStr for TextRendering {
162 type Err = &'static str;
163
164 fn from_str(s: &str) -> Result<Self, Self::Err> {
165 match s {
166 "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
167 "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
168 "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
169 _ => Err("invalid"),
170 }
171 }
172}
173
174#[allow(missing_docs)]
178#[derive(Clone, Copy, PartialEq, Debug)]
179pub enum ImageRendering {
180 OptimizeQuality,
181 OptimizeSpeed,
182 Smooth,
184 HighQuality,
185 CrispEdges,
186 Pixelated,
187}
188
189impl Default for ImageRendering {
190 fn default() -> Self {
191 Self::OptimizeQuality
192 }
193}
194
195impl std::str::FromStr for ImageRendering {
196 type Err = &'static str;
197
198 fn from_str(s: &str) -> Result<Self, Self::Err> {
199 match s {
200 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
201 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
202 "smooth" => Ok(ImageRendering::Smooth),
203 "high-quality" => Ok(ImageRendering::HighQuality),
204 "crisp-edges" => Ok(ImageRendering::CrispEdges),
205 "pixelated" => Ok(ImageRendering::Pixelated),
206 _ => Err("invalid"),
207 }
208 }
209}
210
211#[allow(missing_docs)]
215#[derive(Clone, Copy, PartialEq, Debug)]
216pub enum BlendMode {
217 Normal,
218 Multiply,
219 Screen,
220 Overlay,
221 Darken,
222 Lighten,
223 ColorDodge,
224 ColorBurn,
225 HardLight,
226 SoftLight,
227 Difference,
228 Exclusion,
229 Hue,
230 Saturation,
231 Color,
232 Luminosity,
233}
234
235impl Default for BlendMode {
236 fn default() -> Self {
237 Self::Normal
238 }
239}
240
241impl Display for BlendMode {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 let blend_mode = match self {
244 BlendMode::Normal => "normal",
245 BlendMode::Multiply => "multiply",
246 BlendMode::Screen => "screen",
247 BlendMode::Overlay => "overlay",
248 BlendMode::Darken => "darken",
249 BlendMode::Lighten => "lighten",
250 BlendMode::ColorDodge => "color-dodge",
251 BlendMode::ColorBurn => "color-burn",
252 BlendMode::HardLight => "hard-light",
253 BlendMode::SoftLight => "soft-light",
254 BlendMode::Difference => "difference",
255 BlendMode::Exclusion => "exclusion",
256 BlendMode::Hue => "hue",
257 BlendMode::Saturation => "saturation",
258 BlendMode::Color => "color",
259 BlendMode::Luminosity => "luminosity",
260 };
261 write!(f, "{blend_mode}")
262 }
263}
264
265#[allow(missing_docs)]
269#[derive(Clone, Copy, PartialEq, Debug)]
270pub enum SpreadMethod {
271 Pad,
272 Reflect,
273 Repeat,
274}
275
276impl Default for SpreadMethod {
277 fn default() -> Self {
278 Self::Pad
279 }
280}
281
282#[derive(Debug)]
284pub struct BaseGradient {
285 pub(crate) id: NonEmptyString,
286 pub(crate) units: Units, pub(crate) transform: Transform,
288 pub(crate) spread_method: SpreadMethod,
289 pub(crate) stops: Vec<Stop>,
290}
291
292impl BaseGradient {
293 pub fn id(&self) -> &str {
298 self.id.get()
299 }
300
301 pub fn transform(&self) -> Transform {
305 self.transform
306 }
307
308 pub fn spread_method(&self) -> SpreadMethod {
312 self.spread_method
313 }
314
315 pub fn stops(&self) -> &[Stop] {
317 &self.stops
318 }
319}
320
321#[derive(Debug)]
325pub struct LinearGradient {
326 pub(crate) base: BaseGradient,
327 pub(crate) x1: f32,
328 pub(crate) y1: f32,
329 pub(crate) x2: f32,
330 pub(crate) y2: f32,
331}
332
333impl LinearGradient {
334 pub fn x1(&self) -> f32 {
336 self.x1
337 }
338
339 pub fn y1(&self) -> f32 {
341 self.y1
342 }
343
344 pub fn x2(&self) -> f32 {
346 self.x2
347 }
348
349 pub fn y2(&self) -> f32 {
351 self.y2
352 }
353}
354
355impl std::ops::Deref for LinearGradient {
356 type Target = BaseGradient;
357
358 fn deref(&self) -> &Self::Target {
359 &self.base
360 }
361}
362
363#[derive(Debug)]
367pub struct RadialGradient {
368 pub(crate) base: BaseGradient,
369 pub(crate) cx: f32,
370 pub(crate) cy: f32,
371 pub(crate) r: PositiveF32,
372 pub(crate) fx: f32,
373 pub(crate) fy: f32,
374 pub(crate) fr: PositiveF32,
375}
376
377impl RadialGradient {
378 pub fn cx(&self) -> f32 {
380 self.cx
381 }
382
383 pub fn cy(&self) -> f32 {
385 self.cy
386 }
387
388 pub fn r(&self) -> PositiveF32 {
390 self.r
391 }
392
393 pub fn fx(&self) -> f32 {
395 self.fx
396 }
397
398 pub fn fy(&self) -> f32 {
400 self.fy
401 }
402
403 pub fn fr(&self) -> PositiveF32 {
405 self.fr
406 }
407}
408
409impl std::ops::Deref for RadialGradient {
410 type Target = BaseGradient;
411
412 fn deref(&self) -> &Self::Target {
413 &self.base
414 }
415}
416
417pub type StopOffset = NormalizedF32;
419
420#[derive(Clone, Copy, Debug)]
424pub struct Stop {
425 pub(crate) offset: StopOffset,
426 pub(crate) color: Color,
427 pub(crate) opacity: Opacity,
428}
429
430impl Stop {
431 pub fn offset(&self) -> StopOffset {
435 self.offset
436 }
437
438 pub fn color(&self) -> Color {
442 self.color
443 }
444
445 pub fn opacity(&self) -> Opacity {
449 self.opacity
450 }
451}
452
453#[derive(Debug)]
457pub struct Pattern {
458 pub(crate) id: NonEmptyString,
459 pub(crate) units: Units, pub(crate) content_units: Units, pub(crate) transform: Transform,
462 pub(crate) rect: NonZeroRect,
463 pub(crate) view_box: Option<ViewBox>,
464 pub(crate) root: Group,
465}
466
467impl Pattern {
468 pub fn id(&self) -> &str {
473 self.id.get()
474 }
475
476 pub fn transform(&self) -> Transform {
480 self.transform
481 }
482
483 pub fn rect(&self) -> NonZeroRect {
487 self.rect
488 }
489
490 pub fn root(&self) -> &Group {
492 &self.root
493 }
494}
495
496pub type StrokeWidth = NonZeroPositiveF32;
498
499#[derive(Clone, Copy, Debug)]
503pub struct StrokeMiterlimit(f32);
504
505impl StrokeMiterlimit {
506 #[inline]
508 pub fn new(n: f32) -> Self {
509 debug_assert!(n.is_finite());
510 debug_assert!(n >= 1.0);
511
512 let n = if !(n >= 1.0) { 1.0 } else { n };
513
514 StrokeMiterlimit(n)
515 }
516
517 #[inline]
519 pub fn get(&self) -> f32 {
520 self.0
521 }
522}
523
524impl Default for StrokeMiterlimit {
525 #[inline]
526 fn default() -> Self {
527 StrokeMiterlimit::new(4.0)
528 }
529}
530
531impl From<f32> for StrokeMiterlimit {
532 #[inline]
533 fn from(n: f32) -> Self {
534 Self::new(n)
535 }
536}
537
538impl PartialEq for StrokeMiterlimit {
539 #[inline]
540 fn eq(&self, other: &Self) -> bool {
541 self.0.approx_eq_ulps(&other.0, 4)
542 }
543}
544
545#[allow(missing_docs)]
549#[derive(Clone, Copy, PartialEq, Debug)]
550pub enum LineCap {
551 Butt,
552 Round,
553 Square,
554}
555
556impl Default for LineCap {
557 fn default() -> Self {
558 Self::Butt
559 }
560}
561
562#[allow(missing_docs)]
566#[derive(Clone, Copy, PartialEq, Debug)]
567pub enum LineJoin {
568 Miter,
569 MiterClip,
570 Round,
571 Bevel,
572}
573
574impl Default for LineJoin {
575 fn default() -> Self {
576 Self::Miter
577 }
578}
579
580#[derive(Clone, Debug)]
582pub struct Stroke {
583 pub(crate) paint: Paint,
584 pub(crate) dasharray: Option<Vec<f32>>,
585 pub(crate) dashoffset: f32,
586 pub(crate) miterlimit: StrokeMiterlimit,
587 pub(crate) opacity: Opacity,
588 pub(crate) width: StrokeWidth,
589 pub(crate) linecap: LineCap,
590 pub(crate) linejoin: LineJoin,
591 pub(crate) context_element: Option<ContextElement>,
594}
595
596impl Stroke {
597 pub fn paint(&self) -> &Paint {
599 &self.paint
600 }
601
602 pub fn dasharray(&self) -> Option<&[f32]> {
604 self.dasharray.as_deref()
605 }
606
607 pub fn dashoffset(&self) -> f32 {
609 self.dashoffset
610 }
611
612 pub fn miterlimit(&self) -> StrokeMiterlimit {
614 self.miterlimit
615 }
616
617 pub fn opacity(&self) -> Opacity {
619 self.opacity
620 }
621
622 pub fn width(&self) -> StrokeWidth {
624 self.width
625 }
626
627 pub fn linecap(&self) -> LineCap {
629 self.linecap
630 }
631
632 pub fn linejoin(&self) -> LineJoin {
634 self.linejoin
635 }
636
637 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
639 let mut stroke = tiny_skia_path::Stroke {
640 width: self.width.get(),
641 miter_limit: self.miterlimit.get(),
642 line_cap: match self.linecap {
643 LineCap::Butt => tiny_skia_path::LineCap::Butt,
644 LineCap::Round => tiny_skia_path::LineCap::Round,
645 LineCap::Square => tiny_skia_path::LineCap::Square,
646 },
647 line_join: match self.linejoin {
648 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
649 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
650 LineJoin::Round => tiny_skia_path::LineJoin::Round,
651 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
652 },
653 dash: None,
656 };
657
658 if let Some(ref list) = self.dasharray {
659 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
660 }
661
662 stroke
663 }
664}
665
666#[allow(missing_docs)]
670#[derive(Clone, Copy, PartialEq, Debug)]
671pub enum FillRule {
672 NonZero,
673 EvenOdd,
674}
675
676impl Default for FillRule {
677 fn default() -> Self {
678 Self::NonZero
679 }
680}
681
682#[derive(Clone, Copy, Debug)]
683pub(crate) enum ContextElement {
684 UseNode,
689 PathNode(Transform, Option<NonZeroRect>),
694}
695
696#[derive(Clone, Debug)]
698pub struct Fill {
699 pub(crate) paint: Paint,
700 pub(crate) opacity: Opacity,
701 pub(crate) rule: FillRule,
702 pub(crate) context_element: Option<ContextElement>,
705}
706
707impl Fill {
708 pub fn paint(&self) -> &Paint {
710 &self.paint
711 }
712
713 pub fn opacity(&self) -> Opacity {
715 self.opacity
716 }
717
718 pub fn rule(&self) -> FillRule {
720 self.rule
721 }
722}
723
724impl Default for Fill {
725 fn default() -> Self {
726 Fill {
727 paint: Paint::Color(Color::black()),
728 opacity: Opacity::ONE,
729 rule: FillRule::default(),
730 context_element: None,
731 }
732 }
733}
734
735#[derive(Clone, Copy, PartialEq, Debug)]
737#[allow(missing_docs)]
738pub struct Color {
739 pub red: u8,
740 pub green: u8,
741 pub blue: u8,
742}
743
744impl Color {
745 #[inline]
747 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
748 Color { red, green, blue }
749 }
750
751 #[inline]
753 pub fn black() -> Color {
754 Color::new_rgb(0, 0, 0)
755 }
756
757 #[inline]
759 pub fn white() -> Color {
760 Color::new_rgb(255, 255, 255)
761 }
762}
763
764#[allow(missing_docs)]
768#[derive(Clone, Debug)]
769pub enum Paint {
770 Color(Color),
771 LinearGradient(Arc<LinearGradient>),
772 RadialGradient(Arc<RadialGradient>),
773 Pattern(Arc<Pattern>),
774}
775
776impl PartialEq for Paint {
777 #[inline]
778 fn eq(&self, other: &Self) -> bool {
779 match (self, other) {
780 (Self::Color(lc), Self::Color(rc)) => lc == rc,
781 (Self::LinearGradient(lg1), Self::LinearGradient(lg2)) => Arc::ptr_eq(lg1, lg2),
782 (Self::RadialGradient(rg1), Self::RadialGradient(rg2)) => Arc::ptr_eq(rg1, rg2),
783 (Self::Pattern(p1), Self::Pattern(p2)) => Arc::ptr_eq(p1, p2),
784 _ => false,
785 }
786 }
787}
788
789#[derive(Debug)]
793pub struct ClipPath {
794 pub(crate) id: NonEmptyString,
795 pub(crate) transform: Transform,
796 pub(crate) clip_path: Option<Arc<ClipPath>>,
797 pub(crate) root: Group,
798}
799
800impl ClipPath {
801 pub(crate) fn empty(id: NonEmptyString) -> Self {
802 ClipPath {
803 id,
804 transform: Transform::default(),
805 clip_path: None,
806 root: Group::empty(),
807 }
808 }
809
810 pub fn id(&self) -> &str {
815 self.id.get()
816 }
817
818 pub fn transform(&self) -> Transform {
822 self.transform
823 }
824
825 pub fn clip_path(&self) -> Option<&ClipPath> {
829 self.clip_path.as_deref()
830 }
831
832 pub fn root(&self) -> &Group {
834 &self.root
835 }
836}
837
838#[derive(Clone, Copy, PartialEq, Debug)]
840pub enum MaskType {
841 Luminance,
843 Alpha,
845}
846
847impl Default for MaskType {
848 fn default() -> Self {
849 Self::Luminance
850 }
851}
852
853#[derive(Debug)]
857pub struct Mask {
858 pub(crate) id: NonEmptyString,
859 pub(crate) rect: NonZeroRect,
860 pub(crate) kind: MaskType,
861 pub(crate) mask: Option<Arc<Mask>>,
862 pub(crate) root: Group,
863}
864
865impl Mask {
866 pub fn id(&self) -> &str {
871 self.id.get()
872 }
873
874 pub fn rect(&self) -> NonZeroRect {
878 self.rect
879 }
880
881 pub fn kind(&self) -> MaskType {
885 self.kind
886 }
887
888 pub fn mask(&self) -> Option<&Mask> {
892 self.mask.as_deref()
893 }
894
895 pub fn root(&self) -> &Group {
899 &self.root
900 }
901}
902
903#[allow(missing_docs)]
905#[derive(Clone, Debug)]
906pub enum Node {
907 Group(Box<Group>),
908 Path(Box<Path>),
909 Image(Box<Image>),
910 Text(Box<Text>),
911}
912
913impl Node {
914 pub fn id(&self) -> &str {
916 match self {
917 Node::Group(e) => e.id.as_str(),
918 Node::Path(e) => e.id.as_str(),
919 Node::Image(e) => e.id.as_str(),
920 Node::Text(e) => e.id.as_str(),
921 }
922 }
923
924 pub fn abs_transform(&self) -> Transform {
928 match self {
929 Node::Group(group) => group.abs_transform(),
930 Node::Path(path) => path.abs_transform(),
931 Node::Image(image) => image.abs_transform(),
932 Node::Text(text) => text.abs_transform(),
933 }
934 }
935
936 pub fn bounding_box(&self) -> Rect {
938 match self {
939 Node::Group(group) => group.bounding_box(),
940 Node::Path(path) => path.bounding_box(),
941 Node::Image(image) => image.bounding_box(),
942 Node::Text(text) => text.bounding_box(),
943 }
944 }
945
946 pub fn abs_bounding_box(&self) -> Rect {
948 match self {
949 Node::Group(group) => group.abs_bounding_box(),
950 Node::Path(path) => path.abs_bounding_box(),
951 Node::Image(image) => image.abs_bounding_box(),
952 Node::Text(text) => text.abs_bounding_box(),
953 }
954 }
955
956 pub fn stroke_bounding_box(&self) -> Rect {
958 match self {
959 Node::Group(group) => group.stroke_bounding_box(),
960 Node::Path(path) => path.stroke_bounding_box(),
961 Node::Image(image) => image.bounding_box(),
963 Node::Text(text) => text.stroke_bounding_box(),
964 }
965 }
966
967 pub fn abs_stroke_bounding_box(&self) -> Rect {
969 match self {
970 Node::Group(group) => group.abs_stroke_bounding_box(),
971 Node::Path(path) => path.abs_stroke_bounding_box(),
972 Node::Image(image) => image.abs_bounding_box(),
974 Node::Text(text) => text.abs_stroke_bounding_box(),
975 }
976 }
977
978 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
985 match self {
986 Node::Group(group) => Some(group.abs_layer_bounding_box()),
987 Node::Path(path) => path.abs_bounding_box().to_non_zero_rect(),
989 Node::Image(image) => image.abs_bounding_box().to_non_zero_rect(),
990 Node::Text(text) => text.abs_bounding_box().to_non_zero_rect(),
991 }
992 }
993
994 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
1019 match self {
1020 Node::Group(group) => group.subroots(&mut f),
1021 Node::Path(path) => path.subroots(&mut f),
1022 Node::Image(image) => image.subroots(&mut f),
1023 Node::Text(text) => text.subroots(&mut f),
1024 }
1025 }
1026}
1027
1028#[derive(Clone, Debug)]
1035pub struct Group {
1036 pub(crate) id: String,
1037 pub(crate) transform: Transform,
1038 pub(crate) abs_transform: Transform,
1039 pub(crate) opacity: Opacity,
1040 pub(crate) blend_mode: BlendMode,
1041 pub(crate) isolate: bool,
1042 pub(crate) clip_path: Option<Arc<ClipPath>>,
1043 pub(crate) is_context_element: bool,
1045 pub(crate) mask: Option<Arc<Mask>>,
1046 pub(crate) filters: Vec<Arc<filter::Filter>>,
1047 pub(crate) bounding_box: Rect,
1048 pub(crate) abs_bounding_box: Rect,
1049 pub(crate) stroke_bounding_box: Rect,
1050 pub(crate) abs_stroke_bounding_box: Rect,
1051 pub(crate) layer_bounding_box: NonZeroRect,
1052 pub(crate) abs_layer_bounding_box: NonZeroRect,
1053 pub(crate) children: Vec<Node>,
1054}
1055
1056impl Group {
1057 pub(crate) fn empty() -> Self {
1058 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1059 Group {
1060 id: String::new(),
1061 transform: Transform::default(),
1062 abs_transform: Transform::default(),
1063 opacity: Opacity::ONE,
1064 blend_mode: BlendMode::Normal,
1065 isolate: false,
1066 clip_path: None,
1067 mask: None,
1068 filters: Vec::new(),
1069 is_context_element: false,
1070 bounding_box: dummy,
1071 abs_bounding_box: dummy,
1072 stroke_bounding_box: dummy,
1073 abs_stroke_bounding_box: dummy,
1074 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1075 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1076 children: Vec::new(),
1077 }
1078 }
1079
1080 pub fn id(&self) -> &str {
1086 &self.id
1087 }
1088
1089 pub fn transform(&self) -> Transform {
1093 self.transform
1094 }
1095
1096 pub fn abs_transform(&self) -> Transform {
1103 self.abs_transform
1104 }
1105
1106 pub fn opacity(&self) -> Opacity {
1111 self.opacity
1112 }
1113
1114 pub fn blend_mode(&self) -> BlendMode {
1118 self.blend_mode
1119 }
1120
1121 pub fn isolate(&self) -> bool {
1125 self.isolate
1126 }
1127
1128 pub fn clip_path(&self) -> Option<&ClipPath> {
1130 self.clip_path.as_deref()
1131 }
1132
1133 pub fn mask(&self) -> Option<&Mask> {
1135 self.mask.as_deref()
1136 }
1137
1138 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1140 &self.filters
1141 }
1142
1143 pub fn bounding_box(&self) -> Rect {
1149 self.bounding_box
1150 }
1151
1152 pub fn abs_bounding_box(&self) -> Rect {
1156 self.abs_bounding_box
1157 }
1158
1159 pub fn stroke_bounding_box(&self) -> Rect {
1163 self.stroke_bounding_box
1164 }
1165
1166 pub fn abs_stroke_bounding_box(&self) -> Rect {
1170 self.abs_stroke_bounding_box
1171 }
1172
1173 pub fn layer_bounding_box(&self) -> NonZeroRect {
1187 self.layer_bounding_box
1188 }
1189
1190 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1192 self.abs_layer_bounding_box
1193 }
1194
1195 pub fn children(&self) -> &[Node] {
1197 &self.children
1198 }
1199
1200 pub fn should_isolate(&self) -> bool {
1202 self.isolate
1203 || self.opacity != Opacity::ONE
1204 || self.clip_path.is_some()
1205 || self.mask.is_some()
1206 || !self.filters.is_empty()
1207 || self.blend_mode != BlendMode::Normal }
1209
1210 pub fn has_children(&self) -> bool {
1212 !self.children.is_empty()
1213 }
1214
1215 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1226 let mut full_region = BBox::default();
1227 for filter in &self.filters {
1228 full_region = full_region.expand(filter.rect);
1229 }
1230
1231 full_region.to_non_zero_rect()
1232 }
1233
1234 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1235 if let Some(ref clip) = self.clip_path {
1236 f(&clip.root);
1237
1238 if let Some(ref sub_clip) = clip.clip_path {
1239 f(&sub_clip.root);
1240 }
1241 }
1242
1243 if let Some(ref mask) = self.mask {
1244 f(&mask.root);
1245
1246 if let Some(ref sub_mask) = mask.mask {
1247 f(&sub_mask.root);
1248 }
1249 }
1250
1251 for filter in &self.filters {
1252 for primitive in &filter.primitives {
1253 if let filter::Kind::Image(ref image) = primitive.kind {
1254 f(image.root());
1255 }
1256 }
1257 }
1258 }
1259}
1260
1261#[derive(Clone, Copy, PartialEq, Debug)]
1268#[allow(missing_docs)]
1269pub enum PaintOrder {
1270 FillAndStroke,
1271 StrokeAndFill,
1272}
1273
1274impl Default for PaintOrder {
1275 fn default() -> Self {
1276 Self::FillAndStroke
1277 }
1278}
1279
1280#[derive(Clone, Debug)]
1282pub struct Path {
1283 pub(crate) id: String,
1284 pub(crate) visible: bool,
1285 pub(crate) fill: Option<Fill>,
1286 pub(crate) stroke: Option<Stroke>,
1287 pub(crate) paint_order: PaintOrder,
1288 pub(crate) rendering_mode: ShapeRendering,
1289 pub(crate) data: Arc<tiny_skia_path::Path>,
1290 pub(crate) abs_transform: Transform,
1291 pub(crate) bounding_box: Rect,
1292 pub(crate) abs_bounding_box: Rect,
1293 pub(crate) stroke_bounding_box: Rect,
1294 pub(crate) abs_stroke_bounding_box: Rect,
1295}
1296
1297impl Path {
1298 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1299 Self::new(
1300 String::new(),
1301 true,
1302 None,
1303 None,
1304 PaintOrder::default(),
1305 ShapeRendering::default(),
1306 data,
1307 Transform::default(),
1308 )
1309 }
1310
1311 pub(crate) fn new(
1312 id: String,
1313 visible: bool,
1314 fill: Option<Fill>,
1315 stroke: Option<Stroke>,
1316 paint_order: PaintOrder,
1317 rendering_mode: ShapeRendering,
1318 data: Arc<tiny_skia_path::Path>,
1319 abs_transform: Transform,
1320 ) -> Option<Self> {
1321 let bounding_box = data.compute_tight_bounds()?;
1322 let stroke_bounding_box =
1323 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1324
1325 let abs_bounding_box: Rect;
1326 let abs_stroke_bounding_box: Rect;
1327 if abs_transform.has_skew() {
1328 let path2 = data.as_ref().clone();
1330 let path2 = path2.transform(abs_transform)?;
1331 abs_bounding_box = path2.compute_tight_bounds()?;
1332 abs_stroke_bounding_box =
1333 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1334 } else {
1335 abs_bounding_box = bounding_box.transform(abs_transform)?;
1337 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1338 }
1339
1340 Some(Path {
1341 id,
1342 visible,
1343 fill,
1344 stroke,
1345 paint_order,
1346 rendering_mode,
1347 data,
1348 abs_transform,
1349 bounding_box,
1350 abs_bounding_box,
1351 stroke_bounding_box,
1352 abs_stroke_bounding_box,
1353 })
1354 }
1355
1356 pub fn id(&self) -> &str {
1362 &self.id
1363 }
1364
1365 pub fn is_visible(&self) -> bool {
1367 self.visible
1368 }
1369
1370 pub fn fill(&self) -> Option<&Fill> {
1372 self.fill.as_ref()
1373 }
1374
1375 pub fn stroke(&self) -> Option<&Stroke> {
1377 self.stroke.as_ref()
1378 }
1379
1380 pub fn paint_order(&self) -> PaintOrder {
1387 self.paint_order
1388 }
1389
1390 pub fn rendering_mode(&self) -> ShapeRendering {
1394 self.rendering_mode
1395 }
1396
1397 pub fn data(&self) -> &tiny_skia_path::Path {
1402 self.data.as_ref()
1403 }
1404
1405 pub fn abs_transform(&self) -> Transform {
1412 self.abs_transform
1413 }
1414
1415 pub fn bounding_box(&self) -> Rect {
1419 self.bounding_box
1420 }
1421
1422 pub fn abs_bounding_box(&self) -> Rect {
1426 self.abs_bounding_box
1427 }
1428
1429 pub fn stroke_bounding_box(&self) -> Rect {
1433 self.stroke_bounding_box
1434 }
1435
1436 pub fn abs_stroke_bounding_box(&self) -> Rect {
1440 self.abs_stroke_bounding_box
1441 }
1442
1443 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1444 let mut stroke = stroke?.to_tiny_skia();
1445 stroke.dash = None;
1447
1448 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1452 return stroked_path.compute_tight_bounds();
1453 }
1454
1455 None
1456 }
1457
1458 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1459 if let Some(Paint::Pattern(patt)) = self.fill.as_ref().map(|f| &f.paint) {
1460 f(patt.root());
1461 }
1462 if let Some(Paint::Pattern(patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1463 f(patt.root());
1464 }
1465 }
1466}
1467
1468#[derive(Clone)]
1470pub enum ImageKind {
1471 JPEG(Arc<Vec<u8>>),
1473 PNG(Arc<Vec<u8>>),
1475 GIF(Arc<Vec<u8>>),
1477 WEBP(Arc<Vec<u8>>),
1479 SVG(Tree),
1481}
1482
1483impl ImageKind {
1484 pub(crate) fn actual_size(&self) -> Option<Size> {
1485 match self {
1486 ImageKind::JPEG(data)
1487 | ImageKind::PNG(data)
1488 | ImageKind::GIF(data)
1489 | ImageKind::WEBP(data) => imagesize::blob_size(data)
1490 .ok()
1491 .and_then(|size| Size::from_wh(size.width as f32, size.height as f32))
1492 .log_none(|| log::warn!("Image has an invalid size. Skipped.")),
1493 ImageKind::SVG(svg) => Some(svg.size),
1494 }
1495 }
1496}
1497
1498impl std::fmt::Debug for ImageKind {
1499 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1500 match self {
1501 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1502 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1503 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1504 ImageKind::WEBP(_) => f.write_str("ImageKind::WEBP(..)"),
1505 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1506 }
1507 }
1508}
1509
1510#[derive(Clone, Debug)]
1514pub struct Image {
1515 pub(crate) id: String,
1516 pub(crate) visible: bool,
1517 pub(crate) size: Size,
1518 pub(crate) rendering_mode: ImageRendering,
1519 pub(crate) kind: ImageKind,
1520 pub(crate) abs_transform: Transform,
1521 pub(crate) abs_bounding_box: NonZeroRect,
1522}
1523
1524impl Image {
1525 pub fn id(&self) -> &str {
1531 &self.id
1532 }
1533
1534 pub fn is_visible(&self) -> bool {
1536 self.visible
1537 }
1538
1539 pub fn size(&self) -> Size {
1544 self.size
1545 }
1546
1547 pub fn rendering_mode(&self) -> ImageRendering {
1551 self.rendering_mode
1552 }
1553
1554 pub fn kind(&self) -> &ImageKind {
1556 &self.kind
1557 }
1558
1559 pub fn abs_transform(&self) -> Transform {
1566 self.abs_transform
1567 }
1568
1569 pub fn bounding_box(&self) -> Rect {
1573 self.size.to_rect(0.0, 0.0).unwrap()
1574 }
1575
1576 pub fn abs_bounding_box(&self) -> Rect {
1580 self.abs_bounding_box.to_rect()
1581 }
1582
1583 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1584 if let ImageKind::SVG(ref tree) = self.kind {
1585 f(&tree.root);
1586 }
1587 }
1588}
1589
1590#[allow(missing_debug_implementations)]
1592#[derive(Clone, Debug)]
1593pub struct Tree {
1594 pub(crate) size: Size,
1595 pub(crate) root: Group,
1596 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1597 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1598 pub(crate) patterns: Vec<Arc<Pattern>>,
1599 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1600 pub(crate) masks: Vec<Arc<Mask>>,
1601 pub(crate) filters: Vec<Arc<filter::Filter>>,
1602 #[cfg(feature = "text")]
1603 pub(crate) fontdb: Arc<fontdb::Database>,
1604}
1605
1606impl Tree {
1607 pub fn size(&self) -> Size {
1618 self.size
1619 }
1620
1621 pub fn root(&self) -> &Group {
1623 &self.root
1624 }
1625
1626 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1630 if id.is_empty() {
1631 return None;
1632 }
1633
1634 node_by_id(&self.root, id)
1635 }
1636
1637 pub fn has_text_nodes(&self) -> bool {
1639 has_text_nodes(&self.root)
1640 }
1641
1642 pub fn has_defs_nodes(&self) -> bool {
1644 !self.linear_gradients().is_empty()
1645 || !self.radial_gradients().is_empty()
1646 || !self.patterns().is_empty()
1647 || !self.filters().is_empty()
1648 || !self.clip_paths().is_empty()
1649 || !self.masks().is_empty()
1650 }
1651
1652 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1654 &self.linear_gradients
1655 }
1656
1657 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1659 &self.radial_gradients
1660 }
1661
1662 pub fn patterns(&self) -> &[Arc<Pattern>] {
1664 &self.patterns
1665 }
1666
1667 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1669 &self.clip_paths
1670 }
1671
1672 pub fn masks(&self) -> &[Arc<Mask>] {
1674 &self.masks
1675 }
1676
1677 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1679 &self.filters
1680 }
1681
1682 #[cfg(feature = "text")]
1684 pub fn fontdb(&self) -> &Arc<fontdb::Database> {
1685 &self.fontdb
1686 }
1687
1688 pub(crate) fn collect_paint_servers(&mut self) {
1689 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1690 Paint::Color(_) => {}
1691 Paint::LinearGradient(lg) => {
1692 if !self
1693 .linear_gradients
1694 .iter()
1695 .any(|other| Arc::ptr_eq(lg, other))
1696 {
1697 self.linear_gradients.push(lg.clone());
1698 }
1699 }
1700 Paint::RadialGradient(rg) => {
1701 if !self
1702 .radial_gradients
1703 .iter()
1704 .any(|other| Arc::ptr_eq(rg, other))
1705 {
1706 self.radial_gradients.push(rg.clone());
1707 }
1708 }
1709 Paint::Pattern(patt) => {
1710 if !self.patterns.iter().any(|other| Arc::ptr_eq(patt, other)) {
1711 self.patterns.push(patt.clone());
1712 }
1713 }
1714 });
1715 }
1716}
1717
1718fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1719 for child in &parent.children {
1720 if child.id() == id {
1721 return Some(child);
1722 }
1723
1724 if let Node::Group(g) = child {
1725 if let Some(n) = node_by_id(g, id) {
1726 return Some(n);
1727 }
1728 }
1729 }
1730
1731 None
1732}
1733
1734fn has_text_nodes(root: &Group) -> bool {
1735 for node in &root.children {
1736 if let Node::Text(_) = node {
1737 return true;
1738 }
1739
1740 let mut has_text = false;
1741
1742 if let Node::Image(image) = node {
1743 if let ImageKind::SVG(tree) = &image.kind {
1744 if has_text_nodes(&tree.root) {
1745 has_text = true;
1746 }
1747 }
1748 }
1749
1750 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1751
1752 if has_text {
1753 return true;
1754 }
1755 }
1756
1757 false
1758}
1759
1760fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1761 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1762 if let Some(paint) = paint {
1763 f(paint);
1764 }
1765 }
1766
1767 for node in &parent.children {
1768 match node {
1769 Node::Group(group) => loop_over_paint_servers(group, f),
1770 Node::Path(path) => {
1771 push(path.fill.as_ref().map(|f| &f.paint), f);
1772 push(path.stroke.as_ref().map(|f| &f.paint), f);
1773 }
1774 Node::Image(_) => {}
1775 Node::Text(_) => {}
1777 }
1778
1779 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1780 }
1781}
1782
1783impl Group {
1784 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1785 for node in self.children() {
1786 if let Node::Group(g) = node {
1787 if let Some(clip) = &g.clip_path {
1788 if !clip_paths.iter().any(|other| Arc::ptr_eq(clip, other)) {
1789 clip_paths.push(clip.clone());
1790 }
1791
1792 if let Some(sub_clip) = &clip.clip_path {
1793 if !clip_paths.iter().any(|other| Arc::ptr_eq(sub_clip, other)) {
1794 clip_paths.push(sub_clip.clone());
1795 }
1796 }
1797 }
1798 }
1799
1800 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1801
1802 if let Node::Group(g) = node {
1803 g.collect_clip_paths(clip_paths);
1804 }
1805 }
1806 }
1807
1808 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1809 for node in self.children() {
1810 if let Node::Group(g) = node {
1811 if let Some(mask) = &g.mask {
1812 if !masks.iter().any(|other| Arc::ptr_eq(mask, other)) {
1813 masks.push(mask.clone());
1814 }
1815
1816 if let Some(sub_mask) = &mask.mask {
1817 if !masks.iter().any(|other| Arc::ptr_eq(sub_mask, other)) {
1818 masks.push(sub_mask.clone());
1819 }
1820 }
1821 }
1822 }
1823
1824 node.subroots(|subroot| subroot.collect_masks(masks));
1825
1826 if let Node::Group(g) = node {
1827 g.collect_masks(masks);
1828 }
1829 }
1830 }
1831
1832 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1833 for node in self.children() {
1834 if let Node::Group(g) = node {
1835 for filter in g.filters() {
1836 if !filters.iter().any(|other| Arc::ptr_eq(filter, other)) {
1837 filters.push(filter.clone());
1838 }
1839 }
1840 }
1841
1842 node.subroots(|subroot| subroot.collect_filters(filters));
1843
1844 if let Node::Group(g) = node {
1845 g.collect_filters(filters);
1846 }
1847 }
1848 }
1849
1850 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1851 let mut bbox = BBox::default();
1852 for child in &self.children {
1853 let mut c_bbox = child.bounding_box();
1854 if let Node::Group(group) = child {
1855 if let Some(r) = c_bbox.transform(group.transform) {
1856 c_bbox = r;
1857 }
1858 }
1859
1860 bbox = bbox.expand(c_bbox);
1861 }
1862
1863 bbox.to_non_zero_rect()
1864 }
1865
1866 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1867 let mut bbox = BBox::default();
1868 let mut abs_bbox = BBox::default();
1869 let mut stroke_bbox = BBox::default();
1870 let mut abs_stroke_bbox = BBox::default();
1871 let mut layer_bbox = BBox::default();
1872 for child in &self.children {
1873 {
1874 let mut c_bbox = child.bounding_box();
1875 if let Node::Group(group) = child {
1876 if let Some(r) = c_bbox.transform(group.transform) {
1877 c_bbox = r;
1878 }
1879 }
1880
1881 bbox = bbox.expand(c_bbox);
1882 }
1883
1884 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1885
1886 {
1887 let mut c_bbox = child.stroke_bounding_box();
1888 if let Node::Group(group) = child {
1889 if let Some(r) = c_bbox.transform(group.transform) {
1890 c_bbox = r;
1891 }
1892 }
1893
1894 stroke_bbox = stroke_bbox.expand(c_bbox);
1895 }
1896
1897 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1898
1899 if let Node::Group(group) = child {
1900 let r = group.layer_bounding_box;
1901 if let Some(r) = r.transform(group.transform) {
1902 layer_bbox = layer_bbox.expand(r);
1903 }
1904 } else {
1905 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1907 }
1908 }
1909
1910 if let Some(bbox) = bbox.to_rect() {
1913 self.bounding_box = bbox;
1914 self.abs_bounding_box = abs_bbox.to_rect()?;
1915 self.stroke_bounding_box = stroke_bbox.to_rect()?;
1916 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
1917 }
1918
1919 if let Some(filter_bbox) = self.filters_bounding_box() {
1921 self.layer_bounding_box = filter_bbox;
1922 } else {
1923 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
1924 }
1925
1926 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
1927
1928 Some(())
1929 }
1930}