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 CurrentColor,
731 Color(Color),
732 LinearGradient(Arc<LinearGradient>),
733 RadialGradient(Arc<RadialGradient>),
734 Pattern(Arc<Pattern>),
735}
736
737impl PartialEq for Paint {
738 #[inline]
739 fn eq(&self, other: &Self) -> bool {
740 match (self, other) {
741 (Self::Color(lc), Self::Color(rc)) => lc == rc,
742 (Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Arc::ptr_eq(lg1, lg2),
743 (Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Arc::ptr_eq(rg1, rg2),
744 (Self::Pattern(ref p1), Self::Pattern(ref p2)) => Arc::ptr_eq(p1, p2),
745 _ => false,
746 }
747 }
748}
749
750#[derive(Debug)]
754pub struct ClipPath {
755 pub(crate) id: NonEmptyString,
756 pub(crate) transform: Transform,
757 pub(crate) clip_path: Option<Arc<ClipPath>>,
758 pub(crate) root: Group,
759}
760
761impl ClipPath {
762 pub(crate) fn empty(id: NonEmptyString) -> Self {
763 ClipPath {
764 id,
765 transform: Transform::default(),
766 clip_path: None,
767 root: Group::empty(),
768 }
769 }
770
771 pub fn id(&self) -> &str {
776 self.id.get()
777 }
778
779 pub fn transform(&self) -> Transform {
783 self.transform
784 }
785
786 pub fn clip_path(&self) -> Option<&ClipPath> {
790 self.clip_path.as_deref()
791 }
792
793 pub fn root(&self) -> &Group {
795 &self.root
796 }
797}
798
799#[derive(Clone, Copy, PartialEq, Debug)]
801pub enum MaskType {
802 Luminance,
804 Alpha,
806}
807
808impl Default for MaskType {
809 fn default() -> Self {
810 Self::Luminance
811 }
812}
813
814#[derive(Debug)]
818pub struct Mask {
819 pub(crate) id: NonEmptyString,
820 pub(crate) rect: NonZeroRect,
821 pub(crate) kind: MaskType,
822 pub(crate) mask: Option<Arc<Mask>>,
823 pub(crate) root: Group,
824}
825
826impl Mask {
827 pub fn id(&self) -> &str {
832 self.id.get()
833 }
834
835 pub fn rect(&self) -> NonZeroRect {
839 self.rect
840 }
841
842 pub fn kind(&self) -> MaskType {
846 self.kind
847 }
848
849 pub fn mask(&self) -> Option<&Mask> {
853 self.mask.as_deref()
854 }
855
856 pub fn root(&self) -> &Group {
860 &self.root
861 }
862}
863
864#[allow(missing_docs)]
866#[derive(Clone, Debug)]
867pub enum Node {
868 Group(Box<Group>),
869 Path(Box<Path>),
870 Image(Box<Image>),
871 Text(Box<Text>),
872}
873
874impl Node {
875 pub fn id(&self) -> &str {
877 match self {
878 Node::Group(ref e) => e.id.as_str(),
879 Node::Path(ref e) => e.id.as_str(),
880 Node::Image(ref e) => e.id.as_str(),
881 Node::Text(ref e) => e.id.as_str(),
882 }
883 }
884
885 pub fn abs_transform(&self) -> Transform {
889 match self {
890 Node::Group(ref group) => group.abs_transform(),
891 Node::Path(ref path) => path.abs_transform(),
892 Node::Image(ref image) => image.abs_transform(),
893 Node::Text(ref text) => text.abs_transform(),
894 }
895 }
896
897 pub fn bounding_box(&self) -> Rect {
899 match self {
900 Node::Group(ref group) => group.bounding_box(),
901 Node::Path(ref path) => path.bounding_box(),
902 Node::Image(ref image) => image.bounding_box(),
903 Node::Text(ref text) => text.bounding_box(),
904 }
905 }
906
907 pub fn abs_bounding_box(&self) -> Rect {
909 match self {
910 Node::Group(ref group) => group.abs_bounding_box(),
911 Node::Path(ref path) => path.abs_bounding_box(),
912 Node::Image(ref image) => image.abs_bounding_box(),
913 Node::Text(ref text) => text.abs_bounding_box(),
914 }
915 }
916
917 pub fn stroke_bounding_box(&self) -> Rect {
919 match self {
920 Node::Group(ref group) => group.stroke_bounding_box(),
921 Node::Path(ref path) => path.stroke_bounding_box(),
922 Node::Image(ref image) => image.bounding_box(),
924 Node::Text(ref text) => text.stroke_bounding_box(),
925 }
926 }
927
928 pub fn abs_stroke_bounding_box(&self) -> Rect {
930 match self {
931 Node::Group(ref group) => group.abs_stroke_bounding_box(),
932 Node::Path(ref path) => path.abs_stroke_bounding_box(),
933 Node::Image(ref image) => image.abs_bounding_box(),
935 Node::Text(ref text) => text.abs_stroke_bounding_box(),
936 }
937 }
938
939 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
946 match self {
947 Node::Group(ref group) => Some(group.abs_layer_bounding_box()),
948 Node::Path(ref path) => path.abs_bounding_box().to_non_zero_rect(),
950 Node::Image(ref image) => image.abs_bounding_box().to_non_zero_rect(),
951 Node::Text(ref text) => text.abs_bounding_box().to_non_zero_rect(),
952 }
953 }
954
955 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
980 match self {
981 Node::Group(ref group) => group.subroots(&mut f),
982 Node::Path(ref path) => path.subroots(&mut f),
983 Node::Image(ref image) => image.subroots(&mut f),
984 Node::Text(ref text) => text.subroots(&mut f),
985 }
986 }
987}
988
989#[derive(Clone, Debug)]
996pub struct Group {
997 pub(crate) id: String,
998 pub(crate) transform: Transform,
999 pub(crate) abs_transform: Transform,
1000 pub(crate) opacity: Opacity,
1001 pub(crate) blend_mode: BlendMode,
1002 pub(crate) isolate: bool,
1003 pub(crate) clip_path: Option<Arc<ClipPath>>,
1004 pub(crate) is_context_element: bool,
1006 pub(crate) mask: Option<Arc<Mask>>,
1007 pub(crate) filters: Vec<Arc<filter::Filter>>,
1008 pub(crate) bounding_box: Rect,
1009 pub(crate) abs_bounding_box: Rect,
1010 pub(crate) stroke_bounding_box: Rect,
1011 pub(crate) abs_stroke_bounding_box: Rect,
1012 pub(crate) layer_bounding_box: NonZeroRect,
1013 pub(crate) abs_layer_bounding_box: NonZeroRect,
1014 pub(crate) children: Vec<Node>,
1015}
1016
1017impl Group {
1018 pub(crate) fn empty() -> Self {
1019 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1020 Group {
1021 id: String::new(),
1022 transform: Transform::default(),
1023 abs_transform: Transform::default(),
1024 opacity: Opacity::ONE,
1025 blend_mode: BlendMode::Normal,
1026 isolate: false,
1027 clip_path: None,
1028 mask: None,
1029 filters: Vec::new(),
1030 is_context_element: false,
1031 bounding_box: dummy,
1032 abs_bounding_box: dummy,
1033 stroke_bounding_box: dummy,
1034 abs_stroke_bounding_box: dummy,
1035 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1036 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1037 children: Vec::new(),
1038 }
1039 }
1040
1041 pub fn id(&self) -> &str {
1047 &self.id
1048 }
1049
1050 pub fn transform(&self) -> Transform {
1054 self.transform
1055 }
1056
1057 pub fn abs_transform(&self) -> Transform {
1064 self.abs_transform
1065 }
1066
1067 pub fn opacity(&self) -> Opacity {
1072 self.opacity
1073 }
1074
1075 pub fn blend_mode(&self) -> BlendMode {
1079 self.blend_mode
1080 }
1081
1082 pub fn isolate(&self) -> bool {
1086 self.isolate
1087 }
1088
1089 pub fn clip_path(&self) -> Option<&ClipPath> {
1091 self.clip_path.as_deref()
1092 }
1093
1094 pub fn mask(&self) -> Option<&Mask> {
1096 self.mask.as_deref()
1097 }
1098
1099 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1101 &self.filters
1102 }
1103
1104 pub fn bounding_box(&self) -> Rect {
1110 self.bounding_box
1111 }
1112
1113 pub fn abs_bounding_box(&self) -> Rect {
1117 self.abs_bounding_box
1118 }
1119
1120 pub fn stroke_bounding_box(&self) -> Rect {
1124 self.stroke_bounding_box
1125 }
1126
1127 pub fn abs_stroke_bounding_box(&self) -> Rect {
1131 self.abs_stroke_bounding_box
1132 }
1133
1134 pub fn layer_bounding_box(&self) -> NonZeroRect {
1148 self.layer_bounding_box
1149 }
1150
1151 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1153 self.abs_layer_bounding_box
1154 }
1155
1156 pub fn children(&self) -> &[Node] {
1158 &self.children
1159 }
1160
1161 pub fn should_isolate(&self) -> bool {
1163 self.isolate
1164 || self.opacity != Opacity::ONE
1165 || self.clip_path.is_some()
1166 || self.mask.is_some()
1167 || !self.filters.is_empty()
1168 || self.blend_mode != BlendMode::Normal }
1170
1171 pub fn has_children(&self) -> bool {
1173 !self.children.is_empty()
1174 }
1175
1176 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1187 let mut full_region = BBox::default();
1188 for filter in &self.filters {
1189 full_region = full_region.expand(filter.rect);
1190 }
1191
1192 full_region.to_non_zero_rect()
1193 }
1194
1195 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1196 if let Some(ref clip) = self.clip_path {
1197 f(&clip.root);
1198
1199 if let Some(ref sub_clip) = clip.clip_path {
1200 f(&sub_clip.root);
1201 }
1202 }
1203
1204 if let Some(ref mask) = self.mask {
1205 f(&mask.root);
1206
1207 if let Some(ref sub_mask) = mask.mask {
1208 f(&sub_mask.root);
1209 }
1210 }
1211
1212 for filter in &self.filters {
1213 for primitive in &filter.primitives {
1214 if let filter::Kind::Image(ref image) = primitive.kind {
1215 f(image.root());
1216 }
1217 }
1218 }
1219 }
1220}
1221
1222#[derive(Clone, Copy, PartialEq, Debug)]
1229#[allow(missing_docs)]
1230pub enum PaintOrder {
1231 FillAndStroke,
1232 StrokeAndFill,
1233}
1234
1235impl Default for PaintOrder {
1236 fn default() -> Self {
1237 Self::FillAndStroke
1238 }
1239}
1240
1241#[derive(Clone, Debug)]
1243pub struct Path {
1244 pub(crate) id: String,
1245 pub(crate) visible: bool,
1246 pub(crate) fill: Option<Fill>,
1247 pub(crate) stroke: Option<Stroke>,
1248 pub(crate) paint_order: PaintOrder,
1249 pub(crate) rendering_mode: ShapeRendering,
1250 pub(crate) data: Arc<tiny_skia_path::Path>,
1251 pub(crate) abs_transform: Transform,
1252 pub(crate) bounding_box: Rect,
1253 pub(crate) abs_bounding_box: Rect,
1254 pub(crate) stroke_bounding_box: Rect,
1255 pub(crate) abs_stroke_bounding_box: Rect,
1256}
1257
1258impl Path {
1259 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1260 Self::new(
1261 String::new(),
1262 true,
1263 None,
1264 None,
1265 PaintOrder::default(),
1266 ShapeRendering::default(),
1267 data,
1268 Transform::default(),
1269 )
1270 }
1271
1272 pub(crate) fn new(
1273 id: String,
1274 visible: bool,
1275 fill: Option<Fill>,
1276 stroke: Option<Stroke>,
1277 paint_order: PaintOrder,
1278 rendering_mode: ShapeRendering,
1279 data: Arc<tiny_skia_path::Path>,
1280 abs_transform: Transform,
1281 ) -> Option<Self> {
1282 let bounding_box = data.compute_tight_bounds()?;
1283 let stroke_bounding_box =
1284 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1285
1286 let abs_bounding_box: Rect;
1287 let abs_stroke_bounding_box: Rect;
1288 if abs_transform.has_skew() {
1289 let path2 = data.as_ref().clone();
1291 let path2 = path2.transform(abs_transform)?;
1292 abs_bounding_box = path2.compute_tight_bounds()?;
1293 abs_stroke_bounding_box =
1294 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1295 } else {
1296 abs_bounding_box = bounding_box.transform(abs_transform)?;
1298 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1299 }
1300
1301 Some(Path {
1302 id,
1303 visible,
1304 fill,
1305 stroke,
1306 paint_order,
1307 rendering_mode,
1308 data,
1309 abs_transform,
1310 bounding_box,
1311 abs_bounding_box,
1312 stroke_bounding_box,
1313 abs_stroke_bounding_box,
1314 })
1315 }
1316
1317 pub fn id(&self) -> &str {
1323 &self.id
1324 }
1325
1326 pub fn is_visible(&self) -> bool {
1328 self.visible
1329 }
1330
1331 pub fn fill(&self) -> Option<&Fill> {
1333 self.fill.as_ref()
1334 }
1335
1336 pub fn stroke(&self) -> Option<&Stroke> {
1338 self.stroke.as_ref()
1339 }
1340
1341 pub fn paint_order(&self) -> PaintOrder {
1348 self.paint_order
1349 }
1350
1351 pub fn rendering_mode(&self) -> ShapeRendering {
1355 self.rendering_mode
1356 }
1357
1358 pub fn data(&self) -> &tiny_skia_path::Path {
1363 self.data.as_ref()
1364 }
1365
1366 pub fn abs_transform(&self) -> Transform {
1373 self.abs_transform
1374 }
1375
1376 pub fn bounding_box(&self) -> Rect {
1380 self.bounding_box
1381 }
1382
1383 pub fn abs_bounding_box(&self) -> Rect {
1387 self.abs_bounding_box
1388 }
1389
1390 pub fn stroke_bounding_box(&self) -> Rect {
1394 self.stroke_bounding_box
1395 }
1396
1397 pub fn abs_stroke_bounding_box(&self) -> Rect {
1401 self.abs_stroke_bounding_box
1402 }
1403
1404 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1405 let mut stroke = stroke?.to_tiny_skia();
1406 stroke.dash = None;
1408
1409 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1413 return stroked_path.compute_tight_bounds();
1414 }
1415
1416 None
1417 }
1418
1419 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1420 if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
1421 f(patt.root());
1422 }
1423 if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1424 f(patt.root());
1425 }
1426 }
1427}
1428
1429#[derive(Clone)]
1431pub enum ImageKind {
1432 JPEG(Arc<Vec<u8>>),
1434 PNG(Arc<Vec<u8>>),
1436 GIF(Arc<Vec<u8>>),
1438 WEBP(Arc<Vec<u8>>),
1440 SVG(Tree),
1442}
1443
1444impl ImageKind {
1445 pub(crate) fn actual_size(&self) -> Option<Size> {
1446 match self {
1447 ImageKind::JPEG(ref data)
1448 | ImageKind::PNG(ref data)
1449 | ImageKind::GIF(ref data)
1450 | ImageKind::WEBP(ref data) => imagesize::blob_size(data)
1451 .ok()
1452 .and_then(|size| Size::from_wh(size.width as f32, size.height as f32))
1453 .log_none(|| log::warn!("Image has an invalid size. Skipped.")),
1454 ImageKind::SVG(ref svg) => Some(svg.size),
1455 }
1456 }
1457}
1458
1459impl std::fmt::Debug for ImageKind {
1460 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1461 match self {
1462 ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
1463 ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
1464 ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
1465 ImageKind::WEBP(_) => f.write_str("ImageKind::WEBP(..)"),
1466 ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
1467 }
1468 }
1469}
1470
1471#[derive(Clone, Debug)]
1475pub struct Image {
1476 pub(crate) id: String,
1477 pub(crate) visible: bool,
1478 pub(crate) size: Size,
1479 pub(crate) rendering_mode: ImageRendering,
1480 pub(crate) kind: ImageKind,
1481 pub(crate) abs_transform: Transform,
1482 pub(crate) abs_bounding_box: NonZeroRect,
1483}
1484
1485impl Image {
1486 pub fn id(&self) -> &str {
1492 &self.id
1493 }
1494
1495 pub fn is_visible(&self) -> bool {
1497 self.visible
1498 }
1499
1500 pub fn size(&self) -> Size {
1505 self.size
1506 }
1507
1508 pub fn rendering_mode(&self) -> ImageRendering {
1512 self.rendering_mode
1513 }
1514
1515 pub fn kind(&self) -> &ImageKind {
1517 &self.kind
1518 }
1519
1520 pub fn abs_transform(&self) -> Transform {
1527 self.abs_transform
1528 }
1529
1530 pub fn bounding_box(&self) -> Rect {
1534 self.size.to_rect(0.0, 0.0).unwrap()
1535 }
1536
1537 pub fn abs_bounding_box(&self) -> Rect {
1541 self.abs_bounding_box.to_rect()
1542 }
1543
1544 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1545 if let ImageKind::SVG(ref tree) = self.kind {
1546 f(&tree.root);
1547 }
1548 }
1549}
1550
1551#[allow(missing_debug_implementations)]
1553#[derive(Clone, Debug)]
1554pub struct Tree {
1555 pub(crate) size: Size,
1556 pub(crate) root: Group,
1557 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1558 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1559 pub(crate) patterns: Vec<Arc<Pattern>>,
1560 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1561 pub(crate) masks: Vec<Arc<Mask>>,
1562 pub(crate) filters: Vec<Arc<filter::Filter>>,
1563 #[cfg(feature = "text")]
1564 pub(crate) fontdb: Arc<fontdb::Database>,
1565}
1566
1567impl Tree {
1568 pub fn size(&self) -> Size {
1574 self.size
1575 }
1576
1577 pub fn root(&self) -> &Group {
1579 &self.root
1580 }
1581
1582 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1586 if id.is_empty() {
1587 return None;
1588 }
1589
1590 node_by_id(&self.root, id)
1591 }
1592
1593 pub fn has_text_nodes(&self) -> bool {
1595 has_text_nodes(&self.root)
1596 }
1597
1598 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1600 &self.linear_gradients
1601 }
1602
1603 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1605 &self.radial_gradients
1606 }
1607
1608 pub fn patterns(&self) -> &[Arc<Pattern>] {
1610 &self.patterns
1611 }
1612
1613 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1615 &self.clip_paths
1616 }
1617
1618 pub fn masks(&self) -> &[Arc<Mask>] {
1620 &self.masks
1621 }
1622
1623 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1625 &self.filters
1626 }
1627
1628 #[cfg(feature = "text")]
1630 pub fn fontdb(&self) -> &Arc<fontdb::Database> {
1631 &self.fontdb
1632 }
1633
1634 pub(crate) fn collect_paint_servers(&mut self) {
1635 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1636 Paint::CurrentColor => {},
1637 Paint::Color(_) => {}
1638 Paint::LinearGradient(lg) => {
1639 if !self
1640 .linear_gradients
1641 .iter()
1642 .any(|other| Arc::ptr_eq(lg, other))
1643 {
1644 self.linear_gradients.push(lg.clone());
1645 }
1646 }
1647 Paint::RadialGradient(rg) => {
1648 if !self
1649 .radial_gradients
1650 .iter()
1651 .any(|other| Arc::ptr_eq(rg, other))
1652 {
1653 self.radial_gradients.push(rg.clone());
1654 }
1655 }
1656 Paint::Pattern(patt) => {
1657 if !self.patterns.iter().any(|other| Arc::ptr_eq(patt, other)) {
1658 self.patterns.push(patt.clone());
1659 }
1660 }
1661 });
1662 }
1663}
1664
1665fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1666 for child in &parent.children {
1667 if child.id() == id {
1668 return Some(child);
1669 }
1670
1671 if let Node::Group(ref g) = child {
1672 if let Some(n) = node_by_id(g, id) {
1673 return Some(n);
1674 }
1675 }
1676 }
1677
1678 None
1679}
1680
1681fn has_text_nodes(root: &Group) -> bool {
1682 for node in &root.children {
1683 if let Node::Text(_) = node {
1684 return true;
1685 }
1686
1687 let mut has_text = false;
1688
1689 if let Node::Image(ref image) = node {
1690 if let ImageKind::SVG(ref tree) = image.kind {
1691 if has_text_nodes(&tree.root) {
1692 has_text = true;
1693 }
1694 }
1695 }
1696
1697 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1698
1699 if has_text {
1700 return true;
1701 }
1702 }
1703
1704 true
1705}
1706
1707fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1708 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1709 if let Some(paint) = paint {
1710 f(paint);
1711 }
1712 }
1713
1714 for node in &parent.children {
1715 match node {
1716 Node::Group(ref group) => loop_over_paint_servers(group, f),
1717 Node::Path(ref path) => {
1718 push(path.fill.as_ref().map(|f| &f.paint), f);
1719 push(path.stroke.as_ref().map(|f| &f.paint), f);
1720 }
1721 Node::Image(_) => {}
1722 Node::Text(_) => {}
1724 }
1725
1726 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1727 }
1728}
1729
1730impl Group {
1731 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1732 for node in self.children() {
1733 if let Node::Group(ref g) = node {
1734 if let Some(ref clip) = g.clip_path {
1735 if !clip_paths.iter().any(|other| Arc::ptr_eq(clip, other)) {
1736 clip_paths.push(clip.clone());
1737 }
1738
1739 if let Some(ref sub_clip) = clip.clip_path {
1740 if !clip_paths.iter().any(|other| Arc::ptr_eq(sub_clip, other)) {
1741 clip_paths.push(sub_clip.clone());
1742 }
1743 }
1744 }
1745 }
1746
1747 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1748
1749 if let Node::Group(ref g) = node {
1750 g.collect_clip_paths(clip_paths);
1751 }
1752 }
1753 }
1754
1755 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1756 for node in self.children() {
1757 if let Node::Group(ref g) = node {
1758 if let Some(ref mask) = g.mask {
1759 if !masks.iter().any(|other| Arc::ptr_eq(mask, other)) {
1760 masks.push(mask.clone());
1761 }
1762
1763 if let Some(ref sub_mask) = mask.mask {
1764 if !masks.iter().any(|other| Arc::ptr_eq(sub_mask, other)) {
1765 masks.push(sub_mask.clone());
1766 }
1767 }
1768 }
1769 }
1770
1771 node.subroots(|subroot| subroot.collect_masks(masks));
1772
1773 if let Node::Group(ref g) = node {
1774 g.collect_masks(masks);
1775 }
1776 }
1777 }
1778
1779 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1780 for node in self.children() {
1781 if let Node::Group(ref g) = node {
1782 for filter in g.filters() {
1783 if !filters.iter().any(|other| Arc::ptr_eq(filter, other)) {
1784 filters.push(filter.clone());
1785 }
1786 }
1787 }
1788
1789 node.subroots(|subroot| subroot.collect_filters(filters));
1790
1791 if let Node::Group(ref g) = node {
1792 g.collect_filters(filters);
1793 }
1794 }
1795 }
1796
1797 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1798 let mut bbox = BBox::default();
1799 for child in &self.children {
1800 let mut c_bbox = child.bounding_box();
1801 if let Node::Group(ref group) = child {
1802 if let Some(r) = c_bbox.transform(group.transform) {
1803 c_bbox = r;
1804 }
1805 }
1806
1807 bbox = bbox.expand(c_bbox);
1808 }
1809
1810 bbox.to_non_zero_rect()
1811 }
1812
1813 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1814 let mut bbox = BBox::default();
1815 let mut abs_bbox = BBox::default();
1816 let mut stroke_bbox = BBox::default();
1817 let mut abs_stroke_bbox = BBox::default();
1818 let mut layer_bbox = BBox::default();
1819 for child in &self.children {
1820 {
1821 let mut c_bbox = child.bounding_box();
1822 if let Node::Group(ref group) = child {
1823 if let Some(r) = c_bbox.transform(group.transform) {
1824 c_bbox = r;
1825 }
1826 }
1827
1828 bbox = bbox.expand(c_bbox);
1829 }
1830
1831 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1832
1833 {
1834 let mut c_bbox = child.stroke_bounding_box();
1835 if let Node::Group(ref group) = child {
1836 if let Some(r) = c_bbox.transform(group.transform) {
1837 c_bbox = r;
1838 }
1839 }
1840
1841 stroke_bbox = stroke_bbox.expand(c_bbox);
1842 }
1843
1844 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1845
1846 if let Node::Group(ref group) = child {
1847 let r = group.layer_bounding_box;
1848 if let Some(r) = r.transform(group.transform) {
1849 layer_bbox = layer_bbox.expand(r);
1850 }
1851 } else {
1852 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1854 }
1855 }
1856
1857 if let Some(bbox) = bbox.to_rect() {
1860 self.bounding_box = bbox;
1861 self.abs_bounding_box = abs_bbox.to_rect()?;
1862 self.stroke_bounding_box = stroke_bbox.to_rect()?;
1863 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
1864 }
1865
1866 if let Some(filter_bbox) = self.filters_bounding_box() {
1868 self.layer_bounding_box = filter_bbox;
1869 } else {
1870 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
1871 }
1872
1873 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
1874
1875 Some(())
1876 }
1877}