mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 15:18:12 +08:00
Read every paint through the cascaded appearance
The renderer, metadata collection, hit testing, and clip-path reduction all resolve a lane's paint as its own declared appearance or the nearest ancestor's through the cascade, taking the first fill and stroke coverages in one walk: the coverage's stroke parameters replace the element-resident stroke, the paint cells' interior lists feed the paint renderers, and the paint order reads off the coverage row order, which the stroke nodes now place from their paint order input. The paint overlay and its transmute dissolve into an inherited-appearance parameter on the vector-level bodies, with the Render trait untouched. Mask clones paint through a single black-fill appearance, and every row-building node stamps the appearance beside the legacy markers it still writes, so renders stay identical until the markers go. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1fff59f380
commit
46e0a4f2f5
@@ -8,7 +8,7 @@ pub use glue::{map_groups_to_owned, map_groups_to_persistent, map_groups_to_resi
|
|||||||
pub(crate) use legacy::run_to_legacy_list;
|
pub(crate) use legacy::run_to_legacy_list;
|
||||||
pub use legacy::{group_to_legacy_graphic, group_to_legacy_list, map_groups_to_legacy, map_paint_attrs_to_legacy, run_to_list};
|
pub use legacy::{group_to_legacy_graphic, group_to_legacy_list, map_groups_to_legacy, map_paint_attrs_to_legacy, run_to_list};
|
||||||
pub use paint::{
|
pub use paint::{
|
||||||
LanePaint, PaintColumns, PaintOverlay, PaintOverlayColumn, PaintReach, bake_paint_transforms, has_paint, is_paint_present, paint_graphics, set_paint_attribute, set_paint_attribute_at,
|
LanePaint, PaintColumns, PaintReach, bake_paint_transforms, has_paint, is_paint_present, paint_cell_rows, paint_graphics, set_paint_attribute, set_paint_attribute_at,
|
||||||
vector_can_reduce_to_clip_path,
|
vector_can_reduce_to_clip_path,
|
||||||
};
|
};
|
||||||
pub use walk::{GraphicLevel, GraphicLevelColumn, RowStep, VectorRow, direct_vector_len, flatten_vector_rows, group_is_empty, lane_attributes, run_lane_attributes, walk_vector_rows};
|
pub use walk::{GraphicLevel, GraphicLevelColumn, RowStep, VectorRow, direct_vector_len, flatten_vector_rows, group_is_empty, lane_attributes, run_lane_attributes, walk_vector_rows};
|
||||||
@@ -399,9 +399,9 @@ impl<'e> Graphic<'e> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_reduce_to_clip_path(&self) -> bool {
|
pub fn can_reduce_to_clip_path(&self, inherited_appearance: Option<&Appearance>) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Graphic::Vector(vector) => vector_can_reduce_to_clip_path(&core_types::lane::Single(vector)),
|
Graphic::Vector(vector) => vector_can_reduce_to_clip_path(&core_types::lane::Single(vector), inherited_appearance),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,15 +40,26 @@ where
|
|||||||
|
|
||||||
/// Whether every lane of a vector source draws as a plain clip path: fully
|
/// Whether every lane of a vector source draws as a plain clip path: fully
|
||||||
/// opaque, fill absent or opaque, stroke invisible or fully transparent.
|
/// opaque, fill absent or opaque, stroke invisible or fully transparent.
|
||||||
pub fn vector_can_reduce_to_clip_path<S: LaneSource<Element = Vector>>(source: &S) -> bool {
|
pub fn vector_can_reduce_to_clip_path<S: LaneSource<Element = Vector>>(source: &S, inherited_appearance: Option<&Appearance>) -> bool {
|
||||||
(0..source.lane_count()).all(|index| {
|
(0..source.lane_count()).all(|index| {
|
||||||
let Some(element) = source.element(index) else { return false };
|
if source.element(index).is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let opacity: f64 = source.attr::<Opacity>(index);
|
let opacity: f64 = source.attr::<Opacity>(index);
|
||||||
|
|
||||||
let fill_opaque_or_absent = paint_graphics::<Fill, _>(source, index).is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_opaque()));
|
let appearance = Appearance::cascade(source.attr::<AppearanceMarker>(index), inherited_appearance);
|
||||||
|
let resolved = appearance.map(Appearance::fill_and_stroke).unwrap_or_default();
|
||||||
|
|
||||||
let stroke_invisible_or_transparent = element.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
let fill_opaque_or_absent = resolved
|
||||||
|| paint_graphics::<Stroke, _>(source, index).is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_fully_transparent()));
|
.fill_paint
|
||||||
|
.and_then(paint_cell_rows)
|
||||||
|
.is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_opaque()));
|
||||||
|
|
||||||
|
let stroke_invisible_or_transparent = resolved.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
||||||
|
|| resolved
|
||||||
|
.stroke_paint
|
||||||
|
.and_then(paint_cell_rows)
|
||||||
|
.is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_fully_transparent()));
|
||||||
|
|
||||||
opacity > 1. - f64::EPSILON && fill_opaque_or_absent && stroke_invisible_or_transparent
|
opacity > 1. - f64::EPSILON && fill_opaque_or_absent && stroke_invisible_or_transparent
|
||||||
})
|
})
|
||||||
@@ -168,76 +179,13 @@ impl<'a> PaintReach<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A source with a lane's paint forced over its fill and stroke columns,
|
/// The paint a coverage row's cell holds, in the canonical `List<Graphic>` form the paint
|
||||||
/// reaching the interiors the legacy conversion's paint push reached.
|
/// renderers consume: this crate's writers carry the list as one graphic cell, and a bare
|
||||||
pub struct PaintOverlay<'a, S> {
|
/// cell of any other form is treated as paint that draws nothing.
|
||||||
inner: &'a S,
|
pub fn paint_cell_rows<'a>(cell: &'a Graphic<'static>) -> Option<&'a List<Graphic<'static>>> {
|
||||||
paint: LanePaint<'a>,
|
match cell {
|
||||||
}
|
Graphic::Graphic(list) => Some(list).filter(|list| is_paint_present(list)),
|
||||||
|
|
||||||
impl<'a, S> PaintOverlay<'a, S> {
|
|
||||||
pub fn new(inner: &'a S, paint: LanePaint<'a>) -> Self {
|
|
||||||
Self { inner, paint }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PaintOverlayColumn<'a, S: LaneSource + 'a, A: Attribute> {
|
|
||||||
inner: S::Column<'a, A>,
|
|
||||||
forced: Option<A::Value<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S: LaneSource, A: Attribute> LaneColumn<'a, A> for PaintOverlayColumn<'a, S, A> {
|
|
||||||
fn try_get(&self, lane: usize) -> Option<A::Value<'a>> {
|
|
||||||
match self.forced {
|
|
||||||
Some(forced) => Some(forced),
|
|
||||||
None => self.inner.try_get(lane),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The forced value for the marker `A`: the lane paint where `A` is this
|
|
||||||
/// crate's fill or stroke marker, absent otherwise.
|
|
||||||
fn forced_paint<'a, A: Attribute>(paint: LanePaint<'a>) -> Option<A::Value<'a>> {
|
|
||||||
let slot = match A::NAME {
|
|
||||||
name if name == Fill::NAME => paint.fill,
|
|
||||||
name if name == Stroke::NAME => paint.stroke,
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}?;
|
|
||||||
assert_eq!(
|
|
||||||
std::any::TypeId::of::<A::Value<'static>>(),
|
|
||||||
std::any::TypeId::of::<Option<&'static List<Graphic<'static>>>>(),
|
|
||||||
"attribute `{}` is declared at another value type than this crate's paint form",
|
|
||||||
A::NAME
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
size_of::<A::Value<'a>>(),
|
|
||||||
size_of::<Option<&'a List<Graphic<'a>>>>(),
|
|
||||||
"the paint value form must span the marker's value"
|
|
||||||
);
|
|
||||||
// SAFETY: the census admits one value type per attribute name, so a `fill` or `stroke` marker carries this crate's `Option<&List<Graphic>>` at the asserted size.
|
|
||||||
Some(unsafe { std::mem::transmute_copy::<Option<&'a List<Graphic>>, A::Value<'a>>(&Some(slot)) })
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S: LaneSource> LaneSource for PaintOverlay<'a, S> {
|
|
||||||
type Element = S::Element;
|
|
||||||
type Column<'b, A: Attribute>
|
|
||||||
= PaintOverlayColumn<'b, S, A>
|
|
||||||
where
|
|
||||||
Self: 'b;
|
|
||||||
|
|
||||||
fn lane_count(&self) -> usize {
|
|
||||||
self.inner.lane_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn element(&self, lane: usize) -> Option<&S::Element> {
|
|
||||||
self.inner.element(lane)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn column<A: Attribute>(&self) -> PaintOverlayColumn<'_, S, A> {
|
|
||||||
PaintOverlayColumn {
|
|
||||||
inner: self.inner.column::<A>(),
|
|
||||||
forced: forced_paint::<A>(self.paint),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ use dyn_any::DynAny;
|
|||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
use graphene_hash::CacheHashWrapper;
|
use graphene_hash::CacheHashWrapper;
|
||||||
use graphene_resource::Resource;
|
use graphene_resource::Resource;
|
||||||
use graphic_types::graphic::{PaintColumns, PaintOverlay, PaintReach, has_paint, is_paint_present, paint_graphics, set_paint_attribute, vector_can_reduce_to_clip_path};
|
use graphic_types::appearance::{Appearance, Coverage};
|
||||||
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
|
use graphic_types::graphic::{PaintColumns, PaintReach, is_paint_present, paint_cell_rows, set_paint_attribute, vector_can_reduce_to_clip_path};
|
||||||
|
use graphic_types::markers::{Appearance as AppearanceMarker, EditorMergedLayers};
|
||||||
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster, Texture};
|
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster, Texture};
|
||||||
use graphic_types::vector_types::gradient::{GradientStops, GradientType};
|
use graphic_types::vector_types::gradient::{GradientStops, GradientType};
|
||||||
use graphic_types::vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod};
|
use graphic_types::vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod};
|
||||||
@@ -207,6 +208,11 @@ pub struct RenderContext {
|
|||||||
pub resource_overrides: Vec<(peniko::ImageBrush, Texture)>,
|
pub resource_overrides: Vec<(peniko::ImageBrush, Texture)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The single black-fill appearance a mask clone paints with, at full alpha so the mask fully covers the interior.
|
||||||
|
fn black_fill_appearance() -> Appearance {
|
||||||
|
Appearance::new_single(Coverage::new_fill(), Graphic::Graphic(List::new_from_element(Graphic::Color(Color::BLACK))))
|
||||||
|
}
|
||||||
|
|
||||||
/// The alpha multiplier a paint row's opacity attributes apply when it serves as a paint.
|
/// The alpha multiplier a paint row's opacity attributes apply when it serves as a paint.
|
||||||
/// Fill opacity fades a paint just as opacity does, but a masker drops it so it cannot reach the content clipped to it.
|
/// Fill opacity fades a paint just as opacity does, but a masker drops it so it cannot reach the content clipped to it.
|
||||||
pub(crate) fn paint_row_opacity<T>(list: &List<T>, index: usize, for_mask: bool) -> f32 {
|
pub(crate) fn paint_row_opacity<T>(list: &List<T>, index: usize, for_mask: bool) -> f32 {
|
||||||
@@ -624,7 +630,7 @@ impl Render for Graphic<'_> {
|
|||||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
match self {
|
match self {
|
||||||
Graphic::Graphic(list) => list.render_svg(render, render_params),
|
Graphic::Graphic(list) => list.render_svg(render, render_params),
|
||||||
Graphic::Vector(vector) => render_vector_svg(&Single(vector), render, render_params),
|
Graphic::Vector(vector) => render_vector_svg(&Single(vector), None, render, render_params),
|
||||||
Graphic::RasterCPU(raster) => render_raster_cpu_svg(&Single(raster), render, render_params),
|
Graphic::RasterCPU(raster) => render_raster_cpu_svg(&Single(raster), render, render_params),
|
||||||
Graphic::RasterGPU(_) => (),
|
Graphic::RasterGPU(_) => (),
|
||||||
Graphic::Color(color) => render_color_svg(&Single(color), render, render_params),
|
Graphic::Color(color) => render_color_svg(&Single(color), render, render_params),
|
||||||
@@ -637,7 +643,7 @@ impl Render for Graphic<'_> {
|
|||||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
match self {
|
match self {
|
||||||
Graphic::Graphic(list) => list.render_to_vello(scene, transform, context, render_params),
|
Graphic::Graphic(list) => list.render_to_vello(scene, transform, context, render_params),
|
||||||
Graphic::Vector(vector) => render_vector_vello(&Single(vector), scene, transform, context, render_params),
|
Graphic::Vector(vector) => render_vector_vello(&Single(vector), None, scene, transform, context, render_params),
|
||||||
Graphic::RasterCPU(raster) => render_raster_cpu_vello(&Single(raster), scene, transform, render_params),
|
Graphic::RasterCPU(raster) => render_raster_cpu_vello(&Single(raster), scene, transform, render_params),
|
||||||
Graphic::RasterGPU(raster) => render_raster_gpu_vello(&Single(raster), scene, transform, context, render_params),
|
Graphic::RasterGPU(raster) => render_raster_gpu_vello(&Single(raster), scene, transform, context, render_params),
|
||||||
Graphic::Color(color) => render_color_vello(&Single(color), scene, render_params),
|
Graphic::Color(color) => render_color_vello(&Single(color), scene, render_params),
|
||||||
@@ -677,7 +683,7 @@ impl Render for Graphic<'_> {
|
|||||||
|
|
||||||
fn render_element_svg<'a>(element: &'a Graphic, reach: PaintReach<'a>, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_element_svg<'a>(element: &'a Graphic, reach: PaintReach<'a>, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
match element {
|
match element {
|
||||||
Graphic::Vector(vector) if reach.applies() => render_vector_svg(&PaintOverlay::new(&Single(vector), reach.paint), render, render_params),
|
Graphic::Vector(vector) => render_vector_svg(&Single(vector), reach.appearance, render, render_params),
|
||||||
Graphic::Graphic(inner) => render_graphic_svg_with(inner, reach.nested(), render, render_params),
|
Graphic::Graphic(inner) => render_graphic_svg_with(inner, reach.nested(), render, render_params),
|
||||||
Graphic::Group(group) => render_group_svg(group, reach, render, render_params),
|
Graphic::Group(group) => render_group_svg(group, reach, render, render_params),
|
||||||
_ => element.render_svg(render, render_params),
|
_ => element.render_svg(render, render_params),
|
||||||
@@ -686,7 +692,7 @@ fn render_element_svg<'a>(element: &'a Graphic, reach: PaintReach<'a>, render: &
|
|||||||
|
|
||||||
fn render_element_vello<'a>(element: &'a Graphic, reach: PaintReach<'a>, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_element_vello<'a>(element: &'a Graphic, reach: PaintReach<'a>, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
match element {
|
match element {
|
||||||
Graphic::Vector(vector) if reach.applies() => render_vector_vello(&PaintOverlay::new(&Single(vector), reach.paint), scene, transform, context, render_params),
|
Graphic::Vector(vector) => render_vector_vello(&Single(vector), reach.appearance, scene, transform, context, render_params),
|
||||||
Graphic::Graphic(inner) => render_graphic_vello_with(inner, reach.nested(), scene, transform, context, render_params),
|
Graphic::Graphic(inner) => render_graphic_vello_with(inner, reach.nested(), scene, transform, context, render_params),
|
||||||
Graphic::Group(group) => render_group_vello(group, reach, scene, transform, context, render_params),
|
Graphic::Group(group) => render_group_vello(group, reach, scene, transform, context, render_params),
|
||||||
_ => element.render_to_vello(scene, transform, context, render_params),
|
_ => element.render_to_vello(scene, transform, context, render_params),
|
||||||
@@ -695,13 +701,12 @@ fn render_element_vello<'a>(element: &'a Graphic, reach: PaintReach<'a>, scene:
|
|||||||
|
|
||||||
fn element_can_reduce_to_clip_path<'a>(element: &'a Graphic, reach: PaintReach<'a>) -> bool {
|
fn element_can_reduce_to_clip_path<'a>(element: &'a Graphic, reach: PaintReach<'a>) -> bool {
|
||||||
match element {
|
match element {
|
||||||
Graphic::Vector(vector) if reach.applies() => vector_can_reduce_to_clip_path(&PaintOverlay::new(&Single(vector), reach.paint)),
|
Graphic::Vector(vector) => vector_can_reduce_to_clip_path(&Single(vector), reach.appearance),
|
||||||
Graphic::Group(group) => match RunView::<Vector>::new(&group.content) {
|
Graphic::Group(group) => match RunView::<Vector>::new(&group.content) {
|
||||||
Some(run) if reach.applies() => vector_can_reduce_to_clip_path(&PaintOverlay::new(&run, reach.paint)),
|
Some(run) => vector_can_reduce_to_clip_path(&run, reach.appearance),
|
||||||
Some(run) => vector_can_reduce_to_clip_path(&run),
|
|
||||||
None => false,
|
None => false,
|
||||||
},
|
},
|
||||||
_ => element.can_reduce_to_clip_path(),
|
_ => element.can_reduce_to_clip_path(reach.appearance),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,8 +737,7 @@ fn collect_element_metadata<'a>(
|
|||||||
|
|
||||||
match element {
|
match element {
|
||||||
Graphic::Graphic(list) => collect_graphic_metadata_with(list, reach.nested(), metadata, footprint, element_id),
|
Graphic::Graphic(list) => collect_graphic_metadata_with(list, reach.nested(), metadata, footprint, element_id),
|
||||||
Graphic::Vector(vector) if reach.applies() => collect_vector_metadata(&PaintOverlay::new(&Single(vector), reach.paint), metadata, footprint, element_id),
|
Graphic::Vector(vector) => collect_vector_metadata(&Single(vector), reach.appearance, metadata, footprint, element_id),
|
||||||
Graphic::Vector(vector) => collect_vector_metadata(&Single(vector), metadata, footprint, element_id),
|
|
||||||
Graphic::RasterCPU(raster) => collect_raster_metadata(&Single(raster), metadata, footprint, element_id),
|
Graphic::RasterCPU(raster) => collect_raster_metadata(&Single(raster), metadata, footprint, element_id),
|
||||||
Graphic::RasterGPU(raster) => collect_raster_metadata(&Single(raster), metadata, footprint, element_id),
|
Graphic::RasterGPU(raster) => collect_raster_metadata(&Single(raster), metadata, footprint, element_id),
|
||||||
Graphic::Color(_) => {}
|
Graphic::Color(_) => {}
|
||||||
@@ -775,8 +779,7 @@ fn collect_group_row_metadata(group: &Group, metadata: &mut RenderMetadata, elem
|
|||||||
fn add_element_upstream_click_targets<'a>(element: &'a Graphic, reach: PaintReach<'a>, click_targets: &mut Vec<ClickTarget>) {
|
fn add_element_upstream_click_targets<'a>(element: &'a Graphic, reach: PaintReach<'a>, click_targets: &mut Vec<ClickTarget>) {
|
||||||
match element {
|
match element {
|
||||||
Graphic::Graphic(list) => add_graphic_upstream_click_targets_with(list, reach.nested(), click_targets),
|
Graphic::Graphic(list) => add_graphic_upstream_click_targets_with(list, reach.nested(), click_targets),
|
||||||
Graphic::Vector(vector) if reach.applies() => add_vector_upstream_click_targets(&PaintOverlay::new(&Single(vector), reach.paint), click_targets),
|
Graphic::Vector(vector) => add_vector_upstream_click_targets(&Single(vector), reach.appearance, click_targets),
|
||||||
Graphic::Vector(vector) => add_vector_upstream_click_targets(&Single(vector), click_targets),
|
|
||||||
Graphic::RasterCPU(_) | Graphic::RasterGPU(_) => add_raster_upstream_click_targets(click_targets),
|
Graphic::RasterCPU(_) | Graphic::RasterGPU(_) => add_raster_upstream_click_targets(click_targets),
|
||||||
Graphic::Color(_) | Graphic::Gradient(_) => {}
|
Graphic::Color(_) | Graphic::Gradient(_) => {}
|
||||||
Graphic::Text(text) => add_text_upstream_click_targets(&Single(text), click_targets),
|
Graphic::Text(text) => add_text_upstream_click_targets(&Single(text), click_targets),
|
||||||
@@ -787,8 +790,7 @@ fn add_element_upstream_click_targets<'a>(element: &'a Graphic, reach: PaintReac
|
|||||||
fn add_element_upstream_outline_targets<'a>(element: &'a Graphic, reach: PaintReach<'a>, outlines: &mut Vec<ClickTarget>) {
|
fn add_element_upstream_outline_targets<'a>(element: &'a Graphic, reach: PaintReach<'a>, outlines: &mut Vec<ClickTarget>) {
|
||||||
match element {
|
match element {
|
||||||
Graphic::Graphic(list) => add_graphic_upstream_outline_targets_with(list, reach.nested(), outlines),
|
Graphic::Graphic(list) => add_graphic_upstream_outline_targets_with(list, reach.nested(), outlines),
|
||||||
Graphic::Vector(vector) if reach.applies() => add_vector_upstream_outline_targets(&PaintOverlay::new(&Single(vector), reach.paint), outlines),
|
Graphic::Vector(vector) => add_vector_upstream_outline_targets(&Single(vector), reach.appearance, outlines),
|
||||||
Graphic::Vector(vector) => add_vector_upstream_outline_targets(&Single(vector), outlines),
|
|
||||||
Graphic::RasterCPU(_) | Graphic::RasterGPU(_) => add_raster_upstream_click_targets(outlines),
|
Graphic::RasterCPU(_) | Graphic::RasterGPU(_) => add_raster_upstream_click_targets(outlines),
|
||||||
Graphic::Color(_) | Graphic::Gradient(_) => {}
|
Graphic::Color(_) | Graphic::Gradient(_) => {}
|
||||||
Graphic::Text(text) => add_text_upstream_click_targets(&Single(text), outlines),
|
Graphic::Text(text) => add_text_upstream_click_targets(&Single(text), outlines),
|
||||||
@@ -803,10 +805,7 @@ fn render_group_svg<'a>(group: &'a Group, reach: PaintReach<'a>, render: &mut Sv
|
|||||||
if let Some(run) = RunView::<Graphic>::new(item) {
|
if let Some(run) = RunView::<Graphic>::new(item) {
|
||||||
render_graphic_svg_with(&run, reach.into_group_graphics(), render, render_params)
|
render_graphic_svg_with(&run, reach.into_group_graphics(), render, render_params)
|
||||||
} else if let Some(run) = RunView::<Vector>::new(item) {
|
} else if let Some(run) = RunView::<Vector>::new(item) {
|
||||||
match reach.applies() {
|
render_vector_svg(&run, reach.appearance, render, render_params)
|
||||||
true => render_vector_svg(&PaintOverlay::new(&run, reach.paint), render, render_params),
|
|
||||||
false => render_vector_svg(&run, render, render_params),
|
|
||||||
}
|
|
||||||
} else if let Some(run) = RunView::<Raster<CPU>>::new(item) {
|
} else if let Some(run) = RunView::<Raster<CPU>>::new(item) {
|
||||||
render_raster_cpu_svg(&run, render, render_params)
|
render_raster_cpu_svg(&run, render, render_params)
|
||||||
} else if item.typed_lanes::<Raster<GPU>>().is_some() {
|
} else if item.typed_lanes::<Raster<GPU>>().is_some() {
|
||||||
@@ -824,10 +823,7 @@ fn render_group_vello<'a>(group: &'a Group, reach: PaintReach<'a>, scene: &mut S
|
|||||||
if let Some(run) = RunView::<Graphic>::new(item) {
|
if let Some(run) = RunView::<Graphic>::new(item) {
|
||||||
render_graphic_vello_with(&run, reach.into_group_graphics(), scene, transform, context, render_params)
|
render_graphic_vello_with(&run, reach.into_group_graphics(), scene, transform, context, render_params)
|
||||||
} else if let Some(run) = RunView::<Vector>::new(item) {
|
} else if let Some(run) = RunView::<Vector>::new(item) {
|
||||||
match reach.applies() {
|
render_vector_vello(&run, reach.appearance, scene, transform, context, render_params)
|
||||||
true => render_vector_vello(&PaintOverlay::new(&run, reach.paint), scene, transform, context, render_params),
|
|
||||||
false => render_vector_vello(&run, scene, transform, context, render_params),
|
|
||||||
}
|
|
||||||
} else if let Some(run) = RunView::<Raster<CPU>>::new(item) {
|
} else if let Some(run) = RunView::<Raster<CPU>>::new(item) {
|
||||||
render_raster_cpu_vello(&run, scene, transform, render_params)
|
render_raster_cpu_vello(&run, scene, transform, render_params)
|
||||||
} else if let Some(run) = RunView::<Raster<GPU>>::new(item) {
|
} else if let Some(run) = RunView::<Raster<GPU>>::new(item) {
|
||||||
@@ -849,10 +845,7 @@ fn collect_group_metadata<'a>(group: &'a Group, reach: PaintReach<'a>, metadata:
|
|||||||
if let Some(run) = RunView::<Graphic>::new(item) {
|
if let Some(run) = RunView::<Graphic>::new(item) {
|
||||||
collect_graphic_metadata_with(&run, reach.into_group_graphics(), metadata, footprint, element_id)
|
collect_graphic_metadata_with(&run, reach.into_group_graphics(), metadata, footprint, element_id)
|
||||||
} else if let Some(run) = RunView::<Vector>::new(item) {
|
} else if let Some(run) = RunView::<Vector>::new(item) {
|
||||||
match reach.applies() {
|
collect_vector_metadata(&run, reach.appearance, metadata, footprint, element_id)
|
||||||
true => collect_vector_metadata(&PaintOverlay::new(&run, reach.paint), metadata, footprint, element_id),
|
|
||||||
false => collect_vector_metadata(&run, metadata, footprint, element_id),
|
|
||||||
}
|
|
||||||
} else if let Some(run) = RunView::<Raster<CPU>>::new(item) {
|
} else if let Some(run) = RunView::<Raster<CPU>>::new(item) {
|
||||||
collect_raster_metadata(&run, metadata, footprint, element_id)
|
collect_raster_metadata(&run, metadata, footprint, element_id)
|
||||||
} else if let Some(run) = RunView::<Raster<GPU>>::new(item) {
|
} else if let Some(run) = RunView::<Raster<GPU>>::new(item) {
|
||||||
@@ -868,10 +861,7 @@ fn add_group_upstream_click_targets<'a>(group: &'a Group, reach: PaintReach<'a>,
|
|||||||
if let Some(run) = RunView::<Graphic>::new(item) {
|
if let Some(run) = RunView::<Graphic>::new(item) {
|
||||||
add_graphic_upstream_click_targets_with(&run, reach.into_group_graphics(), click_targets)
|
add_graphic_upstream_click_targets_with(&run, reach.into_group_graphics(), click_targets)
|
||||||
} else if let Some(run) = RunView::<Vector>::new(item) {
|
} else if let Some(run) = RunView::<Vector>::new(item) {
|
||||||
match reach.applies() {
|
add_vector_upstream_click_targets(&run, reach.appearance, click_targets)
|
||||||
true => add_vector_upstream_click_targets(&PaintOverlay::new(&run, reach.paint), click_targets),
|
|
||||||
false => add_vector_upstream_click_targets(&run, click_targets),
|
|
||||||
}
|
|
||||||
} else if item.typed_lanes::<Raster<CPU>>().is_some() || item.typed_lanes::<Raster<GPU>>().is_some() {
|
} else if item.typed_lanes::<Raster<CPU>>().is_some() || item.typed_lanes::<Raster<GPU>>().is_some() {
|
||||||
add_raster_upstream_click_targets(click_targets)
|
add_raster_upstream_click_targets(click_targets)
|
||||||
} else if let Some(run) = RunView::<String>::new(item) {
|
} else if let Some(run) = RunView::<String>::new(item) {
|
||||||
@@ -884,10 +874,7 @@ fn add_group_upstream_outline_targets<'a>(group: &'a Group, reach: PaintReach<'a
|
|||||||
if let Some(run) = RunView::<Graphic>::new(item) {
|
if let Some(run) = RunView::<Graphic>::new(item) {
|
||||||
add_graphic_upstream_outline_targets_with(&run, reach.into_group_graphics(), outlines)
|
add_graphic_upstream_outline_targets_with(&run, reach.into_group_graphics(), outlines)
|
||||||
} else if let Some(run) = RunView::<Vector>::new(item) {
|
} else if let Some(run) = RunView::<Vector>::new(item) {
|
||||||
match reach.applies() {
|
add_vector_upstream_outline_targets(&run, reach.appearance, outlines)
|
||||||
true => add_vector_upstream_outline_targets(&PaintOverlay::new(&run, reach.paint), outlines),
|
|
||||||
false => add_vector_upstream_outline_targets(&run, outlines),
|
|
||||||
}
|
|
||||||
} else if item.typed_lanes::<Raster<CPU>>().is_some() || item.typed_lanes::<Raster<GPU>>().is_some() {
|
} else if item.typed_lanes::<Raster<CPU>>().is_some() || item.typed_lanes::<Raster<GPU>>().is_some() {
|
||||||
add_raster_upstream_click_targets(outlines)
|
add_raster_upstream_click_targets(outlines)
|
||||||
} else if let Some(run) = RunView::<String>::new(item) {
|
} else if let Some(run) = RunView::<String>::new(item) {
|
||||||
@@ -1343,7 +1330,7 @@ impl Render for List<Graphic<'_>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, inherited_appearance: Option<&Appearance>, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
for index in 0..source.lane_count() {
|
for index in 0..source.lane_count() {
|
||||||
let Some(vector) = source.element(index) else { continue };
|
let Some(vector) = source.element(index) else { continue };
|
||||||
let item_transform: DAffine2 = source.attr::<Transform>(index);
|
let item_transform: DAffine2 = source.attr::<Transform>(index);
|
||||||
@@ -1351,8 +1338,17 @@ fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut S
|
|||||||
let opacity_attr: f64 = source.attr::<Opacity>(index);
|
let opacity_attr: f64 = source.attr::<Opacity>(index);
|
||||||
let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index);
|
let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index);
|
||||||
|
|
||||||
|
// The lane's paint: its own declared appearance, or the nearest ancestor's through the cascade
|
||||||
|
let appearance = Appearance::cascade(source.attr::<AppearanceMarker>(index), inherited_appearance);
|
||||||
|
let mut resolved = appearance.map(Appearance::fill_and_stroke).unwrap_or_default();
|
||||||
|
// The paint order rides the coverage list's row order
|
||||||
|
if let Some(stroke) = &mut resolved.stroke {
|
||||||
|
stroke.paint_order = if resolved.stroke_below { PaintOrder::StrokeBelow } else { PaintOrder::StrokeAbove };
|
||||||
|
}
|
||||||
|
let element_stroke = resolved.stroke.as_ref();
|
||||||
|
|
||||||
// Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
|
// Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
|
||||||
let has_real_stroke = vector.stroke.as_ref().filter(|stroke| stroke.weight() > 0.);
|
let has_real_stroke = element_stroke.filter(|stroke| stroke.weight() > 0.);
|
||||||
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform_is_invertible(*transform));
|
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform_is_invertible(*transform));
|
||||||
let applied_stroke_transform = set_stroke_transform.unwrap_or(item_transform);
|
let applied_stroke_transform = set_stroke_transform.unwrap_or(item_transform);
|
||||||
let applied_stroke_transform = render_params.alignment_parent_transform.unwrap_or(applied_stroke_transform);
|
let applied_stroke_transform = render_params.alignment_parent_transform.unwrap_or(applied_stroke_transform);
|
||||||
@@ -1372,26 +1368,26 @@ fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut S
|
|||||||
path.push_str(bezpath.to_svg().as_str());
|
path.push_str(bezpath.to_svg().as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mask_type = if vector.stroke.as_ref().map(|x| x.align) == Some(StrokeAlign::Inside) {
|
let mask_type = if element_stroke.map(|x| x.align) == Some(StrokeAlign::Inside) {
|
||||||
MaskType::Clip
|
MaskType::Clip
|
||||||
} else {
|
} else {
|
||||||
MaskType::Mask
|
MaskType::Mask
|
||||||
};
|
};
|
||||||
|
|
||||||
let fill_graphic_list = paint_graphics::<Fill, _>(source, index);
|
let fill_graphic_list = resolved.fill_paint.and_then(paint_cell_rows);
|
||||||
let fill_graphic = fill_graphic_list.and_then(|l| l.element(0));
|
let fill_graphic = fill_graphic_list.and_then(|l| l.element(0));
|
||||||
|
|
||||||
let stroke_graphic_list = paint_graphics::<Stroke, _>(source, index);
|
let stroke_graphic_list = resolved.stroke_paint.and_then(paint_cell_rows);
|
||||||
let stroke_graphic = stroke_graphic_list.and_then(|l| l.element(0));
|
let stroke_graphic = stroke_graphic_list.and_then(|l| l.element(0));
|
||||||
|
|
||||||
let path_is_closed = vector.stroke_bezier_paths().all(|path| path.closed());
|
let path_is_closed = vector.stroke_bezier_paths().all(|path| path.closed());
|
||||||
let can_draw_aligned_stroke = path_is_closed
|
let can_draw_aligned_stroke = path_is_closed
|
||||||
&& vector.stroke.as_ref().is_some_and(|stroke| stroke.has_renderable_stroke() && stroke.align.is_not_centered())
|
&& element_stroke.is_some_and(|stroke| stroke.has_renderable_stroke() && stroke.align.is_not_centered())
|
||||||
&& stroke_graphic.is_some_and(|graphic| !graphic.is_fully_transparent());
|
&& stroke_graphic.is_some_and(|graphic| !graphic.is_fully_transparent());
|
||||||
let can_use_paint_order = !(fill_graphic.is_none_or(|graphic| !graphic.covers_opaquely()) || mask_type == MaskType::Clip);
|
let can_use_paint_order = !(fill_graphic.is_none_or(|graphic| !graphic.covers_opaquely()) || mask_type == MaskType::Clip);
|
||||||
|
|
||||||
let needs_separate_alignment_fill = can_draw_aligned_stroke && !can_use_paint_order;
|
let needs_separate_alignment_fill = can_draw_aligned_stroke && !can_use_paint_order;
|
||||||
let wants_stroke_below = vector.stroke.as_ref().map(|s| s.paint_order) == Some(PaintOrder::StrokeBelow);
|
let wants_stroke_below = element_stroke.map(|s| s.paint_order) == Some(PaintOrder::StrokeBelow);
|
||||||
let override_paint_order = can_draw_aligned_stroke && can_use_paint_order;
|
let override_paint_order = can_draw_aligned_stroke && can_use_paint_order;
|
||||||
let use_face_fill = vector.use_face_fill();
|
let use_face_fill = vector.use_face_fill();
|
||||||
|
|
||||||
@@ -1418,6 +1414,7 @@ fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut S
|
|||||||
// The wrapping SVG group (above) handles the user-set opacity.
|
// The wrapping SVG group (above) handles the user-set opacity.
|
||||||
let mut mask_item = Item::new_from_element(cloned_vector).with_attribute(ATTR_TRANSFORM, item_transform);
|
let mut mask_item = Item::new_from_element(cloned_vector).with_attribute(ATTR_TRANSFORM, item_transform);
|
||||||
set_paint_attribute(mask_item.attributes_mut(), ATTR_FILL, List::new_from_element(Color::BLACK));
|
set_paint_attribute(mask_item.attributes_mut(), ATTR_FILL, List::new_from_element(Color::BLACK));
|
||||||
|
mask_item.set_attribute(graphic_types::ATTR_APPEARANCE, black_fill_appearance());
|
||||||
let vector_item = List::new_from_item(mask_item);
|
let vector_item = List::new_from_item(mask_item);
|
||||||
|
|
||||||
(id, mask_type, vector_item)
|
(id, mask_type, vector_item)
|
||||||
@@ -1452,7 +1449,7 @@ fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut S
|
|||||||
if let Some((ref id, mask_type, ref vector_item)) = push_id {
|
if let Some((ref id, mask_type, ref vector_item)) = push_id {
|
||||||
let mut svg = SvgRender::new();
|
let mut svg = SvgRender::new();
|
||||||
vector_item.render_svg(&mut svg, &render_params.for_alignment(applied_stroke_transform));
|
vector_item.render_svg(&mut svg, &render_params.for_alignment(applied_stroke_transform));
|
||||||
let stroke = vector.stroke.as_ref().unwrap();
|
let stroke = element_stroke.unwrap();
|
||||||
// `push_id` is only `Some` when `can_draw_aligned_stroke`, which is gated on `path_is_closed`
|
// `push_id` is only `Some` when `can_draw_aligned_stroke`, which is gated on `path_is_closed`
|
||||||
let (largest_scale, _) = singular_values(applied_stroke_transform);
|
let (largest_scale, _) = singular_values(applied_stroke_transform);
|
||||||
let inflation = stroke.max_aabb_inflation(true) * largest_scale;
|
let inflation = stroke.max_aabb_inflation(true) * largest_scale;
|
||||||
@@ -1479,9 +1476,7 @@ fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut S
|
|||||||
render_params.aligned_strokes = can_draw_aligned_stroke;
|
render_params.aligned_strokes = can_draw_aligned_stroke;
|
||||||
render_params.override_paint_order = override_paint_order;
|
render_params.override_paint_order = override_paint_order;
|
||||||
|
|
||||||
let stroke_shape_attribute = vector
|
let stroke_shape_attribute = element_stroke
|
||||||
.stroke
|
|
||||||
.as_ref()
|
|
||||||
.map(|stroke| {
|
.map(|stroke| {
|
||||||
if stroke_graphic_list.is_some_and(is_paint_present) {
|
if stroke_graphic_list.is_some_and(is_paint_present) {
|
||||||
stroke.render(defs, item_transform, element_transform, applied_stroke_transform, bounds_matrix, &render_params, PaintTarget::Stroke)
|
stroke.render(defs, item_transform, element_transform, applied_stroke_transform, bounds_matrix, &render_params, PaintTarget::Stroke)
|
||||||
@@ -1492,7 +1487,7 @@ fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut S
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Need to avoid generating only paint attribute, otherwise SVG uses 1px width stroke as a fallback
|
// Need to avoid generating only paint attribute, otherwise SVG uses 1px width stroke as a fallback
|
||||||
let stroke_visible = vector.stroke.as_ref().is_some_and(|stroke| stroke.has_renderable_stroke()) && stroke_graphic.is_some_and(|g| !g.is_fully_transparent());
|
let stroke_visible = element_stroke.is_some_and(|stroke| stroke.has_renderable_stroke()) && stroke_graphic.is_some_and(|g| !g.is_fully_transparent());
|
||||||
let stroke_attribute = if stroke_visible {
|
let stroke_attribute = if stroke_visible {
|
||||||
stroke_graphic_list
|
stroke_graphic_list
|
||||||
.map(|list| {
|
.map(|list| {
|
||||||
@@ -1555,7 +1550,14 @@ fn render_vector_svg<S: LaneSource<Element = Vector>>(source: &S, render: &mut S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_vector_vello<S: LaneSource<Element = Vector>>(source: &S, scene: &mut Scene, parent_transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_vector_vello<S: LaneSource<Element = Vector>>(
|
||||||
|
source: &S,
|
||||||
|
inherited_appearance: Option<&Appearance>,
|
||||||
|
scene: &mut Scene,
|
||||||
|
parent_transform: DAffine2,
|
||||||
|
context: &mut RenderContext,
|
||||||
|
render_params: &RenderParams,
|
||||||
|
) {
|
||||||
for index in 0..source.lane_count() {
|
for index in 0..source.lane_count() {
|
||||||
use graphic_types::vector_types::vector;
|
use graphic_types::vector_types::vector;
|
||||||
|
|
||||||
@@ -1565,7 +1567,20 @@ fn render_vector_vello<S: LaneSource<Element = Vector>>(source: &S, scene: &mut
|
|||||||
let opacity_attr: f64 = source.attr::<Opacity>(index);
|
let opacity_attr: f64 = source.attr::<Opacity>(index);
|
||||||
let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index);
|
let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index);
|
||||||
let multiplied_transform = parent_transform * item_transform;
|
let multiplied_transform = parent_transform * item_transform;
|
||||||
let has_real_stroke = element.stroke.as_ref().filter(|stroke| stroke.weight() > 0.);
|
|
||||||
|
// The lane's paint: its own declared appearance, or the nearest ancestor's through the cascade
|
||||||
|
let appearance = Appearance::cascade(source.attr::<AppearanceMarker>(index), inherited_appearance);
|
||||||
|
let mut resolved = appearance.map(Appearance::fill_and_stroke).unwrap_or_default();
|
||||||
|
// The paint order rides the coverage list's row order
|
||||||
|
if let Some(stroke) = &mut resolved.stroke {
|
||||||
|
stroke.paint_order = if resolved.stroke_below {
|
||||||
|
vector::style::PaintOrder::StrokeBelow
|
||||||
|
} else {
|
||||||
|
vector::style::PaintOrder::StrokeAbove
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_real_stroke = resolved.stroke.as_ref().filter(|stroke| stroke.weight() > 0.);
|
||||||
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform_is_invertible(*transform));
|
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform_is_invertible(*transform));
|
||||||
let mut applied_stroke_transform = set_stroke_transform.unwrap_or(multiplied_transform);
|
let mut applied_stroke_transform = set_stroke_transform.unwrap_or(multiplied_transform);
|
||||||
let mut element_transform = set_stroke_transform
|
let mut element_transform = set_stroke_transform
|
||||||
@@ -1589,8 +1604,8 @@ fn render_vector_vello<S: LaneSource<Element = Vector>>(source: &S, scene: &mut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fill_graphic_list = paint_graphics::<Fill, _>(source, index);
|
let fill_graphic_list = resolved.fill_paint.and_then(paint_cell_rows);
|
||||||
let stroke_graphic_list = paint_graphics::<Stroke, _>(source, index);
|
let stroke_graphic_list = resolved.stroke_paint.and_then(paint_cell_rows);
|
||||||
|
|
||||||
// If we're using opacity or a blend mode, we need to push a layer
|
// If we're using opacity or a blend mode, we need to push a layer
|
||||||
let blend_mode = match render_params.render_mode {
|
let blend_mode = match render_params.render_mode {
|
||||||
@@ -1602,7 +1617,7 @@ fn render_vector_vello<S: LaneSource<Element = Vector>>(source: &S, scene: &mut
|
|||||||
// Whether the renderer will engage the stroke-alignment compositing trick (non-Center align on a fully closed path).
|
// Whether the renderer will engage the stroke-alignment compositing trick (non-Center align on a fully closed path).
|
||||||
// Used by both the blend-layer clip rect inflation below (as `max_aabb_inflation`'s `path_is_closed` arg, equivalent here since
|
// Used by both the blend-layer clip rect inflation below (as `max_aabb_inflation`'s `path_is_closed` arg, equivalent here since
|
||||||
// the function ignores the arg for Center align) and the `SrcIn`/`SrcOut` aligned-stroke branch further down.
|
// the function ignores the arg for Center align) and the `SrcIn`/`SrcOut` aligned-stroke branch further down.
|
||||||
let stroke = element.stroke.as_ref();
|
let stroke = resolved.stroke.as_ref();
|
||||||
let stroke_fully_transparent = stroke_graphic_list.is_none_or(|l| l.element(0).is_none_or(|g| g.is_fully_transparent()));
|
let stroke_fully_transparent = stroke_graphic_list.is_none_or(|l| l.element(0).is_none_or(|g| g.is_fully_transparent()));
|
||||||
let can_draw_aligned_stroke = !stroke_fully_transparent && stroke.is_some_and(|s| s.has_renderable_stroke() && s.align.is_not_centered()) && element.stroke_bezier_paths().all(|p| p.closed());
|
let can_draw_aligned_stroke = !stroke_fully_transparent && stroke.is_some_and(|s| s.has_renderable_stroke() && s.align.is_not_centered()) && element.stroke_bezier_paths().all(|p| p.closed());
|
||||||
|
|
||||||
@@ -1764,6 +1779,7 @@ fn render_vector_vello<S: LaneSource<Element = Vector>>(source: &S, scene: &mut
|
|||||||
// The outer opacity/blend layer (above) handles the user-set opacity.
|
// The outer opacity/blend layer (above) handles the user-set opacity.
|
||||||
let mut mask_item = Item::new_from_element(cloned_element).with_attribute(ATTR_TRANSFORM, item_transform);
|
let mut mask_item = Item::new_from_element(cloned_element).with_attribute(ATTR_TRANSFORM, item_transform);
|
||||||
set_paint_attribute(mask_item.attributes_mut(), ATTR_FILL, List::new_from_element(Color::BLACK));
|
set_paint_attribute(mask_item.attributes_mut(), ATTR_FILL, List::new_from_element(Color::BLACK));
|
||||||
|
mask_item.set_attribute(graphic_types::ATTR_APPEARANCE, black_fill_appearance());
|
||||||
let vector_list = List::new_from_item(mask_item);
|
let vector_list = List::new_from_item(mask_item);
|
||||||
|
|
||||||
let bounds = element.bounding_box_with_transform(multiplied_transform).unwrap_or(layer_bounds);
|
let bounds = element.bounding_box_with_transform(multiplied_transform).unwrap_or(layer_bounds);
|
||||||
@@ -1833,7 +1849,13 @@ fn render_vector_vello<S: LaneSource<Element = Vector>>(source: &S, scene: &mut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_vector_metadata<S: LaneSource<Element = Vector>>(source: &S, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
fn collect_vector_metadata<S: LaneSource<Element = Vector>>(
|
||||||
|
source: &S,
|
||||||
|
inherited_appearance: Option<&Appearance>,
|
||||||
|
metadata: &mut RenderMetadata,
|
||||||
|
footprint: Footprint,
|
||||||
|
caller_element_id: Option<NodeId>,
|
||||||
|
) {
|
||||||
// Aggregate all items' targets per element_id so multi-item lists (e.g. 'Text' node with "Separate Glyphs" active) produce hit areas for every glyph.
|
// Aggregate all items' targets per element_id so multi-item lists (e.g. 'Text' node with "Separate Glyphs" active) produce hit areas for every glyph.
|
||||||
// Targets are baked relative to item 0's transform since `Graphic::collect_metadata` records that as `local_transforms[element_id]`.
|
// Targets are baked relative to item 0's transform since `Graphic::collect_metadata` records that as `local_transforms[element_id]`.
|
||||||
let item_zero_transform: DAffine2 = if source.lane_count() > 0 { source.attr::<Transform>(0) } else { DAffine2::IDENTITY };
|
let item_zero_transform: DAffine2 = if source.lane_count() > 0 { source.attr::<Transform>(0) } else { DAffine2::IDENTITY };
|
||||||
@@ -1852,6 +1874,10 @@ fn collect_vector_metadata<S: LaneSource<Element = Vector>>(source: &S, metadata
|
|||||||
let layer_path: &[NodeId] = source.attr::<EditorLayerPath>(index);
|
let layer_path: &[NodeId] = source.attr::<EditorLayerPath>(index);
|
||||||
let layer = layer_path.last().copied();
|
let layer = layer_path.last().copied();
|
||||||
|
|
||||||
|
// The lane's paint: its own declared appearance, or the nearest ancestor's through the cascade
|
||||||
|
let appearance = Appearance::cascade(source.attr::<AppearanceMarker>(index), inherited_appearance);
|
||||||
|
let resolved = appearance.map(Appearance::fill_and_stroke).unwrap_or_default();
|
||||||
|
|
||||||
if let Some(element_id) = caller_element_id.or(layer) {
|
if let Some(element_id) = caller_element_id.or(layer) {
|
||||||
// When recovering element_id from the item's editor:layer_path tag (because the caller
|
// When recovering element_id from the item's editor:layer_path tag (because the caller
|
||||||
// passed None), also store the transform metadata that Graphic::collect_metadata
|
// passed None), also store the transform metadata that Graphic::collect_metadata
|
||||||
@@ -1867,12 +1893,12 @@ fn collect_vector_metadata<S: LaneSource<Element = Vector>>(source: &S, metadata
|
|||||||
let item_relative_transform = item_zero_inverse * transform;
|
let item_relative_transform = item_zero_inverse * transform;
|
||||||
|
|
||||||
let mut click_targets_unwrapped = Vec::new();
|
let mut click_targets_unwrapped = Vec::new();
|
||||||
extend_targets_from_vector(&mut click_targets_unwrapped, source, index, click_target_vector, item_relative_transform);
|
extend_targets_from_vector(&mut click_targets_unwrapped, &resolved, click_target_vector, item_relative_transform);
|
||||||
accumulated_click_targets.entry(element_id).or_default().extend(click_targets_unwrapped.into_iter().map(Arc::new));
|
accumulated_click_targets.entry(element_id).or_default().extend(click_targets_unwrapped.into_iter().map(Arc::new));
|
||||||
|
|
||||||
// Outlines always use source geometry so the visual outline reflects actual letterforms
|
// Outlines always use source geometry so the visual outline reflects actual letterforms
|
||||||
let mut outlines_unwrapped = Vec::new();
|
let mut outlines_unwrapped = Vec::new();
|
||||||
extend_targets_from_vector(&mut outlines_unwrapped, source, index, element, item_relative_transform);
|
extend_targets_from_vector(&mut outlines_unwrapped, &resolved, element, item_relative_transform);
|
||||||
accumulated_outlines.entry(element_id).or_default().extend(outlines_unwrapped.into_iter().map(Arc::new));
|
accumulated_outlines.entry(element_id).or_default().extend(outlines_unwrapped.into_iter().map(Arc::new));
|
||||||
|
|
||||||
// Source geometry (not the click-target override) so editing tools work on letterforms.
|
// Source geometry (not the click-target override) so editing tools work on letterforms.
|
||||||
@@ -1882,10 +1908,10 @@ fn collect_vector_metadata<S: LaneSource<Element = Vector>>(source: &S, metadata
|
|||||||
if let std::collections::hash_map::Entry::Vacant(e) = metadata.vector_data.entry(element_id) {
|
if let std::collections::hash_map::Entry::Vacant(e) = metadata.vector_data.entry(element_id) {
|
||||||
e.insert(Arc::new(element.clone()));
|
e.insert(Arc::new(element.clone()));
|
||||||
|
|
||||||
if let Some(fill_graphic) = source.attr::<Fill>(index).filter(|list| is_paint_present(list)) {
|
if let Some(fill_graphic) = resolved.fill_paint.and_then(paint_cell_rows) {
|
||||||
metadata.fill_attributes.insert(element_id, Arc::new(fill_graphic.clone()));
|
metadata.fill_attributes.insert(element_id, Arc::new(fill_graphic.clone()));
|
||||||
}
|
}
|
||||||
if let Some(stroke_graphic) = source.attr::<Stroke>(index).filter(|list| is_paint_present(list)) {
|
if let Some(stroke_graphic) = resolved.stroke_paint.and_then(paint_cell_rows) {
|
||||||
metadata.stroke_attributes.insert(element_id, Arc::new(stroke_graphic.clone()));
|
metadata.stroke_attributes.insert(element_id, Arc::new(stroke_graphic.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1915,7 +1941,7 @@ fn collect_vector_metadata<S: LaneSource<Element = Vector>>(source: &S, metadata
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_vector_upstream_click_targets<S: LaneSource<Element = Vector>>(source: &S, click_targets: &mut Vec<ClickTarget>) {
|
fn add_vector_upstream_click_targets<S: LaneSource<Element = Vector>>(source: &S, inherited_appearance: Option<&Appearance>, click_targets: &mut Vec<ClickTarget>) {
|
||||||
for index in 0..source.lane_count() {
|
for index in 0..source.lane_count() {
|
||||||
let Some(element) = source.element(index) else { continue };
|
let Some(element) = source.element(index) else { continue };
|
||||||
let transform: DAffine2 = source.attr::<Transform>(index);
|
let transform: DAffine2 = source.attr::<Transform>(index);
|
||||||
@@ -1923,39 +1949,45 @@ fn add_vector_upstream_click_targets<S: LaneSource<Element = Vector>>(source: &S
|
|||||||
// Use click-target override geometry if the item provides one (e.g. 'Text' node's per-glyph bounding boxes)
|
// Use click-target override geometry if the item provides one (e.g. 'Text' node's per-glyph bounding boxes)
|
||||||
let vector = source.attr::<EditorClickTarget>(index).unwrap_or(element);
|
let vector = source.attr::<EditorClickTarget>(index).unwrap_or(element);
|
||||||
|
|
||||||
extend_targets_from_vector(click_targets, source, index, vector, transform);
|
let appearance = Appearance::cascade(source.attr::<AppearanceMarker>(index), inherited_appearance);
|
||||||
|
let resolved = appearance.map(Appearance::fill_and_stroke).unwrap_or_default();
|
||||||
|
|
||||||
|
extend_targets_from_vector(click_targets, &resolved, vector, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_vector_upstream_outline_targets<S: LaneSource<Element = Vector>>(source: &S, outlines: &mut Vec<ClickTarget>) {
|
fn add_vector_upstream_outline_targets<S: LaneSource<Element = Vector>>(source: &S, inherited_appearance: Option<&Appearance>, outlines: &mut Vec<ClickTarget>) {
|
||||||
// Source geometry only, ignoring `editor:click_target`, so outlines reflect actual letterforms
|
// Source geometry only, ignoring `editor:click_target`, so outlines reflect actual letterforms
|
||||||
for index in 0..source.lane_count() {
|
for index in 0..source.lane_count() {
|
||||||
let Some(element) = source.element(index) else { continue };
|
let Some(element) = source.element(index) else { continue };
|
||||||
let transform: DAffine2 = source.attr::<Transform>(index);
|
let transform: DAffine2 = source.attr::<Transform>(index);
|
||||||
|
|
||||||
extend_targets_from_vector(outlines, source, index, element, transform);
|
let appearance = Appearance::cascade(source.attr::<AppearanceMarker>(index), inherited_appearance);
|
||||||
|
let resolved = appearance.map(Appearance::fill_and_stroke).unwrap_or_default();
|
||||||
|
|
||||||
|
extend_targets_from_vector(outlines, &resolved, element, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for List<Vector> {
|
impl Render for List<Vector> {
|
||||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
render_vector_svg(self, render, render_params)
|
render_vector_svg(self, None, render, render_params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
render_vector_vello(self, scene, parent_transform, context, render_params)
|
render_vector_vello(self, None, scene, parent_transform, context, render_params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
||||||
collect_vector_metadata(self, metadata, footprint, caller_element_id)
|
collect_vector_metadata(self, None, metadata, footprint, caller_element_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||||
add_vector_upstream_click_targets(self, click_targets)
|
add_vector_upstream_click_targets(self, None, click_targets)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_upstream_outline_targets(&self, outlines: &mut Vec<ClickTarget>) {
|
fn add_upstream_outline_targets(&self, outlines: &mut Vec<ClickTarget>) {
|
||||||
add_vector_upstream_outline_targets(self, outlines)
|
add_vector_upstream_outline_targets(self, None, outlines)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_ids_from_hash(&mut self, reference: Option<NodeId>) {
|
fn new_ids_from_hash(&mut self, reference: Option<NodeId>) {
|
||||||
@@ -1967,15 +1999,15 @@ impl Render for List<Vector> {
|
|||||||
|
|
||||||
/// Build one `CompoundPath` (non-zero fill rule, so holes like the inside of an "O" work
|
/// Build one `CompoundPath` (non-zero fill rule, so holes like the inside of an "O" work
|
||||||
/// correctly) plus one `FreePoint` per disconnected anchor, apply the transform, and append.
|
/// correctly) plus one `FreePoint` per disconnected anchor, apply the transform, and append.
|
||||||
fn extend_targets_from_vector<S: LaneSource<Element = Vector>>(targets: &mut Vec<ClickTarget>, source: &S, index: usize, geometry: &Vector, transform: DAffine2) {
|
fn extend_targets_from_vector(targets: &mut Vec<ClickTarget>, resolved: &graphic_types::appearance::FillAndStroke<'_>, geometry: &Vector, transform: DAffine2) {
|
||||||
let filled = has_paint::<Fill, _>(source, index);
|
let filled = resolved.fill_paint.and_then(paint_cell_rows).is_some();
|
||||||
|
|
||||||
let mut subpaths: Vec<Subpath<_>> = geometry.stroke_bezier_paths().collect();
|
let mut subpaths: Vec<Subpath<_>> = geometry.stroke_bezier_paths().collect();
|
||||||
let all_subpaths_closed = subpaths.iter().all(|subpath| subpath.closed());
|
let all_subpaths_closed = subpaths.iter().all(|subpath| subpath.closed());
|
||||||
|
|
||||||
// Inside/Outside-aligned strokes reach `weight` from the centerline rather than `weight / 2` per side,
|
// Inside/Outside-aligned strokes reach `weight` from the centerline rather than `weight / 2` per side,
|
||||||
// so they need double the click inflation. Alignment is only honored by the renderer for fully-closed paths.
|
// so they need double the click inflation. Alignment is only honored by the renderer for fully-closed paths.
|
||||||
let stroke_width = geometry.stroke.as_ref().map_or(0., |stroke| {
|
let stroke_width = resolved.stroke.as_ref().map_or(0., |stroke| {
|
||||||
if stroke.align.is_not_centered() && all_subpaths_closed {
|
if stroke.align.is_not_centered() && all_subpaths_closed {
|
||||||
stroke.weight * 2.
|
stroke.weight * 2.
|
||||||
} else {
|
} else {
|
||||||
@@ -2954,23 +2986,23 @@ impl Render for RunView<'_, Graphic<'_>> {
|
|||||||
|
|
||||||
impl Render for RunView<'_, Vector> {
|
impl Render for RunView<'_, Vector> {
|
||||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
render_vector_svg(self, render, render_params)
|
render_vector_svg(self, None, render, render_params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
render_vector_vello(self, scene, parent_transform, context, render_params)
|
render_vector_vello(self, None, scene, parent_transform, context, render_params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
||||||
collect_vector_metadata(self, metadata, footprint, caller_element_id)
|
collect_vector_metadata(self, None, metadata, footprint, caller_element_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||||
add_vector_upstream_click_targets(self, click_targets)
|
add_vector_upstream_click_targets(self, None, click_targets)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_upstream_outline_targets(&self, outlines: &mut Vec<ClickTarget>) {
|
fn add_upstream_outline_targets(&self, outlines: &mut Vec<ClickTarget>) {
|
||||||
add_vector_upstream_outline_targets(self, outlines)
|
add_vector_upstream_outline_targets(self, None, outlines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3120,6 +3152,11 @@ mod group_walk_tests {
|
|||||||
List::new_from_element(Graphic::Color(Color::from_rgbaf32(0.8, 0.2, 0.33, 1.).unwrap()))
|
List::new_from_element(Graphic::Color(Color::from_rgbaf32(0.8, 0.2, 0.33, 1.).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The appearance the fill node stamps beside the legacy fill marker, so test content mirrors node output.
|
||||||
|
fn fill_appearance(paint: &List<Graphic<'static>>) -> Appearance {
|
||||||
|
Appearance::new_single(Coverage::new_fill(), Graphic::Graphic(paint.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
fn rendered_svg(render: impl FnOnce(&mut SvgRender)) -> (String, String) {
|
fn rendered_svg(render: impl FnOnce(&mut SvgRender)) -> (String, String) {
|
||||||
let mut svg_render = SvgRender::new();
|
let mut svg_render = SvgRender::new();
|
||||||
render(&mut svg_render);
|
render(&mut svg_render);
|
||||||
@@ -3132,9 +3169,11 @@ mod group_walk_tests {
|
|||||||
let paint = color_paint();
|
let paint = color_paint();
|
||||||
let vectors = [unit_square_at(DVec2::ZERO), unit_square_at(DVec2::new(3., 1.))];
|
let vectors = [unit_square_at(DVec2::ZERO), unit_square_at(DVec2::new(3., 1.))];
|
||||||
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
||||||
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Vector>(), &[FieldWrite::of::<Fill>(0)], 2).unwrap();
|
let appearance = fill_appearance(&paint);
|
||||||
|
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Vector>(), &[FieldWrite::of::<Fill>(0), FieldWrite::of::<AppearanceMarker>(0)], 2).unwrap();
|
||||||
let lane = builder.push(vectors[0].clone()).unwrap();
|
let lane = builder.push(vectors[0].clone()).unwrap();
|
||||||
builder.attr::<Fill>(lane, Some(&paint));
|
builder.attr::<Fill>(lane, Some(&paint));
|
||||||
|
builder.attr::<AppearanceMarker>(lane, Some(&appearance));
|
||||||
builder.push(vectors[1].clone()).unwrap();
|
builder.push(vectors[1].clone()).unwrap();
|
||||||
let item = builder.finish();
|
let item = builder.finish();
|
||||||
let group = Group { row: None, content: item };
|
let group = Group { row: None, content: item };
|
||||||
@@ -3151,9 +3190,11 @@ mod group_walk_tests {
|
|||||||
let paint = color_paint();
|
let paint = color_paint();
|
||||||
let inner = Graphic::Vector(unit_square_at(DVec2::ZERO));
|
let inner = Graphic::Vector(unit_square_at(DVec2::ZERO));
|
||||||
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
||||||
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Graphic>(), &[FieldWrite::of::<Fill>(0)], 1).unwrap();
|
let appearance = fill_appearance(&paint);
|
||||||
|
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Graphic>(), &[FieldWrite::of::<Fill>(0), FieldWrite::of::<AppearanceMarker>(0)], 1).unwrap();
|
||||||
let lane = builder.push(inner.clone()).unwrap();
|
let lane = builder.push(inner.clone()).unwrap();
|
||||||
builder.attr::<Fill>(lane, Some(&paint));
|
builder.attr::<Fill>(lane, Some(&paint));
|
||||||
|
builder.attr::<AppearanceMarker>(lane, Some(&appearance));
|
||||||
let item = builder.finish();
|
let item = builder.finish();
|
||||||
let group = Group { row: None, content: item };
|
let group = Group { row: None, content: item };
|
||||||
|
|
||||||
@@ -3170,9 +3211,11 @@ mod group_walk_tests {
|
|||||||
let paint = color_paint();
|
let paint = color_paint();
|
||||||
let vectors = [unit_square_at(DVec2::ZERO)];
|
let vectors = [unit_square_at(DVec2::ZERO)];
|
||||||
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
||||||
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Vector>(), &[FieldWrite::of::<Fill>(0)], 1).unwrap();
|
let appearance = fill_appearance(&paint);
|
||||||
|
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Vector>(), &[FieldWrite::of::<Fill>(0), FieldWrite::of::<AppearanceMarker>(0)], 1).unwrap();
|
||||||
let lane = builder.push(vectors[0].clone()).unwrap();
|
let lane = builder.push(vectors[0].clone()).unwrap();
|
||||||
builder.attr::<Fill>(lane, Some(&paint));
|
builder.attr::<Fill>(lane, Some(&paint));
|
||||||
|
builder.attr::<AppearanceMarker>(lane, Some(&appearance));
|
||||||
let item = builder.finish();
|
let item = builder.finish();
|
||||||
let group = Group { row: None, content: item };
|
let group = Group { row: None, content: item };
|
||||||
|
|
||||||
@@ -3195,9 +3238,11 @@ mod group_walk_tests {
|
|||||||
let paint = color_paint();
|
let paint = color_paint();
|
||||||
let vectors = [unit_square_at(DVec2::ZERO), unit_square_at(DVec2::new(2., 2.))];
|
let vectors = [unit_square_at(DVec2::ZERO), unit_square_at(DVec2::new(2., 2.))];
|
||||||
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
let arena = core_types::arena::Arena::new(1 << 16).unwrap();
|
||||||
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Vector>(), &[FieldWrite::of::<Fill>(0)], 2).unwrap();
|
let appearance = fill_appearance(&paint);
|
||||||
|
let mut builder = RunBuilder::new(&arena, element_write_hashed::<Vector>(), &[FieldWrite::of::<Fill>(0), FieldWrite::of::<AppearanceMarker>(0)], 2).unwrap();
|
||||||
let lane = builder.push(vectors[0].clone()).unwrap();
|
let lane = builder.push(vectors[0].clone()).unwrap();
|
||||||
builder.attr::<Fill>(lane, Some(&paint));
|
builder.attr::<Fill>(lane, Some(&paint));
|
||||||
|
builder.attr::<AppearanceMarker>(lane, Some(&appearance));
|
||||||
builder.push(vectors[1].clone()).unwrap();
|
builder.push(vectors[1].clone()).unwrap();
|
||||||
let item = builder.finish();
|
let item = builder.finish();
|
||||||
let group = Group { row: None, content: item };
|
let group = Group { row: None, content: item };
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ fn mirror_lane<'e, T: Clone + Default + Send + Sync + 'static>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, graphic_types::markers::Fill>,
|
Attr<'e, graphic_types::markers::Fill>,
|
||||||
Attr<'e, graphic_types::markers::Stroke>,
|
Attr<'e, graphic_types::markers::Stroke>,
|
||||||
|
Attr<'e, graphic_types::markers::Appearance>,
|
||||||
Attr<'e, core_types::attribute::BlendMode>,
|
Attr<'e, core_types::attribute::BlendMode>,
|
||||||
Attr<'e, core_types::attribute::Opacity>,
|
Attr<'e, core_types::attribute::Opacity>,
|
||||||
Attr<'e, core_types::attribute::OpacityFill>,
|
Attr<'e, core_types::attribute::OpacityFill>,
|
||||||
@@ -227,6 +228,10 @@ where
|
|||||||
}
|
}
|
||||||
let fill = park_paint(legacy.attribute::<Option<List<Graphic>>>(graphic_types::ATTR_FILL, source).cloned().flatten())?;
|
let fill = park_paint(legacy.attribute::<Option<List<Graphic>>>(graphic_types::ATTR_FILL, source).cloned().flatten())?;
|
||||||
let stroke = park_paint(legacy.attribute::<Option<List<Graphic>>>(graphic_types::ATTR_STROKE, source).cloned().flatten())?;
|
let stroke = park_paint(legacy.attribute::<Option<List<Graphic>>>(graphic_types::ATTR_STROKE, source).cloned().flatten())?;
|
||||||
|
let appearance = match legacy.attribute::<graphic_types::Appearance>(graphic_types::ATTR_APPEARANCE, source).cloned() {
|
||||||
|
Some(appearance) => Some(&*arena.alloc_sized_keyed(appearance, 0).ok_or_else(exhausted)?.0),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
let layer_path: Vec<NodeId> = legacy.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, source).cloned().unwrap_or_default();
|
let layer_path: Vec<NodeId> = legacy.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, source).cloned().unwrap_or_default();
|
||||||
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
||||||
|
|
||||||
@@ -235,6 +240,7 @@ where
|
|||||||
Attr(transform),
|
Attr(transform),
|
||||||
Attr(fill),
|
Attr(fill),
|
||||||
Attr(stroke),
|
Attr(stroke),
|
||||||
|
Attr(appearance),
|
||||||
Attr(legacy.attribute_cloned_or_default(core_types::ATTR_BLEND_MODE, source)),
|
Attr(legacy.attribute_cloned_or_default(core_types::ATTR_BLEND_MODE, source)),
|
||||||
Attr(legacy.attribute_cloned_or(core_types::ATTR_OPACITY, source, 1.)),
|
Attr(legacy.attribute_cloned_or(core_types::ATTR_OPACITY, source, 1.)),
|
||||||
Attr(legacy.attribute_cloned_or(core_types::ATTR_OPACITY_FILL, source, 1.)),
|
Attr(legacy.attribute_cloned_or(core_types::ATTR_OPACITY_FILL, source, 1.)),
|
||||||
@@ -268,6 +274,7 @@ fn mirror<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, graphic_types::markers::Fill>,
|
Attr<'e, graphic_types::markers::Fill>,
|
||||||
Attr<'e, graphic_types::markers::Stroke>,
|
Attr<'e, graphic_types::markers::Stroke>,
|
||||||
|
Attr<'e, graphic_types::markers::Appearance>,
|
||||||
Attr<'e, core_types::attribute::BlendMode>,
|
Attr<'e, core_types::attribute::BlendMode>,
|
||||||
Attr<'e, core_types::attribute::Opacity>,
|
Attr<'e, core_types::attribute::Opacity>,
|
||||||
Attr<'e, core_types::attribute::OpacityFill>,
|
Attr<'e, core_types::attribute::OpacityFill>,
|
||||||
@@ -318,6 +325,7 @@ fn mirror_vector<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, graphic_types::markers::Fill>,
|
Attr<'e, graphic_types::markers::Fill>,
|
||||||
Attr<'e, graphic_types::markers::Stroke>,
|
Attr<'e, graphic_types::markers::Stroke>,
|
||||||
|
Attr<'e, graphic_types::markers::Appearance>,
|
||||||
Attr<'e, core_types::attribute::BlendMode>,
|
Attr<'e, core_types::attribute::BlendMode>,
|
||||||
Attr<'e, core_types::attribute::Opacity>,
|
Attr<'e, core_types::attribute::Opacity>,
|
||||||
Attr<'e, core_types::attribute::OpacityFill>,
|
Attr<'e, core_types::attribute::OpacityFill>,
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ use core_types::list::{Item, List};
|
|||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, BlendMode, Color, Ctx};
|
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, BlendMode, Color, Ctx};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphic_types::appearance::{Appearance, CoverPlacement, Coverage};
|
||||||
use graphic_types::graphic::{GraphicLevel, PaintColumns, PaintReach, bake_paint_transforms, is_paint_present, set_paint_attribute, set_paint_attribute_at};
|
use graphic_types::graphic::{GraphicLevel, PaintColumns, PaintReach, bake_paint_transforms, is_paint_present, set_paint_attribute, set_paint_attribute_at};
|
||||||
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
|
use graphic_types::markers::{Appearance as AppearanceMarker, EditorMergedLayers, Fill, Stroke};
|
||||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||||
use graphic_types::vector_types::GradientStops;
|
use graphic_types::vector_types::GradientStops;
|
||||||
use graphic_types::vector_types::gradient::{GradientSpreadMethod, GradientType};
|
use graphic_types::vector_types::gradient::{GradientSpreadMethod, GradientType};
|
||||||
@@ -35,6 +36,7 @@ fn boolean_core<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, Stroke>,
|
Attr<'e, Stroke>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -78,6 +80,10 @@ fn boolean_core<'e>(
|
|||||||
use core_types::lane::LaneSource;
|
use core_types::lane::LaneSource;
|
||||||
let fill = park_paint(result_vector_list.attr::<Fill>(0).filter(|paint| is_paint_present(paint)).cloned())?;
|
let fill = park_paint(result_vector_list.attr::<Fill>(0).filter(|paint| is_paint_present(paint)).cloned())?;
|
||||||
let stroke = park_paint(result_vector_list.attr::<Stroke>(0).filter(|paint| is_paint_present(paint)).cloned())?;
|
let stroke = park_paint(result_vector_list.attr::<Stroke>(0).filter(|paint| is_paint_present(paint)).cloned())?;
|
||||||
|
let appearance = match result_vector_list.attr::<AppearanceMarker>(0).cloned() {
|
||||||
|
Some(appearance) => Some(&*arena.alloc_sized_keyed(appearance, 0).ok_or_else(exhausted)?.0),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
let layer_path: Vec<NodeId> = result_vector_list.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, 0).cloned().unwrap_or_default();
|
let layer_path: Vec<NodeId> = result_vector_list.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, 0).cloned().unwrap_or_default();
|
||||||
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
||||||
// Snapshot the input layers so the renderer can recurse into them for
|
// Snapshot the input layers so the renderer can recurse into them for
|
||||||
@@ -89,6 +95,7 @@ fn boolean_core<'e>(
|
|||||||
Attr(result_vector_list.attribute_cloned_or_default(ATTR_TRANSFORM, 0)),
|
Attr(result_vector_list.attribute_cloned_or_default(ATTR_TRANSFORM, 0)),
|
||||||
Attr(fill),
|
Attr(fill),
|
||||||
Attr(stroke),
|
Attr(stroke),
|
||||||
|
Attr(appearance),
|
||||||
Attr(result_vector_list.attribute_cloned_or_default(ATTR_BLEND_MODE, 0)),
|
Attr(result_vector_list.attribute_cloned_or_default(ATTR_BLEND_MODE, 0)),
|
||||||
Attr(result_vector_list.attribute_cloned_or(ATTR_OPACITY, 0, 1.)),
|
Attr(result_vector_list.attribute_cloned_or(ATTR_OPACITY, 0, 1.)),
|
||||||
Attr(result_vector_list.attribute_cloned_or(ATTR_OPACITY_FILL, 0, 1.)),
|
Attr(result_vector_list.attribute_cloned_or(ATTR_OPACITY_FILL, 0, 1.)),
|
||||||
@@ -117,6 +124,7 @@ fn boolean_operation<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, Stroke>,
|
Attr<'e, Stroke>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -144,6 +152,7 @@ fn boolean_operation_vector<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, Stroke>,
|
Attr<'e, Stroke>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -301,15 +310,22 @@ fn raster_stand_in_rows<S: core_types::lane::LaneSource>(image: &S, parent_trans
|
|||||||
.with_attribute(ATTR_CLIPPING_MASK, clip)
|
.with_attribute(ATTR_CLIPPING_MASK, clip)
|
||||||
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer);
|
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer);
|
||||||
set_paint_attribute(item.attributes_mut(), ATTR_FILL, List::new_from_element(Color::BLACK));
|
set_paint_attribute(item.attributes_mut(), ATTR_FILL, List::new_from_element(Color::BLACK));
|
||||||
|
item.set_attribute(graphic_types::ATTR_APPEARANCE, fill_appearance(List::new_from_element(Graphic::Color(Color::BLACK))));
|
||||||
item
|
item
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The single-fill appearance a built row paints with, beside its legacy fill marker.
|
||||||
|
fn fill_appearance(paint: List<Graphic<'static>>) -> Appearance {
|
||||||
|
Appearance::new_single(Coverage::new_fill(), Graphic::Graphic(paint))
|
||||||
|
}
|
||||||
|
|
||||||
/// A color row: an empty vector carrying the color as its fill paint over the
|
/// A color row: an empty vector carrying the color as its fill paint over the
|
||||||
/// lane's attributes.
|
/// lane's attributes.
|
||||||
fn color_paint_row(color: Color, mut attributes: core_types::list::ItemAttributeValues) -> Item<Vector> {
|
fn color_paint_row(color: Color, mut attributes: core_types::list::ItemAttributeValues) -> Item<Vector> {
|
||||||
set_paint_attribute(&mut attributes, ATTR_FILL, List::new_from_element(color));
|
set_paint_attribute(&mut attributes, ATTR_FILL, List::new_from_element(color));
|
||||||
|
attributes.insert(graphic_types::ATTR_APPEARANCE, fill_appearance(List::new_from_element(Graphic::Color(color))));
|
||||||
|
|
||||||
let mut element = Vector::default();
|
let mut element = Vector::default();
|
||||||
element.set_stroke_transform(DAffine2::IDENTITY);
|
element.set_stroke_transform(DAffine2::IDENTITY);
|
||||||
@@ -330,7 +346,8 @@ fn gradient_paint_row(stops: GradientStops, mut attributes: core_types::list::It
|
|||||||
if let Some(spread_method) = attributes.remove::<GradientSpreadMethod>(ATTR_SPREAD_METHOD) {
|
if let Some(spread_method) = attributes.remove::<GradientSpreadMethod>(ATTR_SPREAD_METHOD) {
|
||||||
gradient_paint.set_attribute(ATTR_SPREAD_METHOD, 0, spread_method);
|
gradient_paint.set_attribute(ATTR_SPREAD_METHOD, 0, spread_method);
|
||||||
}
|
}
|
||||||
attributes.insert(ATTR_FILL, Some(gradient_paint));
|
attributes.insert(ATTR_FILL, Some(gradient_paint.clone()));
|
||||||
|
attributes.insert(graphic_types::ATTR_APPEARANCE, fill_appearance(gradient_paint));
|
||||||
|
|
||||||
let mut element = Vector::default();
|
let mut element = Vector::default();
|
||||||
element.set_stroke_transform(DAffine2::IDENTITY);
|
element.set_stroke_transform(DAffine2::IDENTITY);
|
||||||
@@ -368,6 +385,7 @@ fn push_leaf_vector_row(out: &mut List<Vector>, level: GraphicLevel<'_>, index:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stamp_inherited_appearance(out, out_index, reach.appearance);
|
||||||
let current: DAffine2 = out.attribute_cloned_or_default(ATTR_TRANSFORM, out_index);
|
let current: DAffine2 = out.attribute_cloned_or_default(ATTR_TRANSFORM, out_index);
|
||||||
out.set_attribute(ATTR_TRANSFORM, out_index, ancestors * current);
|
out.set_attribute(ATTR_TRANSFORM, out_index, ancestors * current);
|
||||||
}
|
}
|
||||||
@@ -384,11 +402,21 @@ fn push_vector_rows(out: &mut List<Vector>, rows: &List<Vector>, composed: DAffi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stamp_inherited_appearance(out, index, reach.appearance);
|
||||||
let current: DAffine2 = out.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
let current: DAffine2 = out.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
out.set_attribute(ATTR_TRANSFORM, index, composed * current);
|
out.set_attribute(ATTR_TRANSFORM, index, composed * current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The cascade's resolved appearance lands on a row whose own is undeclared, since a declared row wins wholesale.
|
||||||
|
fn stamp_inherited_appearance(out: &mut List<Vector>, index: usize, inherited: Option<&Appearance>) {
|
||||||
|
if let Some(appearance) = inherited
|
||||||
|
&& out.attribute::<Appearance>(graphic_types::ATTR_APPEARANCE, index).and_then(Appearance::declared).is_none()
|
||||||
|
{
|
||||||
|
out.set_attribute(graphic_types::ATTR_APPEARANCE, index, appearance.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn push_union(out: &mut List<Vector>, flattened: List<Vector>) {
|
fn push_union(out: &mut List<Vector>, flattened: List<Vector>) {
|
||||||
for row in boolean_operation_on_vector_list(&flattened, BooleanOperation::Union).into_iter() {
|
for row in boolean_operation_on_vector_list(&flattened, BooleanOperation::Union).into_iter() {
|
||||||
out.push(row);
|
out.push(row);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use core_types::transform::Transform;
|
|||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, CacheHash, Color, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, CacheHash, Color, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
use graphic_types::appearance::{Appearance, CoverPlacement, Coverage};
|
use graphic_types::appearance::{Appearance, Cover, CoverPlacement, Coverage};
|
||||||
use graphic_types::graphic::{bake_paint_transforms, has_paint, is_paint_present, set_paint_attribute_at};
|
use graphic_types::graphic::{bake_paint_transforms, has_paint, is_paint_present, set_paint_attribute_at};
|
||||||
use graphic_types::markers::{Appearance as AppearanceMarker, EditorMergedLayers, Fill, Stroke as StrokeAttr};
|
use graphic_types::markers::{Appearance as AppearanceMarker, EditorMergedLayers, Fill, Stroke as StrokeAttr};
|
||||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||||
@@ -101,7 +101,17 @@ fn assign_colors<'e>(
|
|||||||
/// The number of elements to span across the gradient before repeating. A 0 value will span the entire gradient once.
|
/// The number of elements to span across the gradient before repeating. A 0 value will span the entire gradient once.
|
||||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
|
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
|
||||||
repeat_every: u32,
|
repeat_every: u32,
|
||||||
) -> Result<IList<(Vector, Attr<'e, TransformAttr>, Attr<'e, Fill>, Attr<'e, StrokeAttr>, Attr<'e, EditorLayerPath>)>, Interrupt> {
|
) -> Result<
|
||||||
|
IList<(
|
||||||
|
Vector,
|
||||||
|
Attr<'e, TransformAttr>,
|
||||||
|
Attr<'e, Fill>,
|
||||||
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
|
Attr<'e, EditorLayerPath>,
|
||||||
|
)>,
|
||||||
|
Interrupt,
|
||||||
|
> {
|
||||||
let lane = ctx.index() as usize;
|
let lane = ctx.index() as usize;
|
||||||
if lane >= content.len() {
|
if lane >= content.len() {
|
||||||
return Err(GraphError::past_end().into());
|
return Err(GraphError::past_end().into());
|
||||||
@@ -110,11 +120,14 @@ fn assign_colors<'e>(
|
|||||||
let park_existing = |paint: Option<&List<Graphic<'static>>>| -> Result<Option<&'e List<Graphic>>, Interrupt> { paint.map(|paint| park_paint(ctx.arena(), paint.clone())).transpose() };
|
let park_existing = |paint: Option<&List<Graphic<'static>>>| -> Result<Option<&'e List<Graphic>>, Interrupt> { paint.map(|paint| park_paint(ctx.arena(), paint.clone())).transpose() };
|
||||||
let existing_fill = park_existing(content.lane(lane).attr::<Fill>())?;
|
let existing_fill = park_existing(content.lane(lane).attr::<Fill>())?;
|
||||||
let existing_stroke = park_existing(content.lane(lane).attr::<StrokeAttr>())?;
|
let existing_stroke = park_existing(content.lane(lane).attr::<StrokeAttr>())?;
|
||||||
|
let existing_appearance = content.lane(lane).attr::<AppearanceMarker>().cloned();
|
||||||
|
let park_appearance_attr = |appearance: Option<Appearance>| -> Result<Option<&'e Appearance>, Interrupt> { appearance.map(|appearance| park_appearance(ctx.arena(), appearance)).transpose() };
|
||||||
let carried = carried_lane_attrs(ctx.arena(), *content.lane(lane))?;
|
let carried = carried_lane_attrs(ctx.arena(), *content.lane(lane))?;
|
||||||
let (transform, layer_path) = carried;
|
let (transform, layer_path) = carried;
|
||||||
|
|
||||||
if gradient.is_empty() {
|
if gradient.is_empty() {
|
||||||
return Ok((element, transform, Attr(existing_fill), Attr(existing_stroke), layer_path));
|
let parked_appearance = park_appearance_attr(existing_appearance)?;
|
||||||
|
return Ok((element, transform, Attr(existing_fill), Attr(existing_stroke), Attr(parked_appearance), layer_path));
|
||||||
}
|
}
|
||||||
let gradient_element = gradient.element_ref(0);
|
let gradient_element = gradient.element_ref(0);
|
||||||
let reversed;
|
let reversed;
|
||||||
@@ -138,7 +151,19 @@ fn assign_colors<'e>(
|
|||||||
true => Some(parked),
|
true => Some(parked),
|
||||||
false => existing_stroke,
|
false => existing_stroke,
|
||||||
};
|
};
|
||||||
Ok((element, transform, Attr(fill_attr), Attr(stroke_attr), layer_path))
|
|
||||||
|
// The same recolor lands on the appearance's coverage paints
|
||||||
|
let mut appearance = existing_appearance.unwrap_or_default();
|
||||||
|
let paint_cell = Graphic::Graphic(parked.clone());
|
||||||
|
if fill && !appearance.set_paint_of(Cover::Fill, paint_cell.clone()) {
|
||||||
|
appearance.replace_or_insert(Coverage::new_fill(), paint_cell.clone(), CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
if stroke && element.stroke.is_some() && !appearance.set_paint_of(Cover::Stroke, paint_cell.clone()) {
|
||||||
|
appearance.replace_or_insert(Coverage::new_stroke(&element.stroke.clone().unwrap_or_default()), paint_cell, CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
let parked_appearance = park_appearance_attr(Some(appearance))?;
|
||||||
|
|
||||||
|
Ok((element, transform, Attr(fill_attr), Attr(stroke_attr), Attr(parked_appearance), layer_path))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@@ -229,15 +254,26 @@ fn assign_colors_graphic<'e>(
|
|||||||
let element = match rows {
|
let element = match rows {
|
||||||
Some(mut rows) => {
|
Some(mut rows) => {
|
||||||
for row in 0..rows.len() {
|
for row in 0..rows.len() {
|
||||||
let has_stroke = rows.element(row).is_some_and(|vector| vector.stroke.is_some());
|
let row_stroke = rows.element(row).and_then(|vector| vector.stroke.clone());
|
||||||
let color = assign_color_at(gradient_element, position + row, length, randomize, seed, repeat_every);
|
let color = assign_color_at(gradient_element, position + row, length, randomize, seed, repeat_every);
|
||||||
let paint = List::new_from_element(color).into_graphic_list();
|
let paint = List::new_from_element(color).into_graphic_list();
|
||||||
if fill {
|
if fill {
|
||||||
set_paint_attribute_at(&mut rows, row, ATTR_FILL, paint.clone());
|
set_paint_attribute_at(&mut rows, row, ATTR_FILL, paint.clone());
|
||||||
}
|
}
|
||||||
if stroke && has_stroke {
|
if stroke && row_stroke.is_some() {
|
||||||
set_paint_attribute_at(&mut rows, row, ATTR_STROKE, paint.clone());
|
set_paint_attribute_at(&mut rows, row, ATTR_STROKE, paint.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The same recolor lands on the row's appearance coverage paints
|
||||||
|
let mut appearance = rows.attribute_cloned_or_default::<Appearance>(graphic_types::ATTR_APPEARANCE, row);
|
||||||
|
let paint_cell = Graphic::Graphic(paint.clone());
|
||||||
|
if fill && !appearance.set_paint_of(Cover::Fill, paint_cell.clone()) {
|
||||||
|
appearance.replace_or_insert(Coverage::new_fill(), paint_cell.clone(), CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
if stroke && row_stroke.is_some() && !appearance.set_paint_of(Cover::Stroke, paint_cell.clone()) {
|
||||||
|
appearance.replace_or_insert(Coverage::new_stroke(&row_stroke.unwrap_or_default()), paint_cell, CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
rows.set_attribute(graphic_types::ATTR_APPEARANCE, row, appearance);
|
||||||
}
|
}
|
||||||
let content = core_types::record::GroupItem::from_list(rows, ctx.arena()).ok_or_else(|| Interrupt::from(GraphError::new("the arena is exhausted")))?;
|
let content = core_types::record::GroupItem::from_list(rows, ctx.arena()).ok_or_else(|| Interrupt::from(GraphError::new("the arena is exhausted")))?;
|
||||||
Graphic::Group(core_types::record::Group { row: None, content })
|
Graphic::Group(core_types::record::Group { row: None, content })
|
||||||
@@ -287,6 +323,15 @@ fn park_paint<'e>(arena: &'e core_types::arena::Arena, paint: List<Graphic<'stat
|
|||||||
Ok(parked)
|
Ok(parked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parks a row's carried appearance, absent rows staying absent.
|
||||||
|
fn park_row_appearance<'e>(arena: &'e core_types::arena::Arena, attributes: &ItemAttributeValues) -> Result<Option<&'e Appearance>, Interrupt> {
|
||||||
|
attributes
|
||||||
|
.get::<Appearance>(graphic_types::ATTR_APPEARANCE)
|
||||||
|
.cloned()
|
||||||
|
.map(|appearance| park_appearance(arena, appearance))
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
/// Keyed, as [`park_paint`] is, so a group-free appearance's promote moves this header.
|
/// Keyed, as [`park_paint`] is, so a group-free appearance's promote moves this header.
|
||||||
fn park_appearance<'e>(arena: &'e core_types::arena::Arena, appearance: Appearance) -> Result<&'e Appearance, Interrupt> {
|
fn park_appearance<'e>(arena: &'e core_types::arena::Arena, appearance: Appearance) -> Result<&'e Appearance, Interrupt> {
|
||||||
let (parked, _) = arena.alloc_sized_keyed(appearance, 0).ok_or(GraphError {
|
let (parked, _) = arena.alloc_sized_keyed(appearance, 0).ok_or(GraphError {
|
||||||
@@ -297,11 +342,12 @@ fn park_appearance<'e>(arena: &'e core_types::arena::Arena, appearance: Appearan
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Appends one coverage to the content's appearance following the painter's algorithm:
|
/// Appends one coverage to the content's appearance following the painter's algorithm:
|
||||||
/// the most downstream paint node in the chain paints on top. The coverage's paint is the
|
/// the most downstream paint node in the chain paints on top unless it asks for the
|
||||||
/// canonical paint list carried as one graphic cell, the input lane's own envelope dropped.
|
/// below placement. The coverage's paint is the canonical paint list carried as one
|
||||||
fn stamped_appearance(content_appearance: Option<&Appearance>, coverage: Coverage, paint: &List<Graphic<'static>>) -> Appearance {
|
/// graphic cell, the input lane's own envelope dropped.
|
||||||
|
fn stamped_appearance(content_appearance: Option<&Appearance>, coverage: Coverage, paint: &List<Graphic<'static>>, placement: CoverPlacement) -> Appearance {
|
||||||
let mut appearance = content_appearance.cloned().unwrap_or_default();
|
let mut appearance = content_appearance.cloned().unwrap_or_default();
|
||||||
appearance.replace_or_insert(coverage, Graphic::Graphic(paint.clone()), CoverPlacement::Above);
|
appearance.replace_or_insert(coverage, Graphic::Graphic(paint.clone()), placement);
|
||||||
appearance
|
appearance
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +409,7 @@ fn fill<'e>(
|
|||||||
) -> Result<(Vector, Attr<'e, Fill>, Attr<'e, AppearanceMarker>), Interrupt> {
|
) -> Result<(Vector, Attr<'e, Fill>, Attr<'e, AppearanceMarker>), Interrupt> {
|
||||||
let mut paint = paint_table(fill);
|
let mut paint = paint_table(fill);
|
||||||
default_gradient_paint(&mut paint, element.bounding_box(), _gradient_type, _spread_method, _has_transform.0.then_some(_transform));
|
default_gradient_paint(&mut paint, element.bounding_box(), _gradient_type, _spread_method, _has_transform.0.then_some(_transform));
|
||||||
let appearance = stamped_appearance(*content_appearance, Coverage::new_fill(), &paint);
|
let appearance = stamped_appearance(*content_appearance, Coverage::new_fill(), &paint, CoverPlacement::Above);
|
||||||
let parked = park_paint(ctx.arena(), paint)?;
|
let parked = park_paint(ctx.arena(), paint)?;
|
||||||
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
||||||
Ok((element, Attr(Some(parked)), Attr(Some(parked_appearance))))
|
Ok((element, Attr(Some(parked)), Attr(Some(parked_appearance))))
|
||||||
@@ -390,7 +436,7 @@ fn fill_graphic_leveled<'e>(
|
|||||||
};
|
};
|
||||||
let mut paint = paint_table(fill);
|
let mut paint = paint_table(fill);
|
||||||
default_gradient_paint(&mut paint, bounds, _gradient_type, _spread_method, _has_transform.0.then_some(_transform));
|
default_gradient_paint(&mut paint, bounds, _gradient_type, _spread_method, _has_transform.0.then_some(_transform));
|
||||||
let appearance = stamped_appearance(*content_appearance, Coverage::new_fill(), &paint);
|
let appearance = stamped_appearance(*content_appearance, Coverage::new_fill(), &paint, CoverPlacement::Above);
|
||||||
let parked = park_paint(ctx.arena(), paint)?;
|
let parked = park_paint(ctx.arena(), paint)?;
|
||||||
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
||||||
Ok((element, Attr(Some(parked)), Attr(Some(parked_appearance))))
|
Ok((element, Attr(Some(parked)), Attr(Some(parked_appearance))))
|
||||||
@@ -439,11 +485,10 @@ fn stroke<'e>(
|
|||||||
paint_order,
|
paint_order,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The coverage records the stroke's authoring space, so the item transform is composed in. Its translation
|
// The coverage records the stroke's authoring space, so the item transform is composed in, translation
|
||||||
// cancels out in every consumer, so it is cleared to let an otherwise-identity capture elide.
|
// included so the render consumers see the exact legacy stroke space.
|
||||||
let mut coverage_stroke = stroke.clone();
|
let mut coverage_stroke = stroke.clone();
|
||||||
coverage_stroke.transform *= *content_transform;
|
coverage_stroke.transform *= *content_transform;
|
||||||
coverage_stroke.transform.translation = DVec2::ZERO;
|
|
||||||
|
|
||||||
stroke.transform *= *content_transform;
|
stroke.transform *= *content_transform;
|
||||||
|
|
||||||
@@ -451,7 +496,12 @@ fn stroke<'e>(
|
|||||||
element.stroke = Some(stroke);
|
element.stroke = Some(stroke);
|
||||||
|
|
||||||
let paint = paint_table(paint);
|
let paint = paint_table(paint);
|
||||||
let appearance = stamped_appearance(*content_appearance, Coverage::new_stroke(&coverage_stroke), &paint);
|
// The coverage order carries the paint order: a below stroke lands before the fill in the list
|
||||||
|
let placement = match paint_order {
|
||||||
|
PaintOrder::StrokeAbove => CoverPlacement::Above,
|
||||||
|
PaintOrder::StrokeBelow => CoverPlacement::Below,
|
||||||
|
};
|
||||||
|
let appearance = stamped_appearance(*content_appearance, Coverage::new_stroke(&coverage_stroke), &paint, placement);
|
||||||
let parked = park_paint(ctx.arena(), paint)?;
|
let parked = park_paint(ctx.arena(), paint)?;
|
||||||
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
||||||
Ok((element, Attr(*content_transform), Attr(Some(parked)), Attr(Some(parked_appearance))))
|
Ok((element, Attr(*content_transform), Attr(Some(parked)), Attr(Some(parked_appearance))))
|
||||||
@@ -506,11 +556,9 @@ fn stroke_graphic_leveled<'e>(
|
|||||||
paint_order,
|
paint_order,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The coverage records the stroke's authoring space at the lane, composing the lane transform with cleared
|
// The coverage records the stroke's authoring space at the lane, composing the lane transform as in `stroke` above.
|
||||||
// translation, for the same reason as in `stroke` above.
|
|
||||||
let mut coverage_stroke = stroke.clone();
|
let mut coverage_stroke = stroke.clone();
|
||||||
coverage_stroke.transform *= *content_transform;
|
coverage_stroke.transform *= *content_transform;
|
||||||
coverage_stroke.transform.translation = DVec2::ZERO;
|
|
||||||
|
|
||||||
let mut element = element;
|
let mut element = element;
|
||||||
for_each_interior_vector_mut(&mut element, |vector, transform| {
|
for_each_interior_vector_mut(&mut element, |vector, transform| {
|
||||||
@@ -520,7 +568,12 @@ fn stroke_graphic_leveled<'e>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let paint = paint_table(paint);
|
let paint = paint_table(paint);
|
||||||
let appearance = stamped_appearance(*content_appearance, Coverage::new_stroke(&coverage_stroke), &paint);
|
// The coverage order carries the paint order: a below stroke lands before the fill in the list
|
||||||
|
let placement = match paint_order {
|
||||||
|
PaintOrder::StrokeAbove => CoverPlacement::Above,
|
||||||
|
PaintOrder::StrokeBelow => CoverPlacement::Below,
|
||||||
|
};
|
||||||
|
let appearance = stamped_appearance(*content_appearance, Coverage::new_stroke(&coverage_stroke), &paint, placement);
|
||||||
let parked = park_paint(ctx.arena(), paint)?;
|
let parked = park_paint(ctx.arena(), paint)?;
|
||||||
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
let parked_appearance = park_appearance(ctx.arena(), appearance)?;
|
||||||
Ok((element, Attr(*content_transform), Attr(Some(parked)), Attr(Some(parked_appearance))))
|
Ok((element, Attr(*content_transform), Attr(Some(parked)), Attr(Some(parked_appearance))))
|
||||||
@@ -1467,6 +1520,9 @@ fn solidify_rows(flattened: List<Vector>) -> List<Vector> {
|
|||||||
let mut fill_attributes = attributes.clone();
|
let mut fill_attributes = attributes.clone();
|
||||||
// No stroke remains on the fill row
|
// No stroke remains on the fill row
|
||||||
fill_attributes.remove::<Option<List<Graphic>>>(ATTR_STROKE);
|
fill_attributes.remove::<Option<List<Graphic>>>(ATTR_STROKE);
|
||||||
|
if let Some(appearance) = fill_attributes.get_mut::<Appearance>(graphic_types::ATTR_APPEARANCE) {
|
||||||
|
appearance.retain_cover(Cover::Fill);
|
||||||
|
}
|
||||||
Item::from_parts(vector, fill_attributes)
|
Item::from_parts(vector, fill_attributes)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1474,6 +1530,14 @@ fn solidify_rows(flattened: List<Vector>) -> List<Vector> {
|
|||||||
// Drop the original fill and use the stroke paint to fill the outlined stroke
|
// Drop the original fill and use the stroke paint to fill the outlined stroke
|
||||||
stroke_attributes.remove::<Option<List<Graphic>>>(ATTR_FILL);
|
stroke_attributes.remove::<Option<List<Graphic>>>(ATTR_FILL);
|
||||||
stroke_attributes.rename(ATTR_STROKE, ATTR_FILL);
|
stroke_attributes.rename(ATTR_STROKE, ATTR_FILL);
|
||||||
|
if let Some(appearance) = stroke_attributes.get_mut::<Appearance>(graphic_types::ATTR_APPEARANCE) {
|
||||||
|
// The outlined stroke is filled with the stroke coverage's paint
|
||||||
|
let stroke_paint = appearance.first_paint_of(Cover::Stroke).cloned();
|
||||||
|
*appearance = Appearance::default();
|
||||||
|
if let Some(paint) = stroke_paint {
|
||||||
|
appearance.replace_or_insert(Coverage::new_fill(), paint, CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let stroke_row = Item::from_parts(solidified_stroke, stroke_attributes);
|
let stroke_row = Item::from_parts(solidified_stroke, stroke_attributes);
|
||||||
|
|
||||||
@@ -1503,6 +1567,7 @@ fn solidify_native_lane<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -1563,6 +1628,7 @@ fn emit_legacy_lane<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -1593,6 +1659,11 @@ fn emit_legacy_lane<'e>(
|
|||||||
.and_then(|paint| paint.as_ref())
|
.and_then(|paint| paint.as_ref())
|
||||||
.map(|paint| park_paint(arena, paint.clone()))
|
.map(|paint| park_paint(arena, paint.clone()))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
let appearance = output
|
||||||
|
.attribute::<Appearance>(graphic_types::ATTR_APPEARANCE, lane)
|
||||||
|
.cloned()
|
||||||
|
.map(|appearance| park_appearance(arena, appearance))
|
||||||
|
.transpose()?;
|
||||||
let layer_path: Vec<NodeId> = output.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, lane).cloned().unwrap_or_default();
|
let layer_path: Vec<NodeId> = output.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, lane).cloned().unwrap_or_default();
|
||||||
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
||||||
let merged_layers = output
|
let merged_layers = output
|
||||||
@@ -1606,6 +1677,7 @@ fn emit_legacy_lane<'e>(
|
|||||||
Attr(output.attribute_cloned_or_default(ATTR_TRANSFORM, lane)),
|
Attr(output.attribute_cloned_or_default(ATTR_TRANSFORM, lane)),
|
||||||
Attr(fill),
|
Attr(fill),
|
||||||
Attr(stroke),
|
Attr(stroke),
|
||||||
|
Attr(appearance),
|
||||||
Attr(output.attribute_cloned_or_default(ATTR_BLEND_MODE, lane)),
|
Attr(output.attribute_cloned_or_default(ATTR_BLEND_MODE, lane)),
|
||||||
Attr(output.attribute_cloned_or(ATTR_OPACITY, lane, 1.)),
|
Attr(output.attribute_cloned_or(ATTR_OPACITY, lane, 1.)),
|
||||||
Attr(output.attribute_cloned_or(ATTR_OPACITY_FILL, lane, 1.)),
|
Attr(output.attribute_cloned_or(ATTR_OPACITY_FILL, lane, 1.)),
|
||||||
@@ -1653,6 +1725,7 @@ fn solidify_stroke<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -1693,6 +1766,7 @@ fn solidify_stroke_vector<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -1765,6 +1839,7 @@ fn separate_subpaths<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -1821,6 +1896,7 @@ fn map_points<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -1862,6 +1938,7 @@ fn flatten_path_core<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, EditorLayerPath>,
|
Attr<'e, EditorLayerPath>,
|
||||||
Attr<'e, EditorMergedLayers>,
|
Attr<'e, EditorMergedLayers>,
|
||||||
),
|
),
|
||||||
@@ -1927,7 +2004,31 @@ fn flatten_path_core<'e>(
|
|||||||
// editor click-target preservation, as the boolean operation does.
|
// editor click-target preservation, as the boolean operation does.
|
||||||
let merged_layers = arena.alloc_sized_keyed(snapshot, 0).ok_or_else(exhausted)?.0;
|
let merged_layers = arena.alloc_sized_keyed(snapshot, 0).ok_or_else(exhausted)?.0;
|
||||||
|
|
||||||
Ok((output, Attr(DAffine2::IDENTITY), Attr(fill), Attr(stroke), Attr(layer_path.as_slice()), Attr(Some(merged_layers))))
|
// The carried paints land on the appearance too, the stroke coverage recording the carried stroke's parameters
|
||||||
|
let appearance = {
|
||||||
|
let mut appearance = Appearance::default();
|
||||||
|
if let Some(fill_paint) = fill {
|
||||||
|
appearance.replace_or_insert(Coverage::new_fill(), Graphic::Graphic(fill_paint.clone()), CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
if let Some(stroke_paint) = stroke {
|
||||||
|
let coverage = Coverage::new_stroke(&output.stroke.clone().unwrap_or_default());
|
||||||
|
appearance.replace_or_insert(coverage, Graphic::Graphic(stroke_paint.clone()), CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
match appearance.declared().is_some() {
|
||||||
|
true => Some(park_appearance(arena, appearance)?),
|
||||||
|
false => None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
output,
|
||||||
|
Attr(DAffine2::IDENTITY),
|
||||||
|
Attr(fill),
|
||||||
|
Attr(stroke),
|
||||||
|
Attr(appearance),
|
||||||
|
Attr(layer_path.as_slice()),
|
||||||
|
Attr(Some(merged_layers)),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Rename to "Combine Paths" and make this happen per-element instead of flattening every element into a single path. The migration for this should then become a Flatten Vector -> Combine Paths pair of nodes.
|
// TODO: Rename to "Combine Paths" and make this happen per-element instead of flattening every element into a single path. The migration for this should then become a Flatten Vector -> Combine Paths pair of nodes.
|
||||||
@@ -1941,6 +2042,7 @@ pub fn flatten_path<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, EditorLayerPath>,
|
Attr<'e, EditorLayerPath>,
|
||||||
Attr<'e, EditorMergedLayers>,
|
Attr<'e, EditorMergedLayers>,
|
||||||
),
|
),
|
||||||
@@ -1964,6 +2066,7 @@ pub fn flatten_path_vector<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, EditorLayerPath>,
|
Attr<'e, EditorLayerPath>,
|
||||||
Attr<'e, EditorMergedLayers>,
|
Attr<'e, EditorMergedLayers>,
|
||||||
),
|
),
|
||||||
@@ -2289,6 +2392,7 @@ fn cut_path<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -3273,11 +3377,19 @@ fn morph_core(flattened: List<Vector>, snapshot: List<Graphic<'static>>, progres
|
|||||||
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer_path)
|
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer_path)
|
||||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, Some(graphic_list_content));
|
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, Some(graphic_list_content));
|
||||||
|
|
||||||
|
// The lerped paints land on the appearance too, the stroke coverage recording the lerped stroke's parameters
|
||||||
|
let mut appearance = Appearance::default();
|
||||||
if let Some(fill) = fill_paint {
|
if let Some(fill) = fill_paint {
|
||||||
item.set_attribute(ATTR_FILL, Some(fill));
|
item.set_attribute(ATTR_FILL, Some(fill.clone()));
|
||||||
|
appearance.replace_or_insert(Coverage::new_fill(), Graphic::Graphic(fill), CoverPlacement::Above);
|
||||||
}
|
}
|
||||||
if let Some(stroke) = stroke_paint {
|
if let Some(stroke) = stroke_paint {
|
||||||
item.set_attribute(ATTR_STROKE, Some(stroke));
|
item.set_attribute(ATTR_STROKE, Some(stroke.clone()));
|
||||||
|
let coverage = Coverage::new_stroke(&item.element().stroke.clone().unwrap_or_default());
|
||||||
|
appearance.replace_or_insert(coverage, Graphic::Graphic(stroke), CoverPlacement::Above);
|
||||||
|
}
|
||||||
|
if appearance.declared().is_some() {
|
||||||
|
item.set_attribute(graphic_types::ATTR_APPEARANCE, appearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
List::new_from_item(item)
|
List::new_from_item(item)
|
||||||
@@ -3300,6 +3412,7 @@ fn morph_lane<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -3340,6 +3453,7 @@ fn morph<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
@@ -3372,6 +3486,7 @@ fn morph_vector<'e>(
|
|||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
Attr<'e, Fill>,
|
Attr<'e, Fill>,
|
||||||
Attr<'e, StrokeAttr>,
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, AppearanceMarker>,
|
||||||
Attr<'e, BlendModeAttr>,
|
Attr<'e, BlendModeAttr>,
|
||||||
Attr<'e, Opacity>,
|
Attr<'e, Opacity>,
|
||||||
Attr<'e, OpacityFill>,
|
Attr<'e, OpacityFill>,
|
||||||
|
|||||||
Reference in New Issue
Block a user