1#![allow(missing_docs)]
2#![cfg_attr(docsrs, feature(doc_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();
76
77 self.layers.flush();
78
79 for &damage_bounds in damage {
80 let damage_bounds = damage_bounds * scale_factor;
81
82 let path = tiny_skia::PathBuilder::from_rect(
83 tiny_skia::Rect::from_xywh(
84 damage_bounds.x,
85 damage_bounds.y,
86 damage_bounds.width,
87 damage_bounds.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(layer_bounds) =
109 damage_bounds.intersection(&(layer.bounds * scale_factor))
110 else {
111 continue;
112 };
113
114 engine::adjust_clip_mask(clip_mask, layer_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 layer_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(group_bounds) = (group.clip_bounds()
136 * group.transformation()
137 * scale_factor)
138 .intersection(&layer_bounds)
139 else {
140 continue;
141 };
142
143 engine::adjust_clip_mask(clip_mask, group_bounds);
144
145 for primitive in group.as_slice() {
146 self.engine.draw_primitive(
147 primitive,
148 group.transformation()
149 * Transformation::scale(scale_factor),
150 pixels,
151 clip_mask,
152 group_bounds,
153 );
154 }
155
156 engine::adjust_clip_mask(clip_mask, layer_bounds);
157 }
158
159 render_span.finish();
160 }
161
162 if !layer.images.is_empty() {
163 let render_span = debug::render(debug::Primitive::Image);
164
165 for image in &layer.images {
166 self.engine.draw_image(
167 image,
168 Transformation::scale(scale_factor),
169 pixels,
170 clip_mask,
171 layer_bounds,
172 );
173 }
174
175 render_span.finish();
176 }
177
178 if !layer.text.is_empty() {
179 let render_span = debug::render(debug::Primitive::Image);
180
181 for group in &layer.text {
182 for text in group.as_slice() {
183 self.engine.draw_text(
184 text,
185 group.transformation()
186 * Transformation::scale(scale_factor),
187 pixels,
188 clip_mask,
189 layer_bounds,
190 );
191 }
192 }
193
194 render_span.finish();
195 }
196 }
197 }
198
199 self.engine.trim();
200 }
201}
202
203impl core::Renderer for Renderer {
204 fn start_layer(&mut self, bounds: Rectangle) {
205 self.layers.push_clip(bounds);
206 }
207
208 fn end_layer(&mut self) {
209 self.layers.pop_clip();
210 }
211
212 fn start_transformation(&mut self, transformation: Transformation) {
213 self.layers.push_transformation(transformation);
214 }
215
216 fn end_transformation(&mut self) {
217 self.layers.pop_transformation();
218 }
219
220 fn fill_quad(
221 &mut self,
222 quad: renderer::Quad,
223 background: impl Into<Background>,
224 ) {
225 let (layer, transformation) = self.layers.current_mut();
226 layer.draw_quad(quad, background.into(), transformation);
227 }
228
229 fn reset(&mut self, new_bounds: Rectangle) {
230 self.layers.reset(new_bounds);
231 }
232
233 fn allocate_image(
234 &mut self,
235 _handle: &core::image::Handle,
236 callback: impl FnOnce(Result<core::image::Allocation, core::image::Error>)
237 + Send
238 + 'static,
239 ) {
240 #[cfg(feature = "image")]
241 #[allow(unsafe_code)]
242 callback(self.engine.raster_pipeline.load(_handle));
244
245 #[cfg(not(feature = "image"))]
246 callback(Err(core::image::Error::Unsupported));
247 }
248}
249
250impl core::text::Renderer for Renderer {
251 type Font = Font;
252 type Paragraph = Paragraph;
253 type Editor = Editor;
254
255 const ICON_FONT: Font = Font::with_name("Iced-Icons");
256 const CHECKMARK_ICON: char = '\u{f00c}';
257 const ARROW_DOWN_ICON: char = '\u{e800}';
258 const ICED_LOGO: char = '\u{e801}';
259 const SCROLL_UP_ICON: char = '\u{e802}';
260 const SCROLL_DOWN_ICON: char = '\u{e803}';
261 const SCROLL_LEFT_ICON: char = '\u{e804}';
262 const SCROLL_RIGHT_ICON: char = '\u{e805}';
263
264 fn default_font(&self) -> Self::Font {
265 self.default_font
266 }
267
268 fn default_size(&self) -> Pixels {
269 self.default_text_size
270 }
271
272 fn fill_paragraph(
273 &mut self,
274 text: &Self::Paragraph,
275 position: Point,
276 color: Color,
277 clip_bounds: Rectangle,
278 ) {
279 let (layer, transformation) = self.layers.current_mut();
280
281 layer.draw_paragraph(
282 text,
283 position,
284 color,
285 clip_bounds,
286 transformation,
287 );
288 }
289
290 fn fill_editor(
291 &mut self,
292 editor: &Self::Editor,
293 position: Point,
294 color: Color,
295 clip_bounds: Rectangle,
296 ) {
297 let (layer, transformation) = self.layers.current_mut();
298 layer.draw_editor(editor, position, color, clip_bounds, transformation);
299 }
300
301 fn fill_text(
302 &mut self,
303 text: core::Text,
304 position: Point,
305 color: Color,
306 clip_bounds: Rectangle,
307 ) {
308 let (layer, transformation) = self.layers.current_mut();
309 layer.draw_text(text, position, color, clip_bounds, transformation);
310 }
311}
312
313impl graphics::text::Renderer for Renderer {
314 fn fill_raw(&mut self, raw: graphics::text::Raw) {
315 let (layer, transformation) = self.layers.current_mut();
316 layer.draw_text_raw(raw, transformation);
317 }
318}
319
320#[cfg(feature = "geometry")]
321impl graphics::geometry::Renderer for Renderer {
322 type Geometry = Geometry;
323 type Frame = geometry::Frame;
324
325 fn new_frame(&self, bounds: Rectangle) -> Self::Frame {
326 geometry::Frame::new(bounds)
327 }
328
329 fn draw_geometry(&mut self, geometry: Self::Geometry) {
330 let (layer, transformation) = self.layers.current_mut();
331
332 match geometry {
333 Geometry::Live {
334 primitives,
335 images,
336 text,
337 clip_bounds,
338 } => {
339 layer.draw_primitive_group(
340 primitives,
341 clip_bounds,
342 transformation,
343 );
344
345 for image in images {
346 layer.draw_image(image, transformation);
347 }
348
349 layer.draw_text_group(text, clip_bounds, transformation);
350 }
351 Geometry::Cache(cache) => {
352 layer.draw_primitive_cache(
353 cache.primitives,
354 cache.clip_bounds,
355 transformation,
356 );
357
358 for image in cache.images.iter() {
359 layer.draw_image(image.clone(), transformation);
360 }
361
362 layer.draw_text_cache(
363 cache.text,
364 cache.clip_bounds,
365 transformation,
366 );
367 }
368 }
369 }
370}
371
372impl graphics::mesh::Renderer for Renderer {
373 fn draw_mesh(&mut self, _mesh: graphics::Mesh) {
374 log::warn!("iced_tiny_skia does not support drawing meshes");
375 }
376
377 fn draw_mesh_cache(&mut self, _cache: iced_graphics::mesh::Cache) {
378 log::warn!("iced_tiny_skia does not support drawing meshes");
379 }
380}
381
382#[cfg(feature = "image")]
383impl core::image::Renderer for Renderer {
384 type Handle = core::image::Handle;
385
386 fn load_image(
387 &self,
388 handle: &Self::Handle,
389 ) -> Result<core::image::Allocation, core::image::Error> {
390 self.engine.raster_pipeline.load(handle)
391 }
392
393 fn measure_image(
394 &self,
395 handle: &Self::Handle,
396 ) -> Option<crate::core::Size<u32>> {
397 self.engine.raster_pipeline.dimensions(handle)
398 }
399
400 fn draw_image(
401 &mut self,
402 image: core::Image,
403 bounds: Rectangle,
404 clip_bounds: Rectangle,
405 ) {
406 let (layer, transformation) = self.layers.current_mut();
407 layer.draw_raster(image, bounds, clip_bounds, transformation);
408 }
409}
410
411#[cfg(feature = "svg")]
412impl core::svg::Renderer for Renderer {
413 fn measure_svg(
414 &self,
415 handle: &core::svg::Handle,
416 ) -> crate::core::Size<u32> {
417 self.engine.vector_pipeline.viewport_dimensions(handle)
418 }
419
420 fn draw_svg(
421 &mut self,
422 svg: core::Svg,
423 bounds: Rectangle,
424 clip_bounds: Rectangle,
425 ) {
426 let (layer, transformation) = self.layers.current_mut();
427 layer.draw_svg(svg, bounds, clip_bounds, transformation);
428 }
429}
430
431impl compositor::Default for Renderer {
432 type Compositor = window::Compositor;
433}
434
435impl renderer::Headless for Renderer {
436 async fn new(
437 default_font: Font,
438 default_text_size: Pixels,
439 backend: Option<&str>,
440 ) -> Option<Self> {
441 if backend.is_some_and(|backend| {
442 !["tiny-skia", "tiny_skia"].contains(&backend)
443 }) {
444 return None;
445 }
446
447 Some(Self::new(default_font, default_text_size))
448 }
449
450 fn name(&self) -> String {
451 "tiny-skia".to_owned()
452 }
453
454 fn screenshot(
455 &mut self,
456 size: Size<u32>,
457 scale_factor: f32,
458 background_color: Color,
459 ) -> Vec<u8> {
460 let viewport = Viewport::with_physical_size(size, scale_factor);
461
462 window::compositor::screenshot(self, &viewport, background_color)
463 }
464}