Files
deckcraft/crates/render/src/lib.rs
T

121 lines
4.3 KiB
Rust

//! The DeckCraft renderer: draws slides, layouts and masters with `vello_cpu`.
//!
//! One code path serves the editor canvas, thumbnails, image/PDF-raster export and the slide show:
//! [`render_slide`] draws the background, the master and layout graphics, then the slide's shapes
//! (fills with theme shading, gradients, pictures, outlines with dashes and arrowheads, shadows,
//! glow and soft edges, text, tables, charts, groups), optionally with per-shape animation state.
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
#![forbid(unsafe_code)]
mod bevel;
mod camera;
mod chart;
mod chartex;
mod colr;
mod extrude;
mod light;
mod compound;
mod extent;
mod images;
mod metafile;
mod morph_path;
mod morph_text;
mod paint;
mod pattern;
mod pathgrad;
mod placed;
mod radar;
mod ramp;
mod table;
mod transition;
mod warp;
use std::collections::HashMap;
use std::sync::Arc;
use deckcraft_color::Rgba;
use deckcraft_fonts::FontDb;
use deckcraft_geom::preset::{self, FillMode, Geometry, SubPath};
use deckcraft_model::resolve::{self, Ctx};
use deckcraft_model::style::{Effects, Fill, Line};
use deckcraft_model::text::TextBody;
use deckcraft_model::{Geom, PhType, Presentation, Shape, ShapeId, ShapeKind, Slide};
use deckcraft_text::datetime::{self, DateTime};
use deckcraft_text::{Fields, Opts};
use kurbo::{Affine, BezPath, PathEl, Point, Rect, Shape as _, Vec2};
use vello_common::filter_effects::{EdgeMode, Filter, FilterPrimitive};
use vello_cpu::{RenderContext, Resources, peniko};
pub use deckcraft_anim::{transition_frame, transition_layers, uses_slide_parts};
pub use extent::{Region, TILE, layout_extent, pasteboard_region, slide_extent};
pub use images::{decode as decode_image, gif_frame};
pub use morph_path::PathMorph;
pub use morph_text::TextMorph;
pub use pattern::{is_fg as pattern_is_fg, presets as pattern_presets, tile as pattern_tile};
pub use placed::{Placed, PlacedLink, PlacedShape, PlacedText, place_master_texts, place_slide, text_outline};
pub use table::cell_body as table_cell_body;
pub use table::cell_rects as table_cell_rects;
pub use table::row_heights as table_row_heights;
pub use transition::{SlideParts, composite_transition, render_transition};
mod geometry;
mod opts;
mod raster;
mod renderer;
mod slide;
mod stroke;
mod text_draw;
use geometry::shade;
pub use geometry::{camera_turn, crop_dest, parse_path, shape_geometry};
pub(crate) use geometry::{color, scale_of};
use opts::SlideFields;
pub use opts::{ParaState, RenderOpts, ShapeState};
pub use raster::{Image, MAX_SIDE, dpi_for_scale, image_dpi};
#[cfg(test)]
use renderer::RENDERER;
pub use renderer::Renderer;
pub use slide::{
SlidePart, needs_filters, panics_caught, render_blend, render_layout, render_shape, render_shapes, render_slide, render_slide_drawn,
render_slide_part,
};
pub(crate) use stroke::stroke;
#[cfg(test)]
use stroke::{MAX_DASHES, dash_with_caps, head_size, limit_dash_count, trim_ends};
use text_draw::preview_empty_bullets;
pub(crate) use text_draw::{draw_glyph, fill_deco, glyph, glyph_place};
pub use text_draw::{draw_layout, draw_layout_with, draw_text, text_transform, text_warp};
#[cfg(test)]
mod tests;
#[cfg(test)]
mod tests_wild;
#[cfg(test)]
mod mt_tests {
use super::*;
/// Regression: multithreaded rendering of a slide with shadows panicked inside vello_cpu.
#[test]
fn effects_render_with_threads_requested() {
let mut s = deckcraft_model::Presentation::default();
let sl = std::sync::Arc::make_mut(&mut s.slides[0]);
let mut sh = deckcraft_model::Shape {
id: ShapeId(900),
xfrm: Some(deckcraft_geom::Xfrm::new(10.0, 10.0, 100.0, 100.0)),
style: Some(deckcraft_model::ShapeStyle::accent(deckcraft_color::SchemeSlot::Accent1)),
..Default::default()
};
sh.effects = Some(deckcraft_model::Effects { soft_edge: Some(5.0), ..Default::default() });
sl.shapes.push(sh);
assert!(needs_filters(&s, &s.slides[0]));
let img = RENDERER.with(|r| r.borrow_mut().slide(&s, &s.slides[0], 0, &RenderOpts { scale: 0.5, threads: 4, ..Default::default() }));
assert_eq!(img.width, 480);
// The shape itself is drawn (not a blank fallback).
assert_ne!(img.pixel(30, 30), [255, 255, 255, 255]);
}
}