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#[derive(Debug)]
44pub struct Renderer {
45 default_font: Font,
46 default_text_size: Pixels,
47 layers: layer::Stack,
48 engine: Engine, }
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 ®ion 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}