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, Size, Transformation,
33};
34use crate::engine::Engine;
35use crate::graphics::compositor;
36use crate::graphics::text::{Editor, Paragraph};
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
265    const ICON_FONT: Font = Font::with_name("Iced-Icons");
266    const CHECKMARK_ICON: char = '\u{f00c}';
267    const ARROW_DOWN_ICON: char = '\u{e800}';
268
269    fn default_font(&self) -> Self::Font {
270        self.default_font
271    }
272
273    fn default_size(&self) -> Pixels {
274        self.default_text_size
275    }
276
277    fn fill_paragraph(
278        &mut self,
279        text: &Self::Paragraph,
280        position: Point,
281        color: Color,
282        clip_bounds: Rectangle,
283    ) {
284        let (layer, transformation) = self.layers.current_mut();
285
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
318#[cfg(feature = "geometry")]
319impl graphics::geometry::Renderer for Renderer {
320    type Geometry = Geometry;
321    type Frame = geometry::Frame;
322
323    fn new_frame(&self, size: core::Size) -> Self::Frame {
324        geometry::Frame::new(size)
325    }
326
327    fn draw_geometry(&mut self, geometry: Self::Geometry) {
328        let (layer, transformation) = self.layers.current_mut();
329
330        match geometry {
331            Geometry::Live {
332                primitives,
333                images,
334                text,
335                clip_bounds,
336            } => {
337                layer.draw_primitive_group(
338                    primitives,
339                    clip_bounds,
340                    transformation,
341                );
342
343                for image in images {
344                    layer.draw_image(image, transformation);
345                }
346
347                layer.draw_text_group(text, clip_bounds, transformation);
348            }
349            Geometry::Cache(cache) => {
350                layer.draw_primitive_cache(
351                    cache.primitives,
352                    cache.clip_bounds,
353                    transformation,
354                );
355
356                for image in cache.images.iter() {
357                    layer.draw_image(image.clone(), transformation);
358                }
359
360                layer.draw_text_cache(
361                    cache.text,
362                    cache.clip_bounds,
363                    transformation,
364                );
365            }
366        }
367    }
368}
369
370impl graphics::mesh::Renderer for Renderer {
371    fn draw_mesh(&mut self, _mesh: graphics::Mesh) {
372        log::warn!("iced_tiny_skia does not support drawing meshes");
373    }
374}
375
376#[cfg(feature = "image")]
377impl core::image::Renderer for Renderer {
378    type Handle = core::image::Handle;
379
380    fn measure_image(&self, handle: &Self::Handle) -> crate::core::Size<u32> {
381        self.engine.raster_pipeline.dimensions(handle)
382    }
383
384    fn draw_image(&mut self, image: core::Image, bounds: Rectangle) {
385        let (layer, transformation) = self.layers.current_mut();
386        layer.draw_raster(image, bounds, transformation);
387    }
388}
389
390#[cfg(feature = "svg")]
391impl core::svg::Renderer for Renderer {
392    fn measure_svg(
393        &self,
394        handle: &core::svg::Handle,
395    ) -> crate::core::Size<u32> {
396        self.engine.vector_pipeline.viewport_dimensions(handle)
397    }
398
399    fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle) {
400        let (layer, transformation) = self.layers.current_mut();
401        layer.draw_svg(svg, bounds, transformation);
402    }
403}
404
405impl compositor::Default for Renderer {
406    type Compositor = window::Compositor;
407}
408
409impl renderer::Headless for Renderer {
410    fn new(default_font: Font, default_text_size: Pixels) -> Self {
411        Self::new(default_font, default_text_size)
412    }
413
414    fn screenshot(
415        &mut self,
416        size: Size<u32>,
417        scale_factor: f32,
418        background_color: Color,
419    ) -> Vec<u8> {
420        let viewport =
421            Viewport::with_physical_size(size, f64::from(scale_factor));
422
423        window::compositor::screenshot::<&str>(
424            self,
425            &viewport,
426            background_color,
427            &[],
428        )
429    }
430}