summaryrefslogtreecommitdiffstats
path: root/tiny_skia/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tiny_skia/src/lib.rs')
-rw-r--r--tiny_skia/src/lib.rs405
1 files changed, 400 insertions, 5 deletions
diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs
index e7294f9b..1aabff00 100644
--- a/tiny_skia/src/lib.rs
+++ b/tiny_skia/src/lib.rs
@@ -1,9 +1,9 @@
-#![forbid(rust_2018_idioms)]
-#![deny(unsafe_code, unused_results, rustdoc::broken_intra_doc_links)]
+#![allow(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod window;
-mod backend;
+mod engine;
+mod layer;
mod primitive;
mod settings;
mod text;
@@ -20,12 +20,407 @@ pub mod geometry;
pub use iced_graphics as graphics;
pub use iced_graphics::core;
-pub use backend::Backend;
+pub use layer::Layer;
pub use primitive::Primitive;
pub use settings::Settings;
+#[cfg(feature = "geometry")]
+pub use geometry::Geometry;
+
+use crate::core::renderer;
+use crate::core::{
+ Background, Color, Font, Pixels, Point, Rectangle, Transformation,
+};
+use crate::engine::Engine;
+use crate::graphics::compositor;
+use crate::graphics::text::{Editor, Paragraph};
+use crate::graphics::Viewport;
+
/// A [`tiny-skia`] graphics renderer for [`iced`].
///
/// [`tiny-skia`]: https://github.com/RazrFalcon/tiny-skia
/// [`iced`]: https://github.com/iced-rs/iced
-pub type Renderer = iced_graphics::Renderer<Backend>;
+#[derive(Debug)]
+pub struct Renderer {
+ default_font: Font,
+ default_text_size: Pixels,
+ layers: layer::Stack,
+ engine: Engine, // TODO: Shared engine
+}
+
+impl Renderer {
+ pub fn new(default_font: Font, default_text_size: Pixels) -> Self {
+ Self {
+ default_font,
+ default_text_size,
+ layers: layer::Stack::new(),
+ engine: Engine::new(),
+ }
+ }
+
+ pub fn layers(&mut self) -> &[Layer] {
+ self.layers.flush();
+ self.layers.as_slice()
+ }
+
+ pub fn draw<T: AsRef<str>>(
+ &mut self,
+ pixels: &mut tiny_skia::PixmapMut<'_>,
+ clip_mask: &mut tiny_skia::Mask,
+ viewport: &Viewport,
+ damage: &[Rectangle],
+ background_color: Color,
+ overlay: &[T],
+ ) {
+ let physical_size = viewport.physical_size();
+ let scale_factor = viewport.scale_factor() as f32;
+
+ if !overlay.is_empty() {
+ let path = tiny_skia::PathBuilder::from_rect(
+ tiny_skia::Rect::from_xywh(
+ 0.0,
+ 0.0,
+ physical_size.width as f32,
+ physical_size.height as f32,
+ )
+ .expect("Create damage rectangle"),
+ );
+
+ pixels.fill_path(
+ &path,
+ &tiny_skia::Paint {
+ shader: tiny_skia::Shader::SolidColor(engine::into_color(
+ Color {
+ a: 0.1,
+ ..background_color
+ },
+ )),
+ anti_alias: false,
+ ..Default::default()
+ },
+ tiny_skia::FillRule::default(),
+ tiny_skia::Transform::identity(),
+ None,
+ );
+ }
+
+ self.layers.flush();
+
+ for &region in damage {
+ let region = region * scale_factor;
+
+ let path = tiny_skia::PathBuilder::from_rect(
+ tiny_skia::Rect::from_xywh(
+ region.x,
+ region.y,
+ region.width,
+ region.height,
+ )
+ .expect("Create damage rectangle"),
+ );
+
+ pixels.fill_path(
+ &path,
+ &tiny_skia::Paint {
+ shader: tiny_skia::Shader::SolidColor(engine::into_color(
+ background_color,
+ )),
+ anti_alias: false,
+ blend_mode: tiny_skia::BlendMode::Source,
+ ..Default::default()
+ },
+ tiny_skia::FillRule::default(),
+ tiny_skia::Transform::identity(),
+ None,
+ );
+
+ for layer in self.layers.iter() {
+ let Some(clip_bounds) =
+ region.intersection(&(layer.bounds * scale_factor))
+ else {
+ continue;
+ };
+
+ engine::adjust_clip_mask(clip_mask, clip_bounds);
+
+ for (quad, background) in &layer.quads {
+ self.engine.draw_quad(
+ quad,
+ background,
+ Transformation::scale(scale_factor),
+ pixels,
+ clip_mask,
+ clip_bounds,
+ );
+ }
+
+ for group in &layer.primitives {
+ let Some(new_clip_bounds) = (group.clip_bounds()
+ * scale_factor)
+ .intersection(&clip_bounds)
+ else {
+ continue;
+ };
+
+ engine::adjust_clip_mask(clip_mask, new_clip_bounds);
+
+ for primitive in group.as_slice() {
+ self.engine.draw_primitive(
+ primitive,
+ group.transformation()
+ * Transformation::scale(scale_factor),
+ pixels,
+ clip_mask,
+ clip_bounds,
+ );
+ }
+
+ engine::adjust_clip_mask(clip_mask, clip_bounds);
+ }
+
+ for group in &layer.text {
+ for text in group.as_slice() {
+ self.engine.draw_text(
+ text,
+ group.transformation()
+ * Transformation::scale(scale_factor),
+ pixels,
+ clip_mask,
+ clip_bounds,
+ );
+ }
+ }
+
+ for image in &layer.images {
+ self.engine.draw_image(
+ image,
+ Transformation::scale(scale_factor),
+ pixels,
+ clip_mask,
+ clip_bounds,
+ );
+ }
+ }
+
+ if !overlay.is_empty() {
+ pixels.stroke_path(
+ &path,
+ &tiny_skia::Paint {
+ shader: tiny_skia::Shader::SolidColor(
+ engine::into_color(Color::from_rgb(1.0, 0.0, 0.0)),
+ ),
+ anti_alias: false,
+ ..tiny_skia::Paint::default()
+ },
+ &tiny_skia::Stroke {
+ width: 1.0,
+ ..tiny_skia::Stroke::default()
+ },
+ tiny_skia::Transform::identity(),
+ None,
+ );
+ }
+ }
+
+ self.engine.trim();
+ }
+}
+
+impl core::Renderer for Renderer {
+ fn start_layer(&mut self, bounds: Rectangle) {
+ self.layers.push_clip(bounds);
+ }
+
+ fn end_layer(&mut self) {
+ self.layers.pop_clip();
+ }
+
+ fn start_transformation(&mut self, transformation: Transformation) {
+ self.layers.push_transformation(transformation);
+ }
+
+ fn end_transformation(&mut self) {
+ self.layers.pop_transformation();
+ }
+
+ fn fill_quad(
+ &mut self,
+ quad: renderer::Quad,
+ background: impl Into<Background>,
+ ) {
+ let (layer, transformation) = self.layers.current_mut();
+ layer.draw_quad(quad, background.into(), transformation);
+ }
+
+ fn clear(&mut self) {
+ self.layers.clear();
+ }
+}
+
+impl core::text::Renderer for Renderer {
+ type Font = Font;
+ type Paragraph = Paragraph;
+ type Editor = Editor;
+
+ const ICON_FONT: Font = Font::with_name("Iced-Icons");
+ const CHECKMARK_ICON: char = '\u{f00c}';
+ const ARROW_DOWN_ICON: char = '\u{e800}';
+
+ fn default_font(&self) -> Self::Font {
+ self.default_font
+ }
+
+ fn default_size(&self) -> Pixels {
+ self.default_text_size
+ }
+
+ fn fill_paragraph(
+ &mut self,
+ text: &Self::Paragraph,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ let (layer, transformation) = self.layers.current_mut();
+
+ layer.draw_paragraph(
+ text,
+ position,
+ color,
+ clip_bounds,
+ transformation,
+ );
+ }
+
+ fn fill_editor(
+ &mut self,
+ editor: &Self::Editor,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ let (layer, transformation) = self.layers.current_mut();
+ layer.draw_editor(editor, position, color, clip_bounds, transformation);
+ }
+
+ fn fill_text(
+ &mut self,
+ text: core::Text,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ let (layer, transformation) = self.layers.current_mut();
+ layer.draw_text(text, position, color, clip_bounds, transformation);
+ }
+}
+
+#[cfg(feature = "geometry")]
+impl graphics::geometry::Renderer for Renderer {
+ type Geometry = Geometry;
+ type Frame = geometry::Frame;
+
+ fn new_frame(&self, size: core::Size) -> Self::Frame {
+ geometry::Frame::new(size)
+ }
+
+ fn draw_geometry(&mut self, geometry: Self::Geometry) {
+ let (layer, transformation) = self.layers.current_mut();
+
+ match geometry {
+ Geometry::Live {
+ primitives,
+ text,
+ clip_bounds,
+ } => {
+ layer.draw_primitive_group(
+ primitives,
+ clip_bounds,
+ transformation,
+ );
+
+ layer.draw_text_group(text, clip_bounds, transformation);
+ }
+ Geometry::Cache(cache) => {
+ layer.draw_primitive_cache(
+ cache.primitives,
+ cache.clip_bounds,
+ transformation,
+ );
+
+ layer.draw_text_cache(
+ cache.text,
+ cache.clip_bounds,
+ transformation,
+ );
+ }
+ }
+ }
+}
+
+impl graphics::mesh::Renderer for Renderer {
+ fn draw_mesh(&mut self, _mesh: graphics::Mesh) {
+ log::warn!("iced_tiny_skia does not support drawing meshes");
+ }
+}
+
+#[cfg(feature = "image")]
+impl core::image::Renderer for Renderer {
+ type Handle = core::image::Handle;
+
+ fn measure_image(&self, handle: &Self::Handle) -> crate::core::Size<u32> {
+ self.engine.raster_pipeline.dimensions(handle)
+ }
+
+ fn draw_image(
+ &mut self,
+ handle: Self::Handle,
+ filter_method: core::image::FilterMethod,
+ bounds: Rectangle,
+ rotation: core::Radians,
+ opacity: f32,
+ ) {
+ let (layer, transformation) = self.layers.current_mut();
+ layer.draw_image(
+ handle,
+ filter_method,
+ bounds,
+ transformation,
+ rotation,
+ opacity,
+ );
+ }
+}
+
+#[cfg(feature = "svg")]
+impl core::svg::Renderer for Renderer {
+ fn measure_svg(
+ &self,
+ handle: &core::svg::Handle,
+ ) -> crate::core::Size<u32> {
+ self.engine.vector_pipeline.viewport_dimensions(handle)
+ }
+
+ fn draw_svg(
+ &mut self,
+ handle: core::svg::Handle,
+ color: Option<Color>,
+ bounds: Rectangle,
+ rotation: core::Radians,
+ opacity: f32,
+ ) {
+ let (layer, transformation) = self.layers.current_mut();
+ layer.draw_svg(
+ handle,
+ color,
+ bounds,
+ transformation,
+ rotation,
+ opacity,
+ );
+ }
+}
+
+impl compositor::Default for Renderer {
+ type Compositor = window::Compositor;
+}