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#[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 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}