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