mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 22:21:59 +00:00
A live-effect plug-in that paints its pieces draws each piece's brushed stroke along the piece's path at the piece's stroke width. Culling left room for brush art from the object's own stroke width around the pieces, so brush art of a moved or wider piece was cut off at the edge of the view. Culling now leaves 16 x the piece's stroke width + 8 pt past each brushed piece, and does so for a symbol instance whose art holds such a path (measured once per symbol) and for a blend, envelope or repeat that holds one. Object > Rasterize measures the brush art drawn along each piece and leaves out the object's own brush art, which is not drawn. Groups and layers now leave room for their members' brush art. The cull bounds of a group or layer united its members' bounds without the room each brushed member leaves, so the painted bounds of a group, and the area redrawn when it changes, left out brush art past its members' strokes. That applied to plain brushed paths as well as painted pieces. Each member is now measured as the renderer measures it when it draws the group, in the members of a group with a fill, stroke or adjustment of its own and in a group with crop marks or Vector Halftone too.
90 lines
4.8 KiB
Rust
90 lines
4.8 KiB
Rust
//! Live effects in the renderer.
|
||
//!
|
||
//! Geometry effects (object-level, then per fill/stroke) rewrite the path before painting via
|
||
//! `vectorcraft-effects`. Raster effects use vello_cpu filter layers. Those only work in
|
||
//! single-threaded contexts, so on the multithreaded pipeline each filter layer is rendered
|
||
//! offscreen on the calling thread (cropped to its reach) and composited back with the same blend
|
||
//! mode and opacity ([`Renderer::with_filters`]):
|
||
//!
|
||
//! - Drop Shadow / Outer Glow: the object's silhouette in a `DropShadowOnly` filter layer, painted
|
||
//! below the object with the effect's blend mode and opacity;
|
||
//! - Gaussian Blur: the object painted inside a Gaussian filter layer;
|
||
//! - Feather: like Gaussian Blur but clipped to the shape, so the edge fades inward;
|
||
//! - Inner Glow: a blurred inverse silhouette (Edge) or the blurred silhouette (Center), clipped
|
||
//! to the shape and painted above it;
|
||
//! - Photoshop-style filters (Radial Blur, Smart Blur, Unsharp Mask…): the content is rendered
|
||
//! offscreen and its pixels run through the content-changing effects in stack order (those
|
||
//! filters, Gaussian Blur and Feather, see [`effects::pixel`]), then drawn back
|
||
//! ([`Renderer::pixel_content`]). The result is cached per object while it and the zoom don't
|
||
//! change.
|
||
//! - Rasterize: the content drawn again on the image's own grid, as Object › Rasterize draws it
|
||
//! ([`Renderer::rasterize_image`]), then into the content's raster
|
||
//! ([`effects::Rasterize::compose`]; an image too large for that is made from the content's
|
||
//! raster, [`effects::Rasterize::apply`]), clipped to its outline with Create Clipping Mask. The
|
||
//! effects before it in the stack, and the shadows and glows after it, are drawn around it by
|
||
//! splitting the stack there ([`Renderer::raster_fx`]).
|
||
//!
|
||
//! While [`crate::RenderOptions::interacting`], an object whose content only moved draws its
|
||
//! cached shadows, glows and filtered content at its new place instead of running the effects
|
||
//! again ([`Renderer::move_shadow`], [`Renderer::move_pixel_content`]).
|
||
//!
|
||
//! While [`crate::RenderOptions::dragging`] is set, the renderer draws filtered content that runs
|
||
//! its effects within [`DRAG_PIXEL_BUDGET`] pixels, at a lower resolution where it would need more,
|
||
//! and keeps no raster made that way. On the multithreaded pipeline it does the same for shadows
|
||
//! and glows. The frame after the drag renders them as a still frame does.
|
||
//!
|
||
//! A cached raster is made again when something its content reads from outside the object
|
||
//! changes ([`Inputs`]): the pattern definitions, symbols, images and brushes it uses, the
|
||
//! document's raster resolution and color mode, the substitution highlights, the knockout its
|
||
//! object inherits, the color settings and fonts, the plug-ins its plug-in effects run, and the options
|
||
//! and layer settings it is drawn with.
|
||
//!
|
||
//! The same applies to one fill or stroke's own raster effects (around that item alone) and to
|
||
//! type, images, symbol instances and live objects ([`Renderer::draw_object_fx`]): their art is
|
||
//! drawn offscreen in the filter layers, reshaped first by their geometry effects. Groups and
|
||
//! layers with an appearance of their own take the same path with their evaluated art
|
||
//! ([`effects::evaluate_container`]: their fills and strokes painting the members, their geometry
|
||
//! effects reshaping them), so their raster effects apply to the composite (one combined shadow).
|
||
|
||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||
use std::sync::{Arc, Weak};
|
||
|
||
use vectorcraft_color::cms::Cms;
|
||
use vectorcraft_doc::{AppearanceItem, Document, Effect, Node, NodeId, NodeKind, SymbolBoxes};
|
||
use vectorcraft_effects::pixel::{Channels, PixelFx};
|
||
use vectorcraft_effects::{self as effects, GeomContext, RasterFx};
|
||
use vectorcraft_geom::{Affine, BezPath, FillRule, PathEl, Point, Rect, Shape, Size, Vec2};
|
||
use vello_common::filter_effects::{EdgeMode, Filter, FilterPrimitive};
|
||
use vello_cpu::RenderContext;
|
||
use vello_cpu::peniko;
|
||
|
||
use crate::ink::Ink;
|
||
use crate::{Frame, Renderer, blend_mode, fill_rule, paint};
|
||
|
||
mod bounds;
|
||
mod entries;
|
||
mod pixels;
|
||
mod predicates;
|
||
mod raster;
|
||
mod renderer;
|
||
|
||
#[cfg(test)]
|
||
pub(crate) use bounds::visual_bounds;
|
||
pub(crate) use bounds::{
|
||
brushed_cull_bounds, cull_bounds, drawn_outline, drawn_outlines, painted_brush_width, painted_cull_bounds, painted_reach, path_cull_bounds,
|
||
three_d_art_bounds, with_own_raster_reach,
|
||
};
|
||
#[cfg(test)]
|
||
pub(crate) use entries::Drawn;
|
||
pub(crate) use entries::{DocInputs, FxGeom, Inputs, Reads, ShadowEntry};
|
||
pub(crate) use pixels::paint_names;
|
||
pub(crate) use predicates::{has_fx, has_object_fx};
|
||
#[cfg(test)]
|
||
pub(crate) use raster::RASTERIZED;
|
||
#[cfg(test)]
|
||
use raster::{DRAG_PIXEL_BUDGET, drag_scale, filter_region, filter_region_within, pan_margin};
|
||
pub(crate) use raster::{PIXEL_BUDGET, PixelEntry, linear_key};
|
||
|
||
#[cfg(test)]
|
||
mod tests;
|