1use std::{
4 any::Any,
5 borrow::Cow,
6 cell::RefCell,
7 collections::HashMap,
8 rc::Rc,
9};
10
11use bytes::Bytes;
12use freya_engine::prelude::{
13 ClipOp,
14 LocalResourceProvider,
15 Paint,
16 SkRect,
17 svg,
18};
19use rustc_hash::FxHashMap;
20use torin::{
21 prelude::Size2D,
22 size::Size,
23};
24
25use crate::{
26 data::{
27 AccessibilityData,
28 EffectData,
29 LayoutData,
30 StyleState,
31 TextStyleData,
32 },
33 diff_key::DiffKey,
34 element::{
35 ClipContext,
36 Element,
37 ElementExt,
38 EventHandlerType,
39 LayoutContext,
40 RenderContext,
41 },
42 events::name::EventName,
43 layers::Layer,
44 prelude::{
45 AccessibilityExt,
46 Color,
47 ContainerExt,
48 EventHandlersExt,
49 KeyExt,
50 LayerExt,
51 LayoutExt,
52 MaybeExt,
53 },
54 tree::DiffModifies,
55};
56
57#[derive(Clone, PartialEq)]
60pub struct SvgBytes(Bytes);
61
62impl From<Bytes> for SvgBytes {
63 fn from(bytes: Bytes) -> Self {
64 Self(bytes)
65 }
66}
67
68impl From<Vec<u8>> for SvgBytes {
69 fn from(bytes: Vec<u8>) -> Self {
70 Self(Bytes::from(bytes))
71 }
72}
73
74impl From<&'static [u8]> for SvgBytes {
75 fn from(bytes: &'static [u8]) -> Self {
76 Self(Bytes::from_static(bytes))
77 }
78}
79
80impl<const N: usize> From<&'static [u8; N]> for SvgBytes {
81 fn from(bytes: &'static [u8; N]) -> Self {
82 Self(Bytes::from_static(bytes))
83 }
84}
85
86pub fn svg(bytes: impl Into<SvgBytes>) -> Svg {
97 let mut accessibility = AccessibilityData::default();
98 accessibility.builder.set_role(accesskit::Role::SvgRoot);
99
100 Svg {
101 key: DiffKey::None,
102 element: SvgElement {
103 accessibility,
104 layout: LayoutData::default(),
105 event_handlers: HashMap::default(),
106 bytes: bytes.into(),
107 effect: None,
108 color: None,
109 stroke: None,
110 stroke_width: None,
111 fill: None,
112 relative_layer: Layer::default(),
113 },
114 }
115}
116
117#[derive(PartialEq, Clone)]
118pub struct SvgElement {
119 pub accessibility: AccessibilityData,
120 pub layout: LayoutData,
121 pub event_handlers: FxHashMap<EventName, EventHandlerType>,
122 pub bytes: SvgBytes,
123 pub color: Option<Color>,
124 pub stroke: Option<Color>,
125 pub stroke_width: Option<f32>,
126 pub fill: Option<Color>,
127 pub effect: Option<EffectData>,
128 pub relative_layer: Layer,
129}
130
131impl ElementExt for SvgElement {
132 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
133 let Some(image) = (other.as_ref() as &dyn Any).downcast_ref::<SvgElement>() else {
134 return false;
135 };
136 self != image
137 }
138
139 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
140 let Some(svg) = (other.as_ref() as &dyn Any).downcast_ref::<SvgElement>() else {
141 return DiffModifies::all();
142 };
143
144 let mut diff = DiffModifies::empty();
145
146 if self.accessibility != svg.accessibility {
147 diff.insert(DiffModifies::ACCESSIBILITY);
148 }
149
150 if self.relative_layer != svg.relative_layer {
151 diff.insert(DiffModifies::LAYER);
152 }
153
154 if self.layout != svg.layout || self.bytes != svg.bytes {
155 diff.insert(DiffModifies::LAYOUT);
156 diff.insert(DiffModifies::STYLE);
157 }
158
159 if self.color != svg.color
160 || self.fill != svg.fill
161 || self.stroke != svg.stroke
162 || self.stroke_width != svg.stroke_width
163 {
164 diff.insert(DiffModifies::STYLE);
165 }
166
167 if self.effect != svg.effect {
168 diff.insert(DiffModifies::EFFECT);
169 }
170
171 diff
172 }
173
174 fn layout(&'_ self) -> Cow<'_, LayoutData> {
175 Cow::Borrowed(&self.layout)
176 }
177
178 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
179 self.effect.as_ref().map(Cow::Borrowed)
180 }
181
182 fn style(&'_ self) -> Cow<'_, StyleState> {
183 Cow::Owned(StyleState::default())
184 }
185
186 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
187 Cow::Owned(TextStyleData::default())
188 }
189
190 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
191 Cow::Borrowed(&self.accessibility)
192 }
193
194 fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
195 Some(Cow::Borrowed(&self.event_handlers))
196 }
197
198 fn layer(&self) -> Layer {
199 self.relative_layer
200 }
201
202 fn should_measure_inner_children(&self) -> bool {
203 false
204 }
205
206 fn should_hook_measurement(&self) -> bool {
207 true
208 }
209
210 fn measure(&self, context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
211 let resource_provider = LocalResourceProvider::new(context.font_manager);
212 let svg_dom = svg::Dom::from_bytes(&self.bytes.0, resource_provider);
213 if let Ok(mut svg_dom) = svg_dom {
214 svg_dom.set_container_size(context.area_size.to_i32().to_tuple());
215 let mut root = svg_dom.root();
216 match self.layout.width {
217 Size::Pixels(px) => {
218 root.set_width(svg::Length::new(
219 px.get() * context.scale_factor as f32,
220 svg::LengthUnit::PX,
221 ));
222 }
223 Size::Percentage(per) => {
224 root.set_width(svg::Length::new(per.get(), svg::LengthUnit::Percentage));
225 }
226 Size::Fill => {
227 root.set_width(svg::Length::new(100., svg::LengthUnit::Percentage));
228 }
229 _ => {}
230 }
231 match self.layout.height {
232 Size::Pixels(px) => {
233 root.set_height(svg::Length::new(
234 px.get() * context.scale_factor as f32,
235 svg::LengthUnit::PX,
236 ));
237 }
238 Size::Percentage(per) => {
239 root.set_height(svg::Length::new(per.get(), svg::LengthUnit::Percentage));
240 }
241 Size::Fill => {
242 root.set_height(svg::Length::new(100., svg::LengthUnit::Percentage));
243 }
244 _ => {}
245 }
246 if let Some(stroke_width) = self.stroke_width {
247 root.set_stroke_width(svg::Length::new(stroke_width, svg::LengthUnit::PX));
248 }
249 Some((
250 Size2D::new(root.width().value, root.height().value),
251 Rc::new(RefCell::new(svg_dom)),
252 ))
253 } else {
254 None
255 }
256 }
257
258 fn clip(&self, context: ClipContext) {
259 let area = context.visible_area;
260 context.canvas.clip_rect(
261 SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
262 ClipOp::Intersect,
263 true,
264 );
265 }
266
267 fn render(&self, context: RenderContext) {
268 let mut paint = Paint::default();
269 paint.set_anti_alias(true);
270
271 let svg_dom = context
272 .layout_node
273 .data
274 .as_ref()
275 .unwrap()
276 .downcast_ref::<RefCell<svg::Dom>>()
277 .unwrap();
278 let svg_dom = svg_dom.borrow();
279
280 let mut root = svg_dom.root();
281 context.canvas.save();
282 context
283 .canvas
284 .translate(context.layout_node.visible_area().origin.to_tuple());
285
286 root.set_color(self.color.unwrap_or(context.text_style_state.color).into());
287 if let Some(fill) = self.fill {
288 root.set_fill(svg::Paint::from_color(fill.into()));
289 }
290 if let Some(stroke) = self.stroke {
291 root.set_stroke(svg::Paint::from_color(stroke.into()));
292 }
293 if let Some(stroke_width) = self.stroke_width {
294 root.set_stroke_width(svg::Length::new(stroke_width, svg::LengthUnit::PX));
295 }
296 svg_dom.render(context.canvas);
297 context.canvas.restore();
298 }
299}
300
301impl From<Svg> for Element {
302 fn from(value: Svg) -> Self {
303 Element::Element {
304 key: value.key,
305 element: Rc::new(value.element),
306 elements: vec![],
307 }
308 }
309}
310
311impl KeyExt for Svg {
312 fn write_key(&mut self) -> &mut DiffKey {
313 &mut self.key
314 }
315}
316
317impl EventHandlersExt for Svg {
318 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
319 &mut self.element.event_handlers
320 }
321}
322
323impl LayoutExt for Svg {
324 fn get_layout(&mut self) -> &mut LayoutData {
325 &mut self.element.layout
326 }
327}
328
329impl ContainerExt for Svg {}
330
331impl AccessibilityExt for Svg {
332 fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
333 &mut self.element.accessibility
334 }
335}
336
337impl MaybeExt for Svg {}
338
339impl LayerExt for Svg {
340 fn get_layer(&mut self) -> &mut Layer {
341 &mut self.element.relative_layer
342 }
343}
344
345pub struct Svg {
346 key: DiffKey,
347 element: SvgElement,
348}
349
350impl Svg {
351 pub fn try_downcast(element: &dyn ElementExt) -> Option<SvgElement> {
352 (element as &dyn Any).downcast_ref::<SvgElement>().cloned()
353 }
354
355 pub fn color(mut self, color: impl Into<Color>) -> Self {
356 self.element.color = Some(color.into());
357 self
358 }
359
360 pub fn fill(mut self, fill: impl Into<Color>) -> Self {
361 self.element.fill = Some(fill.into());
362 self
363 }
364
365 pub fn stroke(mut self, stroke: impl Into<Color>) -> Self {
366 self.element.stroke = Some(stroke.into());
367 self
368 }
369
370 pub fn stroke_width(mut self, stroke_width: impl Into<f32>) -> Self {
372 self.element.stroke_width = Some(stroke_width.into());
373 self
374 }
375
376 pub fn rotate(mut self, rotation: impl Into<f32>) -> Self {
377 self.element
378 .effect
379 .get_or_insert_with(Default::default)
380 .rotation = Some(rotation.into());
381 self
382 }
383}