iced_tiny_skia/
lib.rs

1#![allow(missing_docs)]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3pub mod window;
4
5mod engine;
6mod layer;
7mod primitive;
8mod settings;
9mod text;
10
11#[cfg(feature = "image")]
12mod raster;
13
14#[cfg(feature = "svg")]
15mod vector;
16
17#[cfg(feature = "geometry")]
18pub mod geometry;
19
20pub use iced_graphics as graphics;
21pub use iced_graphics::core;
22
23pub use layer::Layer;
24pub use primitive::Primitive;
25pub use settings::Settings;
26
27#[cfg(feature = "geometry")]
28pub use geometry::Geometry;
29
30use crate::core::renderer;
31use crate::core::{
32    Background, Color, Font, Pixels, Point, Rectangle, Transformation,
33};
34use crate::engine::Engine;
35use crate::graphics::compositor;
36use crate::graphics::text::{Editor, Paragraph, Raw};
37use crate::graphics::Viewport;
38
39/// A [`tiny-skia`] graphics renderer for [`iced`].
40///
41/// [`tiny-skia`]: https://github.com/RazrFalcon/tiny-skia
42/// [`iced`]: https://github.com/iced-rs/iced
43#[derive(Debug)]
44pub struct Renderer {
45    default_font: Font,
46    default_text_size: Pixels,
47    layers: layer::Stack,
48    engine: Engine, // TODO: Shared engine
49}
50
51impl Renderer {
52    pub fn new(default_font: Font, default_text_size: Pixels) -> Self {
53        Self {
54            default_font,
55            default_text_size,
56            layers: layer::Stack::new(),
57            engine: Engine::new(),
58        }
59    }
60
61    pub fn layers(&mut self) -> &[Layer] {
62        self.layers.flush();
63        self.layers.as_slice()
64    }
65
66    pub fn draw<T: AsRef<str>>(
67        &mut self,
68        pixels: &mut tiny_skia::PixmapMut<'_>,
69        clip_mask: &mut tiny_skia::Mask,
70        viewport: &Viewport,
71        damage: &[Rectangle],
72        background_color: Color,
73        overlay: &[T],
74    ) {
75        let physical_size = viewport.physical_size();
76        let scale_factor = viewport.scale_factor() as f32;
77
78        if !overlay.is_empty() {
79            let path = tiny_skia::PathBuilder::from_rect(
80                tiny_skia::Rect::from_xywh(
81                    0.0,
82                    0.0,
83                    physical_size.width as f32,
84                    physical_size.height as f32,
85                )
86                .expect("Create damage rectangle"),
87            );
88
89            pixels.fill_path(
90                &path,
91                &tiny_skia::Paint {
92                    shader: tiny_skia::Shader::SolidColor(engine::into_color(
93                        Color {
94                            a: 0.1,
95                            ..background_color
96                        },
97                    )),
98                    anti_alias: false,
99                    ..Default::default()
100                },
101                tiny_skia::FillRule::default(),
102                tiny_skia::Transform::identity(),
103                None,
104            );
105        }
106
107        self.layers.flush();
108
109        for &region in damage {
110            let region = region * scale_factor;
111
112            let path = tiny_skia::PathBuilder::from_rect(
113                tiny_skia::Rect::from_xywh(
114                    region.x,
115                    region.y,
116                    region.width,
117                    region.height,
118                )
119                .expect("Create damage rectangle"),
120            );
121
122            pixels.fill_path(
123                &path,
124                &tiny_skia::Paint {
125                    shader: tiny_skia::Shader::SolidColor(engine::into_color(
126                        background_color,
127                    )),
128                    anti_alias: false,
129                    blend_mode: tiny_skia::BlendMode::Source,
130                    ..Default::default()
131                },
132                tiny_skia::FillRule::default(),
133                tiny_skia::Transform::identity(),
134                None,
135            );
136
137            for layer in self.layers.iter() {
138                let Some(clip_bounds) =
139                    region.intersection(&(layer.bounds * scale_factor))
140                else {
141                    continue;
142                };
143
144                engine::adjust_clip_mask(clip_mask, clip_bounds);
145
146                for (quad, background) in &layer.quads {
147                    self.engine.draw_quad(
148                        quad,
149                        background,
150                        Transformation::scale(scale_factor),
151                        pixels,
152                        clip_mask,
153                        clip_bounds,
154                    );
155                }
156
157                for group in &layer.primitives {
158                    let Some(new_clip_bounds) = (group.clip_bounds()
159                        * scale_factor)
160                        .intersection(&clip_bounds)
161                    else {
162                        continue;
163                    };
164
165                    engine::adjust_clip_mask(clip_mask, new_clip_bounds);
166
167                    for primitive in group.as_slice() {
168                        self.engine.draw_primitive(
169                            primitive,
170                            group.transformation()
171                                * Transformation::scale(scale_factor),
172                            pixels,
173                            clip_mask,
174                            clip_bounds,
175                        );
176                    }
177
178                    engine::adjust_clip_mask(clip_mask, clip_bounds);
179                }
180
181                for image in &layer.images {
182                    self.engine.draw_image(
183                        image,
184                        Transformation::scale(scale_factor),
185                        pixels,
186                        clip_mask,
187                        clip_bounds,
188                    );
189                }
190
191                for group in &layer.text {
192                    for text in group.as_slice() {
193                        self.engine.draw_text(
194                            text,
195                            group.transformation()
196                                * Transformation::scale(scale_factor),
197                            pixels,
198                            clip_mask,
199                            clip_bounds,
200                        );
201                    }
202                }
203            }
204
205            if !overlay.is_empty() {
206                pixels.stroke_path(
207                    &path,
208                    &tiny_skia::Paint {
209                        shader: tiny_skia::Shader::SolidColor(
210                            engine::into_color(Color::from_rgb(1.0, 0.0, 0.0)),
211                        ),
212                        anti_alias: false,
213                        ..tiny_skia::Paint::default()
214                    },
215                    &tiny_skia::Stroke {
216                        width: 1.0,
217                        ..tiny_skia::Stroke::default()
218                    },
219                    tiny_skia::Transform::identity(),
220                    None,
221                );
222            }
223        }
224
225        self.engine.trim();
226    }
227}
228
229impl core::Renderer for Renderer {
230    fn start_layer(&mut self, bounds: Rectangle) {
231        self.layers.push_clip(bounds);
232    }
233
234    fn end_layer(&mut self) {
235        self.layers.pop_clip();
236    }
237
238    fn start_transformation(&mut self, transformation: Transformation) {
239        self.layers.push_transformation(transformation);
240    }
241
242    fn end_transformation(&mut self) {
243        self.layers.pop_transformation();
244    }
245
246    fn fill_quad(
247        &mut self,
248        quad: renderer::Quad,
249        background: impl Into<Background>,
250    ) {
251        let (layer, transformation) = self.layers.current_mut();
252        layer.draw_quad(quad, background.into(), transformation);
253    }
254
255    fn clear(&mut self) {
256        self.layers.clear();
257    }
258}
259
260impl core::text::Renderer for Renderer {
261    type Font = Font;
262    type Paragraph = Paragraph;
263    type Editor = Editor;
264    type Raw = Raw;
265
266    const ICON_FONT: Font = Font::with_name("Iced-Icons");
267    const CHECKMARK_ICON: char = '\u{f00c}';
268    const ARROW_DOWN_ICON: char = '\u{e800}';
269
270    fn default_font(&self) -> Self::Font {
271        self.default_font
272    }
273
274    fn default_size(&self) -> Pixels {
275        self.default_text_size
276    }
277
278    fn fill_paragraph(
279        &mut self,
280        text: &Self::Paragraph,
281        position: Point,
282        color: Color,
283        clip_bounds: Rectangle,
284    ) {
285        let (layer, transformation) = self.layers.current_mut();
286        layer.draw_paragraph(
287            text,
288            position,
289            color,
290            clip_bounds,
291            transformation,
292        );
293    }
294
295    fn fill_editor(
296        &mut self,
297        editor: &Self::Editor,
298        position: Point,
299        color: Color,
300        clip_bounds: Rectangle,
301    ) {
302        let (layer, transformation) = self.layers.current_mut();
303        layer.draw_editor(editor, position, color, clip_bounds, transformation);
304    }
305
306    fn fill_text(
307        &mut self,
308        text: core::Text,
309        position: Point,
310        color: Color,
311        clip_bounds: Rectangle,
312    ) {
313        let (layer, transformation) = self.layers.current_mut();
314        layer.draw_text(text, position, color, clip_bounds, transformation);
315    }
316
317    fn fill_raw(&mut self, raw: Self::Raw) {
318        let (layer, transformation) = self.layers.current_mut();
319        layer.draw_raw(raw, transformation);
320    }
321}
322
323#[cfg(feature = "geometry")]
324impl graphics::geometry::Renderer for Renderer {
325    type Geometry = Geometry;
326    type Frame = geometry::Frame;
327
328    fn new_frame(&self, size: core::Size) -> Self::Frame {
329        geometry::Frame::new(size)
330    }
331
332    fn draw_geometry(&mut self, geometry: Self::Geometry) {
333        let (layer, transformation) = self.layers.current_mut();
334
335        match geometry {
336            Geometry::Live {
337                primitives,
338                images,
339                text,
340                clip_bounds,
341            } => {
342                layer.draw_primitive_group(
343                    primitives,
344                    clip_bounds,
345                    transformation,
346                );
347
348                for image in images {
349                    layer.draw_image(image, transformation);
350                }
351
352                layer.draw_text_group(text, clip_bounds, transformation);
353            }
354            Geometry::Cache(cache) => {
355                layer.draw_primitive_cache(
356                    cache.primitives,
357                    cache.clip_bounds,
358                    transformation,
359                );
360
361                for image in cache.images.iter() {
362                    layer.draw_image(image.clone(), transformation);
363                }
364
365                layer.draw_text_cache(
366                    cache.text,
367                    cache.clip_bounds,
368                    transformation,
369                );
370            }
371        }
372    }
373}
374
375impl graphics::mesh::Renderer for Renderer {
376    fn draw_mesh(&mut self, _mesh: graphics::Mesh) {
377        log::warn!("iced_tiny_skia does not support drawing meshes");
378    }
379}
380
381#[cfg(feature = "image")]
382impl core::image::Renderer for Renderer {
383    type Handle = core::image::Handle;
384
385    fn measure_image(&self, handle: &Self::Handle) -> crate::core::Size<u32> {
386        self.engine.raster_pipeline.dimensions(handle)
387    }
388
389    fn draw_image(
390        &mut self,
391        handle: Self::Handle,
392        filter_method: core::image::FilterMethod,
393        bounds: Rectangle,
394        rotation: core::Radians,
395        opacity: f32,
396        border_radius: [f32; 4],
397    ) {
398        let (layer, transformation) = self.layers.current_mut();
399        layer.draw_raster(
400            crate::core::Image {
401                handle,
402                filter_method,
403                rotation,
404                opacity,
405                snap: true,
406                border_radius,
407            },
408            bounds,
409            transformation,
410        );
411    }
412}
413
414#[cfg(feature = "svg")]
415impl core::svg::Renderer for Renderer {
416    fn measure_svg(
417        &self,
418        handle: &core::svg::Handle,
419    ) -> crate::core::Size<u32> {
420        self.engine.vector_pipeline.viewport_dimensions(handle)
421    }
422
423    fn draw_svg(&mut self, svg: core::svg::Svg, bounds: Rectangle) {
424        let (layer, transformation) = self.layers.current_mut();
425        layer.draw_svg(svg, bounds, transformation);
426    }
427}
428
429impl compositor::Default for Renderer {
430    type Compositor = window::Compositor;
431}