1pub mod filter;
6mod geom;
7mod text;
8
9use std::sync::Arc;
10pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
11pub use svgrtypes::{Align, AspectRatio};
12
13pub use tiny_skia_path;
14
15use crate::hashers::CustomHash;
16use crate::PreloadedImageData;
17
18pub use self::geom::*;
19pub use self::text::*;
20
21pub type Opacity = NormalizedF32;
23
24#[derive(Debug, Hash)]
26pub(crate) struct NonEmptyString(String);
27
28impl NonEmptyString {
29 pub(crate) fn new(string: String) -> Option<Self> {
30 if string.trim().is_empty() {
31 return None;
32 }
33
34 Some(NonEmptyString(string))
35 }
36
37 pub(crate) fn get(&self) -> &str {
38 &self.0
39 }
40}
41
42#[derive(Clone, Copy, Debug)]
46pub struct NonZeroF32(f32);
47
48impl std::hash::Hash for NonZeroF32 {
49 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
50 self.0.to_bits().hash(state);
51 }
52}
53
54impl NonZeroF32 {
55 #[inline]
57 pub fn new(n: f32) -> Option<Self> {
58 if n.approx_eq_ulps(&0.0, 4) {
59 None
60 } else {
61 Some(NonZeroF32(n))
62 }
63 }
64
65 #[inline]
67 pub fn get(&self) -> f32 {
68 self.0
69 }
70}
71
72#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
73pub(crate) enum Units {
74 UserSpaceOnUse,
75 ObjectBoundingBox,
76}
77
78#[allow(missing_docs)]
84#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
85pub enum Visibility {
86 Visible,
87 Hidden,
88 Collapse,
89}
90
91impl Default for Visibility {
92 fn default() -> Self {
93 Self::Visible
94 }
95}
96
97#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
101#[allow(missing_docs)]
102pub enum ShapeRendering {
103 OptimizeSpeed,
104 CrispEdges,
105 GeometricPrecision,
106}
107
108impl ShapeRendering {
109 pub fn use_shape_antialiasing(self) -> bool {
111 match self {
112 ShapeRendering::OptimizeSpeed => false,
113 ShapeRendering::CrispEdges => false,
114 ShapeRendering::GeometricPrecision => true,
115 }
116 }
117}
118
119impl Default for ShapeRendering {
120 fn default() -> Self {
121 Self::GeometricPrecision
122 }
123}
124
125impl std::str::FromStr for ShapeRendering {
126 type Err = &'static str;
127
128 fn from_str(s: &str) -> Result<Self, Self::Err> {
129 match s {
130 "optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
131 "crispEdges" => Ok(ShapeRendering::CrispEdges),
132 "geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
133 _ => Err("invalid"),
134 }
135 }
136}
137
138#[allow(missing_docs)]
142#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
143pub enum TextRendering {
144 OptimizeSpeed,
145 OptimizeLegibility,
146 GeometricPrecision,
147}
148
149impl Default for TextRendering {
150 fn default() -> Self {
151 Self::OptimizeLegibility
152 }
153}
154
155impl std::str::FromStr for TextRendering {
156 type Err = &'static str;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 match s {
160 "optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
161 "optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
162 "geometricPrecision" => Ok(TextRendering::GeometricPrecision),
163 _ => Err("invalid"),
164 }
165 }
166}
167
168#[allow(missing_docs)]
172#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
173pub enum ImageRendering {
174 OptimizeQuality,
175 OptimizeSpeed,
176}
177
178impl Default for ImageRendering {
179 fn default() -> Self {
180 Self::OptimizeQuality
181 }
182}
183
184impl std::str::FromStr for ImageRendering {
185 type Err = &'static str;
186
187 fn from_str(s: &str) -> Result<Self, Self::Err> {
188 match s {
189 "optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
190 "optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
191 _ => Err("invalid"),
192 }
193 }
194}
195
196#[allow(missing_docs)]
200#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
201pub enum BlendMode {
202 Normal,
203 Multiply,
204 Screen,
205 Overlay,
206 Darken,
207 Lighten,
208 ColorDodge,
209 ColorBurn,
210 HardLight,
211 SoftLight,
212 Difference,
213 Exclusion,
214 Hue,
215 Saturation,
216 Color,
217 Luminosity,
218}
219
220impl Default for BlendMode {
221 fn default() -> Self {
222 Self::Normal
223 }
224}
225
226#[allow(missing_docs)]
230#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
231pub enum SpreadMethod {
232 Pad,
233 Reflect,
234 Repeat,
235}
236
237impl Default for SpreadMethod {
238 fn default() -> Self {
239 Self::Pad
240 }
241}
242
243#[derive(Debug)]
245pub struct BaseGradient {
246 pub(crate) id: NonEmptyString,
247 pub(crate) units: Units, pub(crate) transform: Transform,
249 pub(crate) spread_method: SpreadMethod,
250 pub(crate) stops: Vec<Stop>,
251}
252
253impl std::hash::Hash for BaseGradient {
254 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
255 self.id.hash(state);
256 self.transform.custom_hash(state);
257 self.spread_method.hash(state);
258 self.stops.hash(state);
259 }
260}
261
262impl BaseGradient {
263 pub fn id(&self) -> &str {
268 self.id.get()
269 }
270
271 pub fn transform(&self) -> Transform {
275 self.transform
276 }
277
278 pub fn spread_method(&self) -> SpreadMethod {
282 self.spread_method
283 }
284
285 pub fn stops(&self) -> &[Stop] {
287 &self.stops
288 }
289}
290
291#[derive(Debug)]
295pub struct LinearGradient {
296 pub(crate) base: BaseGradient,
297 pub(crate) x1: f32,
298 pub(crate) y1: f32,
299 pub(crate) x2: f32,
300 pub(crate) y2: f32,
301}
302
303impl std::hash::Hash for LinearGradient {
304 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
305 self.id.hash(state);
306 self.x1.to_bits().hash(state);
307 self.y1.to_bits().hash(state);
308 self.x2.to_bits().hash(state);
309 self.y2.to_bits().hash(state);
310 self.base.hash(state);
311 }
312}
313
314impl LinearGradient {
315 pub fn x1(&self) -> f32 {
317 self.x1
318 }
319
320 pub fn y1(&self) -> f32 {
322 self.y1
323 }
324
325 pub fn x2(&self) -> f32 {
327 self.x2
328 }
329
330 pub fn y2(&self) -> f32 {
332 self.y2
333 }
334}
335
336impl std::ops::Deref for LinearGradient {
337 type Target = BaseGradient;
338
339 fn deref(&self) -> &Self::Target {
340 &self.base
341 }
342}
343
344#[derive(Debug)]
348pub struct RadialGradient {
349 pub(crate) base: BaseGradient,
350 pub(crate) cx: f32,
351 pub(crate) cy: f32,
352 pub(crate) r: PositiveF32,
353 pub(crate) fx: f32,
354 pub(crate) fy: f32,
355}
356
357impl std::hash::Hash for RadialGradient {
358 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
359 self.id.hash(state);
360 self.cx.to_bits().hash(state);
361 self.cy.to_bits().hash(state);
362 self.r.hash(state);
363 self.fx.to_bits().hash(state);
364 self.fy.to_bits().hash(state);
365 self.base.hash(state);
366 }
367}
368
369impl RadialGradient {
370 pub fn cx(&self) -> f32 {
372 self.cx
373 }
374
375 pub fn cy(&self) -> f32 {
377 self.cy
378 }
379
380 pub fn r(&self) -> PositiveF32 {
382 self.r
383 }
384
385 pub fn fx(&self) -> f32 {
387 self.fx
388 }
389
390 pub fn fy(&self) -> f32 {
392 self.fy
393 }
394}
395
396impl std::ops::Deref for RadialGradient {
397 type Target = BaseGradient;
398
399 fn deref(&self) -> &Self::Target {
400 &self.base
401 }
402}
403
404pub type StopOffset = NormalizedF32;
406
407#[derive(Clone, Copy, Debug, Hash)]
411pub struct Stop {
412 pub(crate) offset: StopOffset,
413 pub(crate) color: Color,
414 pub(crate) opacity: Opacity,
415}
416
417impl Stop {
418 pub fn offset(&self) -> StopOffset {
422 self.offset
423 }
424
425 pub fn color(&self) -> Color {
429 self.color
430 }
431
432 pub fn opacity(&self) -> Opacity {
436 self.opacity
437 }
438}
439
440#[derive(Debug)]
444pub struct Pattern {
445 pub(crate) id: NonEmptyString,
446 pub(crate) units: Units, pub(crate) content_units: Units, pub(crate) transform: Transform,
449 pub(crate) rect: NonZeroRect,
450 pub(crate) view_box: Option<ViewBox>,
451 pub(crate) root: Group,
452}
453
454impl std::hash::Hash for Pattern {
455 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
456 self.id.hash(state);
457 self.transform.custom_hash(state);
458 self.rect.custom_hash(state);
459 self.view_box.hash(state);
460 self.root.hash(state);
461 }
462}
463
464impl Pattern {
465 pub fn id(&self) -> &str {
470 self.id.get()
471 }
472
473 pub fn transform(&self) -> Transform {
477 self.transform
478 }
479
480 pub fn rect(&self) -> NonZeroRect {
484 self.rect
485 }
486
487 pub fn view_box(&self) -> Option<ViewBox> {
489 self.view_box
490 }
491
492 pub fn root(&self) -> &Group {
494 &self.root
495 }
496}
497
498pub type StrokeWidth = NonZeroPositiveF32;
500
501#[derive(Clone, Copy, Debug)]
505pub struct StrokeMiterlimit(f32);
506
507impl StrokeMiterlimit {
508 #[inline]
510 pub fn new(n: f32) -> Self {
511 debug_assert!(n.is_finite());
512 debug_assert!(n >= 1.0);
513
514 let n = if !(n >= 1.0) { 1.0 } else { n };
515
516 StrokeMiterlimit(n)
517 }
518
519 #[inline]
521 pub fn get(&self) -> f32 {
522 self.0
523 }
524}
525
526impl Default for StrokeMiterlimit {
527 #[inline]
528 fn default() -> Self {
529 StrokeMiterlimit::new(4.0)
530 }
531}
532
533impl From<f32> for StrokeMiterlimit {
534 #[inline]
535 fn from(n: f32) -> Self {
536 Self::new(n)
537 }
538}
539
540impl PartialEq for StrokeMiterlimit {
541 #[inline]
542 fn eq(&self, other: &Self) -> bool {
543 self.0.approx_eq_ulps(&other.0, 4)
544 }
545}
546
547#[allow(missing_docs)]
551#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
552pub enum LineCap {
553 Butt,
554 Round,
555 Square,
556}
557
558impl Default for LineCap {
559 fn default() -> Self {
560 Self::Butt
561 }
562}
563
564#[allow(missing_docs)]
568#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
569pub enum LineJoin {
570 Miter,
571 MiterClip,
572 Round,
573 Bevel,
574}
575
576impl Default for LineJoin {
577 fn default() -> Self {
578 Self::Miter
579 }
580}
581
582#[derive(Clone, Debug)]
584pub struct Stroke {
585 pub(crate) paint: Paint,
586 pub(crate) dasharray: Option<Vec<f32>>,
587 pub(crate) dashoffset: f32,
588 pub(crate) miterlimit: StrokeMiterlimit,
589 pub(crate) opacity: Opacity,
590 pub(crate) width: StrokeWidth,
591 pub(crate) linecap: LineCap,
592 pub(crate) linejoin: LineJoin,
593 pub(crate) context_element: Option<ContextElement>,
596}
597
598impl std::hash::Hash for Stroke {
599 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
600 self.paint.hash(state);
601 self.dasharray
602 .as_ref()
603 .map(|vec| vec.iter().map(|v| v.to_bits()).collect::<Vec<_>>())
604 .hash(state);
605 self.dashoffset.to_bits().hash(state);
606
607 self.miterlimit.0.to_bits().hash(state);
608 self.opacity.hash(state);
609 self.width.hash(state);
610 self.linecap.hash(state);
611 self.linejoin.hash(state);
612 }
613}
614
615impl Stroke {
616 pub fn paint(&self) -> &Paint {
618 &self.paint
619 }
620
621 pub fn dasharray(&self) -> Option<&[f32]> {
623 self.dasharray.as_deref()
624 }
625
626 pub fn dashoffset(&self) -> f32 {
628 self.dashoffset
629 }
630
631 pub fn miterlimit(&self) -> StrokeMiterlimit {
633 self.miterlimit
634 }
635
636 pub fn opacity(&self) -> Opacity {
638 self.opacity
639 }
640
641 pub fn width(&self) -> StrokeWidth {
643 self.width
644 }
645
646 pub fn linecap(&self) -> LineCap {
648 self.linecap
649 }
650
651 pub fn linejoin(&self) -> LineJoin {
653 self.linejoin
654 }
655
656 pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
658 let mut stroke = tiny_skia_path::Stroke {
659 width: self.width.get(),
660 miter_limit: self.miterlimit.get(),
661 line_cap: match self.linecap {
662 LineCap::Butt => tiny_skia_path::LineCap::Butt,
663 LineCap::Round => tiny_skia_path::LineCap::Round,
664 LineCap::Square => tiny_skia_path::LineCap::Square,
665 },
666 line_join: match self.linejoin {
667 LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
668 LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
669 LineJoin::Round => tiny_skia_path::LineJoin::Round,
670 LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
671 },
672 dash: None,
675 };
676
677 if let Some(ref list) = self.dasharray {
678 stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
679 }
680
681 stroke
682 }
683}
684
685#[allow(missing_docs)]
689#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
690pub enum FillRule {
691 NonZero,
692 EvenOdd,
693}
694
695impl Default for FillRule {
696 fn default() -> Self {
697 Self::NonZero
698 }
699}
700
701#[derive(Clone, Copy, Debug)]
702pub(crate) enum ContextElement {
703 UseNode,
708 PathNode(Transform, Option<NonZeroRect>),
713}
714
715impl std::hash::Hash for ContextElement {
716 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
717 match self {
718 ContextElement::UseNode => 0.hash(state),
719 ContextElement::PathNode(transform, rect) => {
720 1.hash(state);
721 transform.custom_hash(state);
722
723 if let Some(rect) = rect {
724 rect.custom_hash(state);
725 }
726 }
727 }
728 }
729}
730
731#[derive(Clone, Debug, Hash)]
733pub struct Fill {
734 pub(crate) paint: Paint,
735 pub(crate) opacity: Opacity,
736 pub(crate) rule: FillRule,
737 pub(crate) context_element: Option<ContextElement>,
740}
741
742impl Fill {
743 pub fn paint(&self) -> &Paint {
745 &self.paint
746 }
747
748 pub fn opacity(&self) -> Opacity {
750 self.opacity
751 }
752
753 pub fn rule(&self) -> FillRule {
755 self.rule
756 }
757}
758
759impl Default for Fill {
760 fn default() -> Self {
761 Fill {
762 paint: Paint::Color(Color::black()),
763 opacity: Opacity::ONE,
764 rule: FillRule::default(),
765 context_element: None,
766 }
767 }
768}
769
770#[allow(missing_docs)]
772#[derive(Clone, Copy, PartialEq, Debug, Hash)]
773pub struct Color {
774 pub red: u8,
775 pub green: u8,
776 pub blue: u8,
777}
778
779impl Color {
780 #[inline]
782 pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
783 Color { red, green, blue }
784 }
785
786 #[inline]
788 pub fn black() -> Color {
789 Color::new_rgb(0, 0, 0)
790 }
791
792 #[inline]
794 pub fn white() -> Color {
795 Color::new_rgb(255, 255, 255)
796 }
797}
798
799#[allow(missing_docs)]
803#[derive(Clone, Debug)]
804pub enum Paint {
805 Color(Color),
806 LinearGradient(Arc<LinearGradient>),
807 RadialGradient(Arc<RadialGradient>),
808 Pattern(Arc<Pattern>),
809}
810
811impl PartialEq for Paint {
812 #[inline]
813 fn eq(&self, other: &Self) -> bool {
814 match (self, other) {
815 (Self::Color(lc), Self::Color(rc)) => lc == rc,
816 (Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Arc::ptr_eq(lg1, lg2),
817 (Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Arc::ptr_eq(rg1, rg2),
818 (Self::Pattern(ref p1), Self::Pattern(ref p2)) => Arc::ptr_eq(p1, p2),
819 _ => false,
820 }
821 }
822}
823
824impl std::hash::Hash for Paint {
825 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
826 match self {
827 Paint::Color(color) => color.hash(state),
828 Paint::LinearGradient(gradient) => gradient.id.hash(state),
829 Paint::RadialGradient(gradient) => gradient.id.hash(state),
830 Paint::Pattern(pattern) => pattern.id.hash(state),
831 }
832 }
833}
834
835#[derive(Debug)]
839pub struct ClipPath {
840 pub(crate) id: NonEmptyString,
841 pub(crate) transform: Transform,
842 pub(crate) clip_path: Option<Arc<ClipPath>>,
843 pub(crate) root: Group,
844}
845
846impl std::hash::Hash for ClipPath {
847 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
848 self.id.hash(state);
849 self.transform.custom_hash(state);
850 self.clip_path.hash(state);
851 self.root.hash(state);
852 }
853}
854
855impl ClipPath {
856 pub(crate) fn empty(id: NonEmptyString) -> Self {
857 ClipPath {
858 id,
859 transform: Transform::default(),
860 clip_path: None,
861 root: Group::empty(),
862 }
863 }
864
865 pub fn id(&self) -> &str {
870 self.id.get()
871 }
872
873 pub fn transform(&self) -> Transform {
877 self.transform
878 }
879
880 pub fn clip_path(&self) -> Option<&ClipPath> {
884 self.clip_path.as_deref()
885 }
886
887 pub fn root(&self) -> &Group {
889 &self.root
890 }
891}
892
893#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
895pub enum MaskType {
896 Luminance,
898 Alpha,
900}
901
902impl Default for MaskType {
903 fn default() -> Self {
904 Self::Luminance
905 }
906}
907
908#[derive(Debug)]
912pub struct Mask {
913 pub(crate) id: NonEmptyString,
914 pub(crate) rect: NonZeroRect,
915 pub(crate) kind: MaskType,
916 pub(crate) mask: Option<Arc<Mask>>,
917 pub(crate) root: Group,
918}
919
920impl std::hash::Hash for Mask {
921 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
922 self.id.hash(state);
923 self.rect.custom_hash(state);
924 self.kind.hash(state);
925 self.mask.hash(state);
926 self.root.hash(state);
927 }
928}
929
930impl Mask {
931 pub fn id(&self) -> &str {
936 self.id.get()
937 }
938
939 pub fn rect(&self) -> NonZeroRect {
943 self.rect
944 }
945
946 pub fn kind(&self) -> MaskType {
950 self.kind
951 }
952
953 pub fn mask(&self) -> Option<&Mask> {
957 self.mask.as_deref()
958 }
959
960 pub fn root(&self) -> &Group {
964 &self.root
965 }
966}
967
968#[allow(missing_docs)]
970#[derive(Clone, Debug, Hash)]
971pub enum Node {
972 Group(Box<Group>),
973 Path(Box<Path>),
974 Image(Box<Image>),
975 Text(Box<Text>),
976}
977
978impl Node {
979 pub fn id(&self) -> &str {
981 match self {
982 Node::Group(ref e) => e.id.as_str(),
983 Node::Path(ref e) => e.id.as_str(),
984 Node::Image(ref e) => e.id.as_str(),
985 Node::Text(ref e) => e.id.as_str(),
986 }
987 }
988
989 pub fn abs_transform(&self) -> Transform {
996 match self {
997 Node::Group(ref group) => group.abs_transform(),
998 Node::Path(ref path) => path.abs_transform(),
999 Node::Image(ref image) => image.abs_transform(),
1000 Node::Text(ref text) => text.abs_transform(),
1001 }
1002 }
1003
1004 pub fn bounding_box(&self) -> Rect {
1006 match self {
1007 Node::Group(ref group) => group.bounding_box(),
1008 Node::Path(ref path) => path.bounding_box(),
1009 Node::Image(ref image) => image.bounding_box(),
1010 Node::Text(ref text) => text.bounding_box(),
1011 }
1012 }
1013
1014 pub fn abs_bounding_box(&self) -> Rect {
1016 match self {
1017 Node::Group(ref group) => group.abs_bounding_box(),
1018 Node::Path(ref path) => path.abs_bounding_box(),
1019 Node::Image(ref image) => image.abs_bounding_box(),
1020 Node::Text(ref text) => text.abs_bounding_box(),
1021 }
1022 }
1023
1024 pub fn stroke_bounding_box(&self) -> Rect {
1026 match self {
1027 Node::Group(ref group) => group.stroke_bounding_box(),
1028 Node::Path(ref path) => path.stroke_bounding_box(),
1029 Node::Image(ref image) => image.bounding_box(),
1031 Node::Text(ref text) => text.stroke_bounding_box(),
1032 }
1033 }
1034
1035 pub fn abs_stroke_bounding_box(&self) -> Rect {
1037 match self {
1038 Node::Group(ref group) => group.abs_stroke_bounding_box(),
1039 Node::Path(ref path) => path.abs_stroke_bounding_box(),
1040 Node::Image(ref image) => image.abs_bounding_box(),
1042 Node::Text(ref text) => text.abs_stroke_bounding_box(),
1043 }
1044 }
1045
1046 pub fn abs_layer_bounding_box(&self) -> Option<NonZeroRect> {
1053 match self {
1054 Node::Group(ref group) => Some(group.abs_layer_bounding_box()),
1055 Node::Path(ref path) => path.abs_bounding_box().to_non_zero_rect(),
1057 Node::Image(ref image) => image.abs_bounding_box().to_non_zero_rect(),
1058 Node::Text(ref text) => text.abs_bounding_box().to_non_zero_rect(),
1059 }
1060 }
1061
1062 pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
1087 match self {
1088 Node::Group(ref group) => group.subroots(&mut f),
1089 Node::Path(ref path) => path.subroots(&mut f),
1090 Node::Image(ref image) => image.subroots(&mut f),
1091 Node::Text(ref text) => text.subroots(&mut f),
1092 }
1093 }
1094}
1095
1096#[derive(Clone, Debug)]
1103pub struct Group {
1104 pub(crate) id: String,
1105 pub(crate) transform: Transform,
1106 pub(crate) abs_transform: Transform,
1107 pub(crate) opacity: Opacity,
1108 pub(crate) blend_mode: BlendMode,
1109 pub(crate) isolate: bool,
1110 pub(crate) clip_path: Option<Arc<ClipPath>>,
1111 pub(crate) is_context_element: bool,
1113 pub(crate) mask: Option<Arc<Mask>>,
1114 pub(crate) filters: Vec<Arc<filter::Filter>>,
1115 pub(crate) bounding_box: Rect,
1116 pub(crate) abs_bounding_box: Rect,
1117 pub(crate) stroke_bounding_box: Rect,
1118 pub(crate) abs_stroke_bounding_box: Rect,
1119 pub(crate) layer_bounding_box: NonZeroRect,
1120 pub(crate) abs_layer_bounding_box: NonZeroRect,
1121 pub(crate) children: Vec<Node>,
1122}
1123
1124impl std::hash::Hash for Group {
1125 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1126 self.id.hash(state);
1127 self.transform.custom_hash(state);
1128 self.abs_transform.custom_hash(state);
1129 self.isolate.hash(state);
1133 self.clip_path.hash(state);
1134 self.mask.hash(state);
1135 self.filters.hash(state);
1136 self.bounding_box.custom_hash(state);
1137 self.abs_bounding_box.custom_hash(state);
1138 self.stroke_bounding_box.custom_hash(state);
1139 self.abs_stroke_bounding_box.custom_hash(state);
1140 self.layer_bounding_box.custom_hash(state);
1141 self.abs_layer_bounding_box.custom_hash(state);
1142 self.children.hash(state);
1143 }
1144}
1145
1146impl Group {
1147 pub(crate) fn empty() -> Self {
1148 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
1149 Group {
1150 id: String::new(),
1151 transform: Transform::default(),
1152 abs_transform: Transform::default(),
1153 opacity: Opacity::ONE,
1154 blend_mode: BlendMode::Normal,
1155 isolate: false,
1156 clip_path: None,
1157 mask: None,
1158 filters: Vec::new(),
1159 is_context_element: false,
1160 bounding_box: dummy,
1161 abs_bounding_box: dummy,
1162 stroke_bounding_box: dummy,
1163 abs_stroke_bounding_box: dummy,
1164 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1165 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
1166 children: Vec::new(),
1167 }
1168 }
1169
1170 pub fn id(&self) -> &str {
1176 &self.id
1177 }
1178
1179 pub fn transform(&self) -> Transform {
1183 self.transform
1184 }
1185
1186 pub fn abs_transform(&self) -> Transform {
1193 self.abs_transform
1194 }
1195
1196 pub fn opacity(&self) -> Opacity {
1201 self.opacity
1202 }
1203
1204 pub fn blend_mode(&self) -> BlendMode {
1208 self.blend_mode
1209 }
1210
1211 pub fn isolate(&self) -> bool {
1215 self.isolate
1216 }
1217
1218 pub fn clip_path(&self) -> Option<&ClipPath> {
1220 self.clip_path.as_deref()
1221 }
1222
1223 pub fn mask(&self) -> Option<&Mask> {
1225 self.mask.as_deref()
1226 }
1227
1228 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1230 &self.filters
1231 }
1232
1233 pub fn bounding_box(&self) -> Rect {
1239 self.bounding_box
1240 }
1241
1242 pub fn abs_bounding_box(&self) -> Rect {
1246 self.abs_bounding_box
1247 }
1248
1249 pub fn stroke_bounding_box(&self) -> Rect {
1253 self.stroke_bounding_box
1254 }
1255
1256 pub fn abs_stroke_bounding_box(&self) -> Rect {
1260 self.abs_stroke_bounding_box
1261 }
1262
1263 pub fn layer_bounding_box(&self) -> NonZeroRect {
1275 self.layer_bounding_box
1276 }
1277
1278 pub fn abs_layer_bounding_box(&self) -> NonZeroRect {
1280 self.abs_layer_bounding_box
1281 }
1282
1283 pub fn children(&self) -> &[Node] {
1285 &self.children
1286 }
1287
1288 pub fn should_isolate(&self) -> bool {
1290 self.isolate
1291 || self.opacity != Opacity::ONE
1292 || self.clip_path.is_some()
1293 || self.mask.is_some()
1294 || !self.filters.is_empty()
1295 || self.blend_mode != BlendMode::Normal }
1297
1298 pub fn has_children(&self) -> bool {
1300 !self.children.is_empty()
1301 }
1302
1303 pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
1314 let mut full_region = BBox::default();
1315 for filter in &self.filters {
1316 full_region = full_region.expand(filter.rect);
1317 }
1318
1319 full_region.to_non_zero_rect()
1320 }
1321
1322 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1323 if let Some(ref clip) = self.clip_path {
1324 f(&clip.root);
1325
1326 if let Some(ref sub_clip) = clip.clip_path {
1327 f(&sub_clip.root);
1328 }
1329 }
1330
1331 if let Some(ref mask) = self.mask {
1332 f(&mask.root);
1333
1334 if let Some(ref sub_mask) = mask.mask {
1335 f(&sub_mask.root);
1336 }
1337 }
1338
1339 for filter in &self.filters {
1340 for primitive in &filter.primitives {
1341 if let filter::Kind::Image(ref image) = primitive.kind {
1342 if let filter::ImageKind::Use(ref use_node) = image.data {
1343 f(use_node);
1344 }
1345 }
1346 }
1347 }
1348 }
1349}
1350
1351#[derive(Clone, Copy, PartialEq, Debug, Hash, Eq)]
1358#[allow(missing_docs)]
1359pub enum PaintOrder {
1360 FillAndStroke,
1361 StrokeAndFill,
1362}
1363
1364impl Default for PaintOrder {
1365 fn default() -> Self {
1366 Self::FillAndStroke
1367 }
1368}
1369
1370#[derive(Clone, Debug)]
1372pub struct Path {
1373 pub(crate) id: String,
1374 pub(crate) visibility: Visibility,
1375 pub(crate) fill: Option<Fill>,
1376 pub(crate) stroke: Option<Stroke>,
1377 pub(crate) paint_order: PaintOrder,
1378 pub(crate) rendering_mode: ShapeRendering,
1379 pub(crate) data: Arc<tiny_skia_path::Path>,
1380 pub(crate) abs_transform: Transform,
1381 pub(crate) bounding_box: Rect,
1382 pub(crate) abs_bounding_box: Rect,
1383 pub(crate) stroke_bounding_box: Rect,
1384 pub(crate) abs_stroke_bounding_box: Rect,
1385}
1386
1387impl std::hash::Hash for Path {
1388 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1389 self.id.hash(state);
1390 self.visibility.hash(state);
1391 self.fill.hash(state);
1392 self.stroke.hash(state);
1393 self.paint_order.hash(state);
1394 self.rendering_mode.hash(state);
1395 self.data.custom_hash(state);
1396 self.abs_transform.custom_hash(state);
1397 self.bounding_box.custom_hash(state);
1398 self.abs_bounding_box.custom_hash(state);
1399 self.stroke_bounding_box.custom_hash(state);
1400 self.abs_stroke_bounding_box.custom_hash(state);
1401 }
1402}
1403
1404impl Path {
1405 pub(crate) fn new_simple(data: Arc<tiny_skia_path::Path>) -> Option<Self> {
1406 Self::new(
1407 String::new(),
1408 Visibility::default(),
1409 None,
1410 None,
1411 PaintOrder::default(),
1412 ShapeRendering::default(),
1413 data,
1414 Transform::default(),
1415 )
1416 }
1417
1418 pub(crate) fn new(
1419 id: String,
1420 visibility: Visibility,
1421 fill: Option<Fill>,
1422 stroke: Option<Stroke>,
1423 paint_order: PaintOrder,
1424 rendering_mode: ShapeRendering,
1425 data: Arc<tiny_skia_path::Path>,
1426 abs_transform: Transform,
1427 ) -> Option<Self> {
1428 let bounding_box = data.compute_tight_bounds()?;
1429 let stroke_bounding_box =
1430 Path::calculate_stroke_bbox(stroke.as_ref(), &data).unwrap_or(bounding_box);
1431
1432 let abs_bounding_box: Rect;
1433 let abs_stroke_bounding_box: Rect;
1434 if abs_transform.has_skew() {
1435 let path2 = data.as_ref().clone();
1437 let path2 = path2.transform(abs_transform)?;
1438 abs_bounding_box = path2.compute_tight_bounds()?;
1439 abs_stroke_bounding_box =
1440 Path::calculate_stroke_bbox(stroke.as_ref(), &path2).unwrap_or(abs_bounding_box);
1441 } else {
1442 abs_bounding_box = bounding_box.transform(abs_transform)?;
1444 abs_stroke_bounding_box = stroke_bounding_box.transform(abs_transform)?;
1445 }
1446
1447 Some(Path {
1448 id,
1449 visibility,
1450 fill,
1451 stroke,
1452 paint_order,
1453 rendering_mode,
1454 data,
1455 abs_transform,
1456 bounding_box,
1457 abs_bounding_box,
1458 stroke_bounding_box,
1459 abs_stroke_bounding_box,
1460 })
1461 }
1462
1463 pub fn id(&self) -> &str {
1469 &self.id
1470 }
1471
1472 pub fn visibility(&self) -> Visibility {
1474 self.visibility
1475 }
1476
1477 pub fn fill(&self) -> Option<&Fill> {
1479 self.fill.as_ref()
1480 }
1481
1482 pub fn stroke(&self) -> Option<&Stroke> {
1484 self.stroke.as_ref()
1485 }
1486
1487 pub fn paint_order(&self) -> PaintOrder {
1494 self.paint_order
1495 }
1496
1497 pub fn rendering_mode(&self) -> ShapeRendering {
1501 self.rendering_mode
1502 }
1503
1504 pub fn data(&self) -> &tiny_skia_path::Path {
1509 self.data.as_ref()
1510 }
1511
1512 pub fn abs_transform(&self) -> Transform {
1519 self.abs_transform
1520 }
1521
1522 pub fn bounding_box(&self) -> Rect {
1526 self.bounding_box
1527 }
1528
1529 pub fn abs_bounding_box(&self) -> Rect {
1533 self.abs_bounding_box
1534 }
1535
1536 pub fn stroke_bounding_box(&self) -> Rect {
1540 self.stroke_bounding_box
1541 }
1542
1543 pub fn abs_stroke_bounding_box(&self) -> Rect {
1547 self.abs_stroke_bounding_box
1548 }
1549
1550 fn calculate_stroke_bbox(stroke: Option<&Stroke>, path: &tiny_skia_path::Path) -> Option<Rect> {
1551 let mut stroke = stroke?.to_tiny_skia();
1552 stroke.dash = None;
1554
1555 if let Some(stroked_path) = path.stroke(&stroke, 1.0) {
1559 return stroked_path.compute_tight_bounds();
1560 }
1561
1562 None
1563 }
1564
1565 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1566 if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
1567 f(patt.root())
1568 }
1569 if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
1570 f(patt.root())
1571 }
1572 }
1573}
1574
1575#[derive(Clone)]
1577pub enum ImageKind {
1578 DATA(Arc<PreloadedImageData>),
1580 SVG {
1582 original_href: String,
1584 tree: Arc<Tree>,
1586 },
1587}
1588
1589impl std::fmt::Debug for ImageKind {
1590 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1591 match self {
1592 ImageKind::DATA(_) => f.write_str("ImageKind::DATA(..)"),
1593 ImageKind::SVG { .. } => f.write_str("ImageKind::SVG(..)"),
1594 }
1595 }
1596}
1597
1598#[derive(Clone, Debug)]
1602pub struct Image {
1603 pub(crate) id: String,
1604 pub(crate) visibility: Visibility,
1605 pub(crate) view_box: ViewBox,
1606 pub(crate) rendering_mode: ImageRendering,
1607 pub(crate) abs_transform: Transform,
1608 pub(crate) abs_bounding_box: NonZeroRect,
1609 pub(crate) origin_href: String,
1611 pub(crate) kind: ImageKind,
1612}
1613
1614impl std::hash::Hash for Image {
1615 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1616 self.visibility.hash(state);
1618 self.view_box.hash(state);
1619 self.rendering_mode.hash(state);
1620 self.abs_transform.custom_hash(state);
1621 self.abs_bounding_box.custom_hash(state);
1622 self.origin_href.hash(state);
1623 }
1624}
1625
1626impl Image {
1627 pub fn id(&self) -> &str {
1633 &self.id
1634 }
1635
1636 pub fn visibility(&self) -> Visibility {
1638 self.visibility
1639 }
1640
1641 pub fn view_box(&self) -> ViewBox {
1646 self.view_box
1647 }
1648
1649 pub fn rendering_mode(&self) -> ImageRendering {
1653 self.rendering_mode
1654 }
1655
1656 pub fn kind(&self) -> &ImageKind {
1658 &self.kind
1659 }
1660
1661 pub fn abs_transform(&self) -> Transform {
1668 self.abs_transform
1669 }
1670
1671 pub fn bounding_box(&self) -> Rect {
1675 self.view_box.rect.to_rect()
1676 }
1677
1678 pub fn abs_bounding_box(&self) -> Rect {
1682 self.abs_bounding_box.to_rect()
1683 }
1684
1685 fn subroots(&self, f: &mut dyn FnMut(&Group)) {
1686 if let ImageKind::SVG { ref tree, .. } = self.kind {
1687 f(&tree.root)
1688 }
1689 }
1690}
1691
1692#[allow(missing_debug_implementations)]
1694#[derive(Clone, Debug)]
1695pub struct Tree {
1696 pub(crate) size: Size,
1697 pub(crate) view_box: ViewBox,
1698 pub(crate) root: Group,
1699 pub(crate) linear_gradients: Vec<Arc<LinearGradient>>,
1700 pub(crate) radial_gradients: Vec<Arc<RadialGradient>>,
1701 pub(crate) patterns: Vec<Arc<Pattern>>,
1702 pub(crate) clip_paths: Vec<Arc<ClipPath>>,
1703 pub(crate) masks: Vec<Arc<Mask>>,
1704 pub(crate) filters: Vec<Arc<filter::Filter>>,
1705}
1706
1707impl Tree {
1708 pub fn size(&self) -> Size {
1714 self.size
1715 }
1716
1717 pub fn view_box(&self) -> ViewBox {
1723 self.view_box
1724 }
1725
1726 pub fn root(&self) -> &Group {
1728 &self.root
1729 }
1730
1731 pub fn node_by_id(&self, id: &str) -> Option<&Node> {
1735 if id.is_empty() {
1736 return None;
1737 }
1738
1739 node_by_id(&self.root, id)
1740 }
1741
1742 pub fn has_text_nodes(&self) -> bool {
1744 has_text_nodes(&self.root)
1745 }
1746
1747 pub fn linear_gradients(&self) -> &[Arc<LinearGradient>] {
1749 &self.linear_gradients
1750 }
1751
1752 pub fn radial_gradients(&self) -> &[Arc<RadialGradient>] {
1754 &self.radial_gradients
1755 }
1756
1757 pub fn patterns(&self) -> &[Arc<Pattern>] {
1759 &self.patterns
1760 }
1761
1762 pub fn clip_paths(&self) -> &[Arc<ClipPath>] {
1764 &self.clip_paths
1765 }
1766
1767 pub fn masks(&self) -> &[Arc<Mask>] {
1769 &self.masks
1770 }
1771
1772 pub fn filters(&self) -> &[Arc<filter::Filter>] {
1774 &self.filters
1775 }
1776
1777 pub(crate) fn collect_paint_servers(&mut self) {
1778 loop_over_paint_servers(&self.root, &mut |paint| match paint {
1779 Paint::Color(_) => {}
1780 Paint::LinearGradient(lg) => {
1781 if !self
1782 .linear_gradients
1783 .iter()
1784 .any(|other| Arc::ptr_eq(&lg, other))
1785 {
1786 self.linear_gradients.push(lg.clone());
1787 }
1788 }
1789 Paint::RadialGradient(rg) => {
1790 if !self
1791 .radial_gradients
1792 .iter()
1793 .any(|other| Arc::ptr_eq(&rg, other))
1794 {
1795 self.radial_gradients.push(rg.clone());
1796 }
1797 }
1798 Paint::Pattern(patt) => {
1799 if !self.patterns.iter().any(|other| Arc::ptr_eq(&patt, other)) {
1800 self.patterns.push(patt.clone());
1801 }
1802 }
1803 });
1804 }
1805}
1806
1807fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
1808 for child in &parent.children {
1809 if child.id() == id {
1810 return Some(child);
1811 }
1812
1813 if let Node::Group(ref g) = child {
1814 if let Some(n) = node_by_id(g, id) {
1815 return Some(n);
1816 }
1817 }
1818 }
1819
1820 None
1821}
1822
1823fn has_text_nodes(root: &Group) -> bool {
1824 for node in &root.children {
1825 if let Node::Text(_) = node {
1826 return true;
1827 }
1828
1829 let mut has_text = false;
1830
1831 if let Node::Image(ref image) = node {
1832 if let ImageKind::SVG { ref tree, .. } = image.kind {
1833 if has_text_nodes(&tree.root) {
1834 has_text = true;
1835 }
1836 }
1837 }
1838
1839 node.subroots(|subroot| has_text |= has_text_nodes(subroot));
1840
1841 if has_text {
1842 return true;
1843 }
1844 }
1845
1846 true
1847}
1848
1849fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
1850 fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
1851 if let Some(paint) = paint {
1852 f(paint);
1853 }
1854 }
1855
1856 for node in &parent.children {
1857 match node {
1858 Node::Group(ref group) => loop_over_paint_servers(group, f),
1859 Node::Path(ref path) => {
1860 push(path.fill.as_ref().map(|f| &f.paint), f);
1861 push(path.stroke.as_ref().map(|f| &f.paint), f);
1862 }
1863 Node::Image(_) => {}
1864 Node::Text(_) => {}
1866 }
1867
1868 node.subroots(|subroot| loop_over_paint_servers(subroot, f));
1869 }
1870}
1871
1872impl Group {
1873 pub(crate) fn collect_clip_paths(&self, clip_paths: &mut Vec<Arc<ClipPath>>) {
1874 for node in self.children() {
1875 if let Node::Group(ref g) = node {
1876 if let Some(ref clip) = g.clip_path {
1877 if !clip_paths.iter().any(|other| Arc::ptr_eq(&clip, other)) {
1878 clip_paths.push(clip.clone());
1879 }
1880
1881 if let Some(ref sub_clip) = clip.clip_path {
1882 if !clip_paths.iter().any(|other| Arc::ptr_eq(&sub_clip, other)) {
1883 clip_paths.push(sub_clip.clone());
1884 }
1885 }
1886 }
1887 }
1888
1889 node.subroots(|subroot| subroot.collect_clip_paths(clip_paths));
1890
1891 if let Node::Group(ref g) = node {
1892 g.collect_clip_paths(clip_paths);
1893 }
1894 }
1895 }
1896
1897 pub(crate) fn collect_masks(&self, masks: &mut Vec<Arc<Mask>>) {
1898 for node in self.children() {
1899 if let Node::Group(ref g) = node {
1900 if let Some(ref mask) = g.mask {
1901 if !masks.iter().any(|other| Arc::ptr_eq(&mask, other)) {
1902 masks.push(mask.clone());
1903 }
1904
1905 if let Some(ref sub_mask) = mask.mask {
1906 if !masks.iter().any(|other| Arc::ptr_eq(&sub_mask, other)) {
1907 masks.push(sub_mask.clone());
1908 }
1909 }
1910 }
1911 }
1912
1913 node.subroots(|subroot| subroot.collect_masks(masks));
1914
1915 if let Node::Group(ref g) = node {
1916 g.collect_masks(masks);
1917 }
1918 }
1919 }
1920
1921 pub(crate) fn collect_filters(&self, filters: &mut Vec<Arc<filter::Filter>>) {
1922 for node in self.children() {
1923 if let Node::Group(ref g) = node {
1924 for filter in g.filters() {
1925 if !filters.iter().any(|other| Arc::ptr_eq(&filter, other)) {
1926 filters.push(filter.clone());
1927 }
1928 }
1929 }
1930
1931 node.subroots(|subroot| subroot.collect_filters(filters));
1932
1933 if let Node::Group(ref g) = node {
1934 g.collect_filters(filters);
1935 }
1936 }
1937 }
1938
1939 pub(crate) fn calculate_object_bbox(&mut self) -> Option<NonZeroRect> {
1940 let mut bbox = BBox::default();
1941 for child in &self.children {
1942 let mut c_bbox = child.bounding_box();
1943 if let Node::Group(ref group) = child {
1944 if let Some(r) = c_bbox.transform(group.transform) {
1945 c_bbox = r;
1946 }
1947 }
1948
1949 bbox = bbox.expand(c_bbox);
1950 }
1951
1952 bbox.to_non_zero_rect()
1953 }
1954
1955 pub(crate) fn calculate_bounding_boxes(&mut self) -> Option<()> {
1956 let mut bbox = BBox::default();
1957 let mut abs_bbox = BBox::default();
1958 let mut stroke_bbox = BBox::default();
1959 let mut abs_stroke_bbox = BBox::default();
1960 let mut layer_bbox = BBox::default();
1961 for child in &self.children {
1962 {
1963 let mut c_bbox = child.bounding_box();
1964 if let Node::Group(ref group) = child {
1965 if let Some(r) = c_bbox.transform(group.transform) {
1966 c_bbox = r;
1967 }
1968 }
1969
1970 bbox = bbox.expand(c_bbox);
1971 }
1972
1973 abs_bbox = abs_bbox.expand(child.abs_bounding_box());
1974
1975 {
1976 let mut c_bbox = child.stroke_bounding_box();
1977 if let Node::Group(ref group) = child {
1978 if let Some(r) = c_bbox.transform(group.transform) {
1979 c_bbox = r;
1980 }
1981 }
1982
1983 stroke_bbox = stroke_bbox.expand(c_bbox);
1984 }
1985
1986 abs_stroke_bbox = abs_stroke_bbox.expand(child.abs_stroke_bounding_box());
1987
1988 if let Node::Group(ref group) = child {
1989 let r = group.layer_bounding_box;
1990 if let Some(r) = r.transform(group.transform) {
1991 layer_bbox = layer_bbox.expand(r);
1992 }
1993 } else {
1994 layer_bbox = layer_bbox.expand(child.stroke_bounding_box());
1996 }
1997 }
1998
1999 if let Some(bbox) = bbox.to_rect() {
2002 self.bounding_box = bbox;
2003 self.abs_bounding_box = abs_bbox.to_rect()?;
2004 self.stroke_bounding_box = stroke_bbox.to_rect()?;
2005 self.abs_stroke_bounding_box = abs_stroke_bbox.to_rect()?;
2006 }
2007
2008 if let Some(filter_bbox) = self.filters_bounding_box() {
2010 self.layer_bounding_box = filter_bbox;
2011 } else {
2012 self.layer_bounding_box = layer_bbox.to_non_zero_rect()?;
2013 }
2014
2015 self.abs_layer_bounding_box = self.layer_bounding_box.transform(self.abs_transform)?;
2016
2017 Some(())
2018 }
2019}