1pub mod filter;
5mod geom;
6mod text;
7
8use std::sync::Arc;
9
10pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
11
12pub use tiny_skia_path;
13
14pub use self::geom::*;
15pub use self::text::*;
16
17use crate::OptionLog;
18
19pub type Opacity = NormalizedF32;
21
22#[derive(Debug)]
24pub(crate) struct NonEmptyString(String);
25
26impl NonEmptyString {
27 pub(crate) fn new(string: String) -> Option<Self> {
28 if string.trim().is_empty() {
29 return None;
30 }
31
32 Some(NonEmptyString(string))
33 }
34
35 pub(crate) fn get(&self) -> &str {
36 &self.0
37 }
38
39 pub(crate) fn take(self) -> String {
40 self.0
41 }
42}
43
44#[derive(Clone, Copy, Debug)]
48pub struct NonZeroF32(f32);
49
50impl NonZeroF32 {
51 #[inline]
53 pub fn new(n: f32) -> Option<Self> {
54 if n.approx_eq_ulps(&0.0, 4) {
55 None
56 } else {
57 Some(NonZeroF32(n))
58 }
59 }
60
61 #[inline]
63 pub fn get(&self) -> f32 {
64 self.0
65 }
66}
67
68#[derive(Clone, Copy, PartialEq, Debug)]
69pub(crate) enum Units {
70 UserSpaceOnUse,
71 ObjectBoundingBox,
72}
73
74#[allow(missing_docs)]
80#[derive(Clone, Copy, PartialEq, Debug)]
81pub(crate) enum Visibility {
82 Visible,
83 Hidden,
84 Collapse,
85}
86
87impl Default for Visibility {
88 fn default() -> Self {
89 Self::Visible
90 }
91}
92
93#[derive(Clone, Copy, PartialEq, Debug)]
97#[allow(missing_docs)]
98pub enum ShapeRendering {
99 OptimizeSpeed,
100 CrispEdges,
101 GeometricPrecision,
102}
103
104impl ShapeRendering {
105 pub fn use_shape_antialiasing(self) -> bool {
107 match self {
108 ShapeRendering::OptimizeSpeed => false,
109 ShapeRendering::CrispEdges => false,
110 ShapeRendering::GeometricPrecision => true,
111 }
112 }
113}
114
115impl Default for ShapeRendering {
116 fn default() -> Self {
117 Self::GeometricPrecision
118 }
119}
120
121impl std::str::FromStr for ShapeRendering {
122 type Err = &'static str;
123
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 match s {
126 "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
127 "crispEdges" => Ok(ShapeRendering::CrispEdges),
128 "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
129 _ => Err("invalid"),
130 }
131 }
132}
133
134#[allow(missing_docs)]
138#[derive(Clone, Copy, PartialEq, Debug)]
139pub enum TextRendering {
140 OptimizeSpeed,
141 OptimizeLegibility,
142 GeometricPrecision,
143}
144
145impl Default for TextRendering {
146 fn default() -> Self {
147 Self::OptimizeLegibility
148 }
149}
150
151impl std::str::FromStr for TextRendering {
152 type Err = &'static str;
153
154 fn from_str(s: &str) -> Result<Self, Self::Err> {
155 match s {
156 "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
157 "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
158 "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
159 _ => Err("invalid"),
160 }
161 }
162}
163
164#[allow(missing_docs)]
168#[derive(Clone, Copy, PartialEq, Debug)]
169pub enum ImageRendering {
170 OptimizeQuality,
171 OptimizeSpeed,
172 Smooth,
174 HighQuality,
175 CrispEdges,
176 Pixelated,
177}
178
179impl Default for ImageRendering {
180 fn default() -> Self {
181 Self::OptimizeQuality
182 }
183}
184
185impl std::str::FromStr for ImageRendering {
186 type Err = &'static str;
187
188 fn from_str(s: &str) -> Result<Self, Self::Err> {
189 match s {
190 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
191 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
192 "smooth" => Ok(ImageRendering::Smooth),
193 "high-quality" => Ok(ImageRendering::HighQuality),
194 "crisp-edges" => Ok(ImageRendering::CrispEdges),
195 "pixelated" => Ok(ImageRendering::Pixelated),
196 _ => Err("invalid"),
197 }
198 }
199}
200
201#[allow(missing_docs)]
205#[derive(Clone, Copy, PartialEq, Debug)]
206pub enum BlendMode {
207 Normal,
208 Multiply,
209 Screen,
210 Overlay,
211 Darken,
212 Lighten,
213 ColorDodge,
214 ColorBurn,
215 HardLight,
216 SoftLight,
217 Difference,
218 Exclusion,
219 Hue,
220 Saturation,
221 Color,
222 Luminosity,
223}
224
225impl Default for BlendMode {
226 fn default() -> Self {
227 Self::Normal
228 }
229}
230
231#[allow(missing_docs)]
235#[derive(Clone, Copy, PartialEq, Debug)]
236pub enum SpreadMethod {
237 Pad,
238 Reflect,
239 Repeat,
240}
241
242impl Default for SpreadMethod {
243 fn default() -> Self {
244 Self::Pad
245 }
246}
247
248#[derive(Debug)]
250pub struct BaseGradient {
251 pub(crate) id: NonEmptyString,
252 pub(crate) units: Units, pub(crate) transform: Transform,
254 pub(crate) spread_method: SpreadMethod,
255 pub(crate) stops: Vec<Stop>,
256}
257
258impl BaseGradient {
259 pub fn id(&self) -> &str {
264 self.id.get()
265 }
266
267 pub fn transform(&self) -> Transform {
271 self.transform
272 }
273
274 pub fn spread_method(&self) -> SpreadMethod {
278 self.spread_method
279 }
280
281 pub fn stops(&self) -> &[Stop] {
283 &self.stops
284 }
285}
286
287#[derive(Debug)]
291pub struct LinearGradient {
292 pub(crate) base: BaseGradient,
293 pub(crate) x1: f32,
294 pub(crate) y1: f32,
295 pub(crate) x2: f32,
296 pub(crate) y2: f32,
297}
298
299impl LinearGradient {
300 pub fn x1(&self) -> f32 {
302 self.x1
303 }
304
305 pub fn y1(&self) -> f32 {
307 self.y1
308 }
309
310 pub fn x2(&self) -> f32 {
312 self.x2
313 }
314
315 pub fn y2(&self) -> f32 {
317 self.y2
318 }
319}
320
321impl std::ops::Deref for LinearGradient {
322 type Target = BaseGradient;
323
324 fn deref(&self) -> &Self::Target {
325 &self.base
326 }
327}
328
329#[derive(Debug)]
333pub struct RadialGradient {
334 pub(crate) base: BaseGradient,
335 pub(crate) cx: f32,
336 pub(crate) cy: f32,
337 pub(crate) r: PositiveF32,
338 pub(crate) fx: f32,
339 pub(crate) fy: f32,
340}
341
342impl RadialGradient {
343 pub fn cx(&self) -> f32 {
345 self.cx
346 }
347
348 pub fn cy(&self) -> f32 {
350 self.cy
351 }
352
353 pub fn r(&self) -> PositiveF32 {
355 self.r
356 }
357
358 pub fn fx(&self) -> f32 {
360 self.fx
361 }
362
363 pub fn fy(&self) -> f32 {
365 self.fy
366 }
367}
368
369impl std::ops::Deref for RadialGradient {
370 type Target = BaseGradient;
371
372 fn deref(&self) -> &Self::Target {
373 &self.base
374 }
375}
376
377pub type StopOffset = NormalizedF32;
379
380#[derive(Clone, Copy, Debug)]
384pub struct Stop {
385 pub(crate) offset: StopOffset,
386 pub(crate) color: Color,
387 pub(crate) opacity: Opacity,
388}
389
390impl Stop {
391 pub fn offset(&self) -> StopOffset {
395 self.offset
396 }
397
398 pub fn color(&self) -> Color {
402 self.color
403 }
404
405 pub fn opacity(&self) -> Opacity {
409 self.opacity
410 }
411}
412
413#[derive(Debug)]
417pub struct Pattern {
418 pub(crate) id: NonEmptyString,
419 pub(crate) units: Units, pub(crate) content_units: Units, pub(crate) transform: Transform,
422 pub(crate) rect: NonZeroRect,
423 pub(crate) view_box: Option<ViewBox>,
424 pub(crate) root: Group,
425}
426
427impl Pattern {
428 pub fn id(&self) -> &str {
433 self.id.get()
434 }
435
436 pub fn transform(&self) -> Transform {
440 self.transform
441 }
442
443 pub fn rect(&self) -> NonZeroRect {
447 self.rect
448 }
449
450 pub fn root(&self) -> &Group {
452 &self.root
453 }
454}
455
456pub type StrokeWidth = NonZeroPositiveF32;
458
459#[derive(Clone, Copy, Debug)]
463pub struct StrokeMiterlimit(f32);
464
465impl StrokeMiterlimit {
466 #[inline]
468 pub fn new(n: f32) -> Self {
469 debug_assert!(n.is_finite());
470 debug_assert!(n >= 1.0);
471
472 let n = if !(n >= 1.0) { 1.0 } else { n };
473
474 StrokeMiterlimit(n)
475 }
476
477 #[inline]
479 pub fn get(&self) -> f32 {
480 self.0
481 }
482}
483
484impl Default for StrokeMiterlimit {
485 #[inline]
486 fn default() -> Self {
487 StrokeMiterlimit::new(4.0)
488 }
489}
490
491impl From<f32> for StrokeMiterlimit {
492 #[inline]
493 fn from(n: f32) -> Self {
494 Self::new(n)
495 }
496}
497
498impl PartialEq for StrokeMiterlimit {
499 #[inline]
500 fn eq(&self, other: &Self) -> bool {
501 self.0.approx_eq_ulps(&other.0, 4)
502 }
503}
504
505#[allow(missing_docs)]
509#[derive(Clone, Copy, PartialEq, Debug)]
510pub enum LineCap {
511 Butt,
512 Round,
513 Square,
514}
515
516impl Default for LineCap {
517 fn default() -> Self {
518 Self::Butt
519 }
520}
521
522#[allow(missing_docs)]
526#[derive(Clone, Copy, PartialEq, Debug)]
527pub enum LineJoin {
528 Miter,
529 MiterClip,
530 Round,
531 Bevel,
532}
533
534impl Default for LineJoin {
535 fn default() -> Self {
536 Self::Miter
537 }
538}
539
540#[derive(Clone, Debug)]
542pub struct Stroke {
543 pub(crate) paint: Paint,
544 pub(crate) dasharray: Option<Vec<f32>>,
545 pub(crate) dashoffset: f32,
546 pub(crate) miterlimit: StrokeMiterlimit,
547 pub(crate) opacity: Opacity,
548 pub(crate) width: StrokeWidth,
549 pub(crate) linecap: LineCap,
550 pub(crate) linejoin: LineJoin,
551 pub(crate) context_element: Option<ContextElement>,
554}
555
556impl Stroke {
557 pub fn paint(&self) -> &Paint {
559 &self.paint
560 }
561
562 pub fn dasharray(&self) -> Option<&[f32]> {
564 self.dasharray.as_deref()
565 }
566
567 pub fn dashoffset(&self) -> f32 {
569 self.dashoffset
570 }
571
572 pub fn miterlimit(&self) -> StrokeMiterlimit {
574 self.miterlimit
575 }
576
577 pub fn opacity(&self) -> Opacity {
579 self.opacity
580 }
581
582 pub fn width(&self) -> StrokeWidth {
584 self.width
585 }
586
587 pub fn linecap(&self) -> LineCap {
589 self.linecap
590 }
591
592 pub fn linejoin(&self) -> LineJoin {
594 self.linejoin
595 }
596
597 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
599 let mut stroke = tiny_skia_path::Stroke {
600 width: self.width.get(),
601 miter_limit: self.miterlimit.get(),
602 line_cap: match self.linecap {
603 LineCap::Butt => tiny_skia_path::LineCap::Butt,
604 LineCap::Round => tiny_skia_path::LineCap::Round,
605 LineCap::Square => tiny_skia_path::LineCap::Square,
606 },
607 line_join: match self.linejoin {
608 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
609 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
610 LineJoin::Round => tiny_skia_path::LineJoin::Round,
611 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
612 },
613 dash: None,
616 };
617
618 if let Some(ref list) = self.dasharray {
619 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
620 }
621
622 stroke
623 }
624}
625
626#[allow(missing_docs)]
630#[derive(Clone, Copy, PartialEq, Debug)]
631pub enum FillRule {
632 NonZero,
633 EvenOdd,
634}
635
636impl Default for FillRule {
637 fn default() -> Self {
638 Self::NonZero
639 }
640}
641
642#[derive(Clone, Copy, Debug)]
643pub(crate) enum ContextElement {
644 UseNode,
649 PathNode(Transform, Option<NonZeroRect>),
654}
655
656#[derive(Clone, Debug)]
658pub struct Fill {
659 pub(crate) paint: Paint,
660 pub(crate) opacity: Opacity,
661 pub(crate) rule: FillRule,
662 pub(crate) context_element: Option<ContextElement>,
665}
666
667impl Fill {
668 pub fn paint(&self) -> &Paint {
670 &self.paint
671 }
672
673 pub fn opacity(&self) -> Opacity {
675 self.opacity
676 }
677
678 pub fn rule(&self) -> FillRule {
680 self.rule
681 }
682}
683
684impl Default for Fill {
685 fn default() -> Self {
686 Fill {
687 paint: Paint::Color(Color::black()),
688 opacity: Opacity::ONE,
689 rule: FillRule::default(),
690 context_element: None,
691 }
692 }
693}
694
695#[derive(Clone, Copy, PartialEq, Debug)]
697#[allow(missing_docs)]
698pub struct Color {
699 pub red: u8,
700 pub green: u8,
701 pub blue: u8,
702}
703
704impl Color {
705 #[inline]
707 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
708 Color { red, green, blue }
709 }
710
711 #[inline]
713 pub fn black() -> Color {
714 Color::new_rgb(0, 0, 0)
715 }
716
717 #[inline]
719 pub fn white() -> Color {
720 Color::new_rgb(255, 255, 255)
721 }
722}
723
724#[allow(missing_docs)]
728#[derive(Clone, Debug)]
729pub enum Paint {
730 Color(Color),
731 LinearGradient(Arc<LinearGradient>),
732 RadialGradient(Arc<RadialGradient>),
733 Pattern(Arc<Pattern>),
734}
735
736impl PartialEq for Paint {
737 #[inline]
738 fn eq(&self, other: &Self) -> bool {
739 match (self, other) {
740 (Self::Color(lc), Self::Color(rc)) => lc == rc,
741 (Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Arc::ptr_eq(lg1, lg2),
742 (Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Arc::ptr_eq(rg1, rg2),
743 (Self::Pattern(ref p1), Self::Pattern(ref p2)) => Arc::ptr_eq(p1, p2),
744 _ => false,
745 }
746 }
747}
748
749#[derive(Debug)]
753pub struct ClipPath {
754 pub(crate) id: NonEmptyString,
755 pub(crate) transform: Transform,
756 pub(crate) clip_path: Option<Arc<ClipPath>>,
757 pub(crate) root: Group,
758}
759
760impl ClipPath {
761 pub(crate) fn empty(id: NonEmptyString) -> Self {
762 ClipPath {
763 id,
764 transform: Transform::default(),
765 clip_path: None,
766 root: Group::empty(),
767 }
768 }
769
770 pub fn id(&self) -> &str {
775 self.id.get()
776 }
777
778 pub fn transform(&self) -> Transform {
782 self.transform
783 }
784
785 pub fn clip_path(&self) -> Option<&ClipPath> {
789 self.clip_path.as_deref()
790 }
791
792 pub fn root(&self) -> &Group {
794 &self.root
795 }
796}
797
798#[derive(Clone, Copy, PartialEq, Debug)]
800pub enum MaskType {
801 Luminance,
803 Alpha,
805}
806
807impl Default for MaskType {
808 fn default() -> Self {
809 Self::Luminance
810 }
811}
812
813#[derive(Debug)]
817pub struct Mask {
818 pub(crate) id: NonEmptyString,
819 pub(crate) rect: NonZeroRect,
820 pub(crate) kind: MaskType,
821 pub(crate) mask: Option<Arc<Mask>>,
822 pub(crate) root: Group,
823}
824
825impl Mask {
826 pub fn id(&self) -> &str {
831 self.id.get()
832 }
833
834 pub fn rect(&self) -> NonZeroRect {
838 self.rect
839 }
840
841 pub fn kind(&self) -> MaskType {
845 self.kind
846 }
847
848 pub fn mask(&self) -> Option<&Mask> {
852 self.mask.as_deref()
853 }
854
855 pub fn root(&self) -> &Group {
859 &self.root
860 }
861}
862
863#[allow(missing_docs)]
865#[derive(Clone, Debug)]
866pub enum Node {
867 Group(Box<Group>),
868 Path(Box<Path>),
869 Image(Box<Image>),
870 Text(Box<Text>),
871}
872
873impl Node {
874 pub fn id(&self) -> &str {
876 match self {
877 Node::Group(ref e) => e.id.as_str(),
878 Node::Path(ref e) => e.id.as_str(),
879 Node::Image(ref e) => e.id.as_str(),
880 Node::Text(ref e) => e.id.as_str(),
881 }
882 }
883
884 pub fn abs_transform(&self) -> Transform {
888 match self {
889 Node::Group(ref group) => group.abs_transform(),
890 Node::Path(ref path) => path.abs_transform(),
891 Node::Image(ref image) => image.abs_transform(),
892 Node::Text(ref text) => text.abs_transform(),
893 }
894 }
895
896 pub fn bounding_box(&self) -> Rect {
898 match self {
899 Node::Group(ref group) => group.bounding_box(),
900 Node::Path(ref path) => path.bounding_box(),
901 Node::Image(ref image) => image.bounding_box(),
902 Node::Text(ref text) => text.bounding_box(),
903 }
904 }
905
906 pub fn abs_bounding_box(&self) -> Rect {
908 match self {
909 Node::Group(ref group) => group.abs_bounding_box(),
910 Node::Path(ref path) => path.abs_bounding_box(),
911 Node::Image(ref image) => image.abs_bounding_box(),
912 Node::Text(ref text) => text.abs_bounding_box(),
913 }
914 }
915
916 pub fn stroke_bounding_box(&self) -> Rect {
918 match self {
919 Node::Group(ref group) => group.stroke_bounding_box(),
920 Node::Path(ref path) => path.stroke_bounding_box(),
921 Node::Image(ref image) => image.bounding_box(),
923 Node::Text(ref text) => text.stroke_bounding_box(),
924 }
925 }
926
927 pub fn abs_stroke_bounding_box(&self) -> Rect {
929 match self {
930 Node::Group(ref group) => group.abs_stroke_bounding_box(),
931 Node::Path(ref path) => path.abs_stroke_bounding_box(),
932 Node::Image(ref image) => image.abs_bounding_box(),
934 Node::Text(ref text) => text.abs_stroke_bounding_box(),
935 }
936 }
937
938 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
945 match self {
946 Node::Group(ref group) => Some(group.abs_layer_bounding_box()),
947 Node::Path(ref path) => path.abs_bounding_box().to_non_zero_rect(),
949 Node::Image(ref image) => image.abs_bounding_box().to_non_zero_rect(),
950 Node::Text(ref text) => text.abs_bounding_box().to_non_zero_rect(),
951 }
952 }
953
954 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
979 match self {
980 Node::Group(ref group) => group.subroots(&mut f),
981 Node::Path(ref path) => path.subroots(&mut f),
982 Node::Image(ref image) => image.subroots(&mut f),
983 Node::Text(ref text) => text.subroots(&mut f),
984 }
985 }
986}
987
988#[derive(Clone, Debug)]
995pub struct Group {
996 pub(crate) id: String,
997 pub(crate) transform: Transform,
998 pub(crate) abs_transform: Transform,
999 pub(crate) opacity: Opacity,
1000 pub(crate) blend_mode: BlendMode,
1001 pub(crate) isolate: bool,
1002 pub(crate) clip_path: Option<Arc<ClipPath>>,
1003 pub(crate) is_context_element: bool,
1005 pub(crate) mask: Option<Arc<Mask>>,
1006 pub(crate) filters: Vec<Arc<filter::Filter>>,
1007 pub(crate) bounding_box: Rect,
1008 pub(crate) abs_bounding_box: Rect,
1009 pub(crate) stroke_bounding_box: Rect,
1010 pub(crate) abs_stroke_bounding_box: Rect,
1011 pub(crate) layer_bounding_box: NonZeroRect,
1012 pub(crate) abs_layer_bounding_box: NonZeroRect,
1013 pub(crate) children: Vec<Node>,
1014}
1015
1016impl Group {
1017 pub(crate) fn empty() -> Self {
1018 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1019 Group {
1020 id: String::new(),
1021 transform: Transform::default(),
1022 abs_transform: Transform::default(),
1023 opacity: Opacity::ONE,
1024 blend_mode: BlendMode::Normal,
1025 isolate: false,
1026 clip_path: None,
1027 mask: None,
1028 filters: Vec::new(),
1029 is_context_element: false,
1030 bounding_box: dummy,
1031 abs_bounding_box: dummy,
1032 stroke_bounding_box: dummy,
1033 abs_stroke_bounding_box: dummy,
1034 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1035 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1036 children: Vec::new(),
1037 }
1038 }
1039
1040 pub fn id(&self) -> &str {
1046 &self.id
1047 }
1048
1049 pub fn transform(&self) -> Transform {
1053 self.transform
1054 }
1055
1056 pub fn abs_transform(&self) -> Transform {
1063 self.abs_transform
1064 }
1065
1066 pub fn opacity(&self) -> Opacity {
1071 self.opacity
1072 }
1073
1074 pub fn blend_mode(&self) -> BlendMode {
1078 self.blend_mode
1079 }
1080
1081 pub fn isolate(&self) -> bool {
1085 self.isolate
1086 }
1087
1088 pub fn clip_path(&self) -> Option<&ClipPath> {
1090 self.clip_path.as_deref()
1091 }
1092
1093 pub fn mask(&self) -> Option<&Mask> {
1095 self.mask.as_deref()
1096 }
1097
1098 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1100 &self.filters
1101 }
1102
1103 pub fn bounding_box(&self) -> Rect {
1109 self.bounding_box
1110 }
1111
1112 pub fn abs_bounding_box(&self) -> Rect {
1116 self.abs_bounding_box
1117 }
1118
1119 pub fn stroke_bounding_box(&self) -> Rect {
1123 self.stroke_bounding_box
1124 }
1125
1126 pub fn abs_stroke_bounding_box(&self) -> Rect {
1130 self.abs_stroke_bounding_box
1131 }
1132
1133 pub fn layer_bounding_box(&self) -> NonZeroRect {
1147 self.layer_bounding_box
1148 }
1149
1150 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1152 self.abs_layer_bounding_box
1153 }
1154
1155 pub fn children(&self) -> &[Node] {
1157 &self.children
1158 }
1159
1160 pub fn should_isolate(&self) -> bool {
1162 self.isolate
1163 || self.opacity != Opacity::ONE
1164 || self.clip_path.is_some()
1165 || self.mask.is_some()
1166 || !self.filters.is_empty()
1167 || self.blend_mode != BlendMode::Normal }
1169
1170 pub fn has_children(&self) -> bool {
1172 !self.children.is_empty()
1173 }
1174
1175 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1186 let mut full_region = BBox::default();
1187 for filter in &self.filters {
1188 full_region = full_region.expand(filter.rect);
1189 }
1190
1191 full_region.to_non_zero_rect()
1192 }
1193
1194 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1195 if let Some(ref clip) = self.clip_path {
1196 f(&clip.root);
1197
1198 if let Some(ref sub_clip) = clip.clip_path {
1199 f(&sub_clip.root);
1200 }
1201 }
1202
1203 if let Some(ref mask) = self.mask {
1204 f(&mask.root);
1205
1206 if let Some(ref sub_mask) = mask.mask {
1207 f(&sub_mask.root);
1208 }
1209 }
1210
1211 for filter in &self.filters {
1212 for primitive in &filter.primitives {
1213 if let filter::Kind::Image(ref image) = primitive.kind {
1214 f(image.root());
1215 }
1216 }
1217 }
1218 }
1219}
1220
1221#[derive(Clone, Copy, PartialEq, Debug)]
1228#[allow(missing_docs)]
1229pub enum PaintOrder {
1230 FillAndStroke,
1231 StrokeAndFill,
1232}
1233
1234impl Default for PaintOrder {
1235 fn default() -> Self {
1236 Self::FillAndStroke
1237 }
1238}
1239
1240#[derive(Clone, Debug)]
1242pub struct Path {
1243 pub(crate) id: String,
1244 pub(crate) visible: bool,
1245 pub(crate) fill: Option<Fill>,
1246 pub(crate) stroke: Option<Stroke>,
1247 pub(crate) paint_order: PaintOrder,
1248 pub(crate) rendering_mode: ShapeRendering,
1249 pub(crate) data: Arc<tiny_skia_path::Path>,
1250 pub(crate) abs_transform: Transform,
1251 pub(crate) bounding_box: Rect,
1252 pub(crate) abs_bounding_box: Rect,
1253 pub(crate) stroke_bounding_box: Rect,
1254 pub(crate) abs_stroke_bounding_box: Rect,
1255}
1256
1257impl Path {
1258 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1259 Self::new(
1260 String::new(),
1261 true,
1262 None,
1263 None,
1264 PaintOrder::default(),
1265 ShapeRendering::default(),
1266 data,
1267 Transform::default(),
1268 )
1269 }
1270
1271 pub(crate) fn new(
1272 id: String,
1273 visible: bool,
1274 fill: Option<Fill>,
1275 stroke: Option<Stroke>,
1276 paint_order: PaintOrder,
1277 rendering_mode: ShapeRendering,
1278 data: Arc<tiny_skia_path::Path>,
1279 abs_transform: Transform,
1280 ) -> Option<Self> {
1281 let bounding_box = data.compute_tight_bounds()?;
1282 let stroke_bounding_box =
1283 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1284
1285 let abs_bounding_box: Rect;
1286 let abs_stroke_bounding_box: Rect;
1287 if abs_transform.has_skew() {
1288 let path2 = data.as_ref().clone();
1290 let path2 = path2.transform(abs_transform)?;
1291 abs_bounding_box = path2.compute_tight_bounds()?;
1292 abs_stroke_bounding_box =
1293 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1294 } else {
1295 abs_bounding_box = bounding_box.transform(abs_transform)?;
1297 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1298 }
1299
1300 Some(Path {
1301 id,
1302 visible,
1303 fill,
1304 stroke,
1305 paint_order,
1306 rendering_mode,
1307 data,
1308 abs_transform,
1309 bounding_box,
1310 abs_bounding_box,
1311 stroke_bounding_box,
1312 abs_stroke_bounding_box,
1313 })
1314 }
1315
1316 pub fn id(&self) -> &str {
1322 &self.id
1323 }
1324
1325 pub fn is_visible(&self) -> bool {
1327 self.visible
1328 }
1329
1330 pub fn fill(&self) -> Option<&Fill> {
1332 self.fill.as_ref()
1333 }
1334
1335 pub fn stroke(&self) -> Option<&Stroke> {
1337 self.stroke.as_ref()
1338 }
1339
1340 pub fn paint_order(&self) -> PaintOrder {
1347 self.paint_order
1348 }
1349
1350 pub fn rendering_mode(&self) -> ShapeRendering {
1354 self.rendering_mode
1355 }
1356
1357 pub fn data(&self) -> &tiny_skia_path::Path {
1362 self.data.as_ref()
1363 }
1364
1365 pub fn abs_transform(&self) -> Transform {
1372 self.abs_transform
1373 }
1374
1375 pub fn bounding_box(&self) -> Rect {
1379 self.bounding_box
1380 }
1381
1382 pub fn abs_bounding_box(&self) -> Rect {
1386 self.abs_bounding_box
1387 }
1388
1389 pub fn stroke_bounding_box(&self) -> Rect {
1393 self.stroke_bounding_box
1394 }
1395
1396 pub fn abs_stroke_bounding_box(&self) -> Rect {
1400 self.abs_stroke_bounding_box
1401 }
1402
1403 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1404 let mut stroke = stroke?.to_tiny_skia();
1405 stroke.dash = None;
1407
1408 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1412 return stroked_path.compute_tight_bounds();
1413 }
1414
1415 None
1416 }
1417
1418 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1419 if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
1420 f(patt.root())
1421 }
1422 if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1423 f(patt.root())
1424 }
1425 }
1426}
1427
1428#[derive(Clone)]
1430pub enum ImageKind {
1431 JPEG(Arc<Vec<u8>>),
1433 PNG(Arc<Vec<u8>>),
1435 GIF(Arc<Vec<u8>>),
1437 WEBP(Arc<Vec<u8>>),
1439 SVG(Tree),
1441}
1442
1443impl ImageKind {
1444 pub(crate) fn actual_size(&self) -> Option<Size> {
1445 match self {
1446 ImageKind::JPEG(ref data)
1447 | ImageKind::PNG(ref data)
1448 | ImageKind::GIF(ref data)
1449 | ImageKind::WEBP(ref data) => imagesize::blob_size(data)
1450 .ok()
1451 .and_then(|size| Size::from_wh(size.width as f32, size.height as f32))
1452 .log_none(|| log::warn!("Image has an invalid size. Skipped.")),
1453 ImageKind::SVG(ref svg) => Some(svg.size),
1454 }
1455 }
1456}
1457
1458impl std::fmt::Debug for ImageKind {
1459 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1460 match self {
1461 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1462 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1463 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1464 ImageKind::WEBP(_) => f.write_str("ImageKind::WEBP(..)"),
1465 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1466 }
1467 }
1468}
1469
1470#[derive(Clone, Debug)]
1474pub struct Image {
1475 pub(crate) id: String,
1476 pub(crate) visible: bool,
1477 pub(crate) size: Size,
1478 pub(crate) rendering_mode: ImageRendering,
1479 pub(crate) kind: ImageKind,
1480 pub(crate) abs_transform: Transform,
1481 pub(crate) abs_bounding_box: NonZeroRect,
1482}
1483
1484impl Image {
1485 pub fn id(&self) -> &str {
1491 &self.id
1492 }
1493
1494 pub fn is_visible(&self) -> bool {
1496 self.visible
1497 }
1498
1499 pub fn size(&self) -> Size {
1504 self.size
1505 }
1506
1507 pub fn rendering_mode(&self) -> ImageRendering {
1511 self.rendering_mode
1512 }
1513
1514 pub fn kind(&self) -> &ImageKind {
1516 &self.kind
1517 }
1518
1519 pub fn abs_transform(&self) -> Transform {
1526 self.abs_transform
1527 }
1528
1529 pub fn bounding_box(&self) -> Rect {
1533 self.size.to_rect(0.0, 0.0).unwrap()
1534 }
1535
1536 pub fn abs_bounding_box(&self) -> Rect {
1540 self.abs_bounding_box.to_rect()
1541 }
1542
1543 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1544 if let ImageKind::SVG(ref tree) = self.kind {
1545 f(&tree.root)
1546 }
1547 }
1548}
1549
1550#[allow(missing_debug_implementations)]
1552#[derive(Clone, Debug)]
1553pub struct Tree {
1554 pub(crate) size: Size,
1555 pub(crate) root: Group,
1556 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1557 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1558 pub(crate) patterns: Vec<Arc<Pattern>>,
1559 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1560 pub(crate) masks: Vec<Arc<Mask>>,
1561 pub(crate) filters: Vec<Arc<filter::Filter>>,
1562 #[cfg(feature = "text")]
1563 pub(crate) fontdb: Arc<fontdb::Database>,
1564}
1565
1566impl Tree {
1567 pub fn size(&self) -> Size {
1573 self.size
1574 }
1575
1576 pub fn root(&self) -> &Group {
1578 &self.root
1579 }
1580
1581 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1585 if id.is_empty() {
1586 return None;
1587 }
1588
1589 node_by_id(&self.root, id)
1590 }
1591
1592 pub fn has_text_nodes(&self) -> bool {
1594 has_text_nodes(&self.root)
1595 }
1596
1597 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1599 &self.linear_gradients
1600 }
1601
1602 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1604 &self.radial_gradients
1605 }
1606
1607 pub fn patterns(&self) -> &[Arc<Pattern>] {
1609 &self.patterns
1610 }
1611
1612 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1614 &self.clip_paths
1615 }
1616
1617 pub fn masks(&self) -> &[Arc<Mask>] {
1619 &self.masks
1620 }
1621
1622 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1624 &self.filters
1625 }
1626
1627 #[cfg(feature = "text")]
1629 pub fn fontdb(&self) -> &Arc<fontdb::Database> {
1630 &self.fontdb
1631 }
1632
1633 pub(crate) fn collect_paint_servers(&mut self) {
1634 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1635 Paint::Color(_) => {}
1636 Paint::LinearGradient(lg) => {
1637 if !self
1638 .linear_gradients
1639 .iter()
1640 .any(|other| Arc::ptr_eq(lg, other))
1641 {
1642 self.linear_gradients.push(lg.clone());
1643 }
1644 }
1645 Paint::RadialGradient(rg) => {
1646 if !self
1647 .radial_gradients
1648 .iter()
1649 .any(|other| Arc::ptr_eq(rg, other))
1650 {
1651 self.radial_gradients.push(rg.clone());
1652 }
1653 }
1654 Paint::Pattern(patt) => {
1655 if !self.patterns.iter().any(|other| Arc::ptr_eq(patt, other)) {
1656 self.patterns.push(patt.clone());
1657 }
1658 }
1659 });
1660 }
1661}
1662
1663fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1664 for child in &parent.children {
1665 if child.id() == id {
1666 return Some(child);
1667 }
1668
1669 if let Node::Group(ref g) = child {
1670 if let Some(n) = node_by_id(g, id) {
1671 return Some(n);
1672 }
1673 }
1674 }
1675
1676 None
1677}
1678
1679fn has_text_nodes(root: &Group) -> bool {
1680 for node in &root.children {
1681 if let Node::Text(_) = node {
1682 return true;
1683 }
1684
1685 let mut has_text = false;
1686
1687 if let Node::Image(ref image) = node {
1688 if let ImageKind::SVG(ref tree) = image.kind {
1689 if has_text_nodes(&tree.root) {
1690 has_text = true;
1691 }
1692 }
1693 }
1694
1695 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1696
1697 if has_text {
1698 return true;
1699 }
1700 }
1701
1702 true
1703}
1704
1705fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1706 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1707 if let Some(paint) = paint {
1708 f(paint);
1709 }
1710 }
1711
1712 for node in &parent.children {
1713 match node {
1714 Node::Group(ref group) => loop_over_paint_servers(group, f),
1715 Node::Path(ref path) => {
1716 push(path.fill.as_ref().map(|f| &f.paint), f);
1717 push(path.stroke.as_ref().map(|f| &f.paint), f);
1718 }
1719 Node::Image(_) => {}
1720 Node::Text(_) => {}
1722 }
1723
1724 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1725 }
1726}
1727
1728impl Group {
1729 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1730 for node in self.children() {
1731 if let Node::Group(ref g) = node {
1732 if let Some(ref clip) = g.clip_path {
1733 if !clip_paths.iter().any(|other| Arc::ptr_eq(clip, other)) {
1734 clip_paths.push(clip.clone());
1735 }
1736
1737 if let Some(ref sub_clip) = clip.clip_path {
1738 if !clip_paths.iter().any(|other| Arc::ptr_eq(sub_clip, other)) {
1739 clip_paths.push(sub_clip.clone());
1740 }
1741 }
1742 }
1743 }
1744
1745 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1746
1747 if let Node::Group(ref g) = node {
1748 g.collect_clip_paths(clip_paths);
1749 }
1750 }
1751 }
1752
1753 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1754 for node in self.children() {
1755 if let Node::Group(ref g) = node {
1756 if let Some(ref mask) = g.mask {
1757 if !masks.iter().any(|other| Arc::ptr_eq(mask, other)) {
1758 masks.push(mask.clone());
1759 }
1760
1761 if let Some(ref sub_mask) = mask.mask {
1762 if !masks.iter().any(|other| Arc::ptr_eq(sub_mask, other)) {
1763 masks.push(sub_mask.clone());
1764 }
1765 }
1766 }
1767 }
1768
1769 node.subroots(|subroot| subroot.collect_masks(masks));
1770
1771 if let Node::Group(ref g) = node {
1772 g.collect_masks(masks);
1773 }
1774 }
1775 }
1776
1777 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1778 for node in self.children() {
1779 if let Node::Group(ref g) = node {
1780 for filter in g.filters() {
1781 if !filters.iter().any(|other| Arc::ptr_eq(filter, other)) {
1782 filters.push(filter.clone());
1783 }
1784 }
1785 }
1786
1787 node.subroots(|subroot| subroot.collect_filters(filters));
1788
1789 if let Node::Group(ref g) = node {
1790 g.collect_filters(filters);
1791 }
1792 }
1793 }
1794
1795 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1796 let mut bbox = BBox::default();
1797 for child in &self.children {
1798 let mut c_bbox = child.bounding_box();
1799 if let Node::Group(ref group) = child {
1800 if let Some(r) = c_bbox.transform(group.transform) {
1801 c_bbox = r;
1802 }
1803 }
1804
1805 bbox = bbox.expand(c_bbox);
1806 }
1807
1808 bbox.to_non_zero_rect()
1809 }
1810
1811 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1812 let mut bbox = BBox::default();
1813 let mut abs_bbox = BBox::default();
1814 let mut stroke_bbox = BBox::default();
1815 let mut abs_stroke_bbox = BBox::default();
1816 let mut layer_bbox = BBox::default();
1817 for child in &self.children {
1818 {
1819 let mut c_bbox = child.bounding_box();
1820 if let Node::Group(ref group) = child {
1821 if let Some(r) = c_bbox.transform(group.transform) {
1822 c_bbox = r;
1823 }
1824 }
1825
1826 bbox = bbox.expand(c_bbox);
1827 }
1828
1829 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1830
1831 {
1832 let mut c_bbox = child.stroke_bounding_box();
1833 if let Node::Group(ref group) = child {
1834 if let Some(r) = c_bbox.transform(group.transform) {
1835 c_bbox = r;
1836 }
1837 }
1838
1839 stroke_bbox = stroke_bbox.expand(c_bbox);
1840 }
1841
1842 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1843
1844 if let Node::Group(ref group) = child {
1845 let r = group.layer_bounding_box;
1846 if let Some(r) = r.transform(group.transform) {
1847 layer_bbox = layer_bbox.expand(r);
1848 }
1849 } else {
1850 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1852 }
1853 }
1854
1855 if let Some(bbox) = bbox.to_rect() {
1858 self.bounding_box = bbox;
1859 self.abs_bounding_box = abs_bbox.to_rect()?;
1860 self.stroke_bounding_box = stroke_bbox.to_rect()?;
1861 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
1862 }
1863
1864 if let Some(filter_bbox) = self.filters_bounding_box() {
1866 self.layer_bounding_box = filter_bbox;
1867 } else {
1868 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
1869 }
1870
1871 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
1872
1873 Some(())
1874 }
1875}