mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Build native groups at the legacy conversion seams
This commit is contained in:
@@ -899,6 +899,19 @@ pub trait ExtractArena {
|
||||
fn arena(&self) -> Self::ArenaRef;
|
||||
}
|
||||
|
||||
/// The arena borrowed at the caller's scope, for kernels whose node shape
|
||||
/// cannot carry the evaluation lifetime (a flipped value node takes no fn
|
||||
/// lifetime).
|
||||
pub trait BorrowArena {
|
||||
fn borrow_arena(&self) -> &crate::arena::Arena;
|
||||
}
|
||||
|
||||
impl BorrowArena for ContextImpl<'_> {
|
||||
fn borrow_arena(&self) -> &crate::arena::Arena {
|
||||
ExtractArena::arena(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CtxFamily {
|
||||
type Ctx<'s>: Ctx + DeriveCtx<Family = Self>;
|
||||
}
|
||||
|
||||
@@ -36,13 +36,6 @@ impl Default for Graphic {
|
||||
}
|
||||
}
|
||||
|
||||
// Graphic
|
||||
impl From<List<Graphic>> for Graphic {
|
||||
fn from(graphic: List<Graphic>) -> Self {
|
||||
Graphic::Graphic(graphic)
|
||||
}
|
||||
}
|
||||
|
||||
/// A typed legacy list as a legacy graphic list: each item de-tables to a
|
||||
/// leaf element, keeping its attributes on the containing lane.
|
||||
fn detable_items<T: Clone + Send + Sync + 'static>(list: List<T>, leaf: fn(T) -> Graphic) -> List<Graphic> {
|
||||
@@ -54,19 +47,66 @@ fn detable_items<T: Clone + Send + Sync + 'static>(list: List<T>, leaf: fn(T) ->
|
||||
out
|
||||
}
|
||||
|
||||
/// The element-space coercion into `Graphic`: a leaf converts in place and a
|
||||
/// legacy list becomes a native group built over the arena, so the coercion
|
||||
/// never constructs a legacy interior.
|
||||
pub trait IntoGraphicElement: Clone + Send + Sync + CacheHash + 'static {
|
||||
/// `None` reports arena exhaustion.
|
||||
fn into_graphic_element(self, arena: &core_types::arena::Arena) -> Option<Graphic>;
|
||||
}
|
||||
|
||||
fn list_group<T: Clone + Send + Sync + CacheHash + PartialEq + 'static>(list: List<T>, arena: &core_types::arena::Arena) -> Option<Graphic> {
|
||||
Some(Graphic::Group(core_types::record::Group {
|
||||
row: None,
|
||||
content: core_types::record::GroupItem::from_list(list, arena)?,
|
||||
}))
|
||||
}
|
||||
|
||||
macro_rules! into_graphic_element {
|
||||
($($leaf:ident: $element:ty;)*) => {
|
||||
$(
|
||||
impl IntoGraphicElement for $element {
|
||||
fn into_graphic_element(self, _arena: &core_types::arena::Arena) -> Option<Graphic> {
|
||||
Some(Graphic::$leaf(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGraphicElement for List<$element> {
|
||||
fn into_graphic_element(self, arena: &core_types::arena::Arena) -> Option<Graphic> {
|
||||
list_group(self, arena)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
into_graphic_element! {
|
||||
Vector: Vector;
|
||||
RasterCPU: Raster<CPU>;
|
||||
RasterGPU: Raster<GPU>;
|
||||
Color: Color;
|
||||
Gradient: GradientStops;
|
||||
Text: String;
|
||||
}
|
||||
|
||||
impl IntoGraphicElement for Graphic {
|
||||
fn into_graphic_element(self, _arena: &core_types::arena::Arena) -> Option<Graphic> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGraphicElement for List<Graphic> {
|
||||
fn into_graphic_element(self, arena: &core_types::arena::Arena) -> Option<Graphic> {
|
||||
list_group(self, arena)
|
||||
}
|
||||
}
|
||||
|
||||
// Vector
|
||||
impl From<Vector> for Graphic {
|
||||
fn from(vector: Vector) -> Self {
|
||||
Graphic::Vector(vector)
|
||||
}
|
||||
}
|
||||
impl From<List<Vector>> for Graphic {
|
||||
fn from(vector: List<Vector>) -> Self {
|
||||
Graphic::Graphic(detable_items(vector, Graphic::Vector))
|
||||
}
|
||||
}
|
||||
|
||||
// Note: List<Vector> -> List<Graphic> conversion handled by blanket impl in gcore
|
||||
|
||||
// Raster<CPU>
|
||||
impl From<Raster<CPU>> for Graphic {
|
||||
@@ -74,12 +114,6 @@ impl From<Raster<CPU>> for Graphic {
|
||||
Graphic::RasterCPU(raster)
|
||||
}
|
||||
}
|
||||
impl From<List<Raster<CPU>>> for Graphic {
|
||||
fn from(raster: List<Raster<CPU>>) -> Self {
|
||||
Graphic::Graphic(detable_items(raster, Graphic::RasterCPU))
|
||||
}
|
||||
}
|
||||
// Note: List conversions handled by blanket impl in gcore
|
||||
|
||||
// Raster<GPU>
|
||||
impl From<Raster<GPU>> for Graphic {
|
||||
@@ -87,12 +121,6 @@ impl From<Raster<GPU>> for Graphic {
|
||||
Graphic::RasterGPU(raster)
|
||||
}
|
||||
}
|
||||
impl From<List<Raster<GPU>>> for Graphic {
|
||||
fn from(raster: List<Raster<GPU>>) -> Self {
|
||||
Graphic::Graphic(detable_items(raster, Graphic::RasterGPU))
|
||||
}
|
||||
}
|
||||
// Note: List conversions handled by blanket impl in gcore
|
||||
|
||||
// Color
|
||||
impl From<Color> for Graphic {
|
||||
@@ -100,12 +128,6 @@ impl From<Color> for Graphic {
|
||||
Graphic::Color(color)
|
||||
}
|
||||
}
|
||||
impl From<List<Color>> for Graphic {
|
||||
fn from(color: List<Color>) -> Self {
|
||||
Graphic::Graphic(detable_items(color, Graphic::Color))
|
||||
}
|
||||
}
|
||||
// Note: List conversions handled by blanket impl in gcore
|
||||
// Note: List<Color> -> Option<Color> is in gcore (Color is defined there)
|
||||
|
||||
// GradientStops
|
||||
@@ -114,11 +136,6 @@ impl From<GradientStops> for Graphic {
|
||||
Graphic::Gradient(gradient)
|
||||
}
|
||||
}
|
||||
impl From<List<GradientStops>> for Graphic {
|
||||
fn from(gradient: List<GradientStops>) -> Self {
|
||||
Graphic::Graphic(detable_items(gradient, Graphic::Gradient))
|
||||
}
|
||||
}
|
||||
|
||||
// String
|
||||
impl From<String> for Graphic {
|
||||
@@ -126,11 +143,6 @@ impl From<String> for Graphic {
|
||||
Graphic::Text(text)
|
||||
}
|
||||
}
|
||||
impl From<List<String>> for Graphic {
|
||||
fn from(text: List<String>) -> Self {
|
||||
Graphic::Graphic(detable_items(text, Graphic::Text))
|
||||
}
|
||||
}
|
||||
|
||||
/// Deeply flattens a `List<Graphic>`, collecting only elements matching a specific variant (extracted by `extract_variant`)
|
||||
/// and discarding all other non-matching content. Recursion through `Graphic::Graphic` sub-`List`s composes transforms and opacity.
|
||||
@@ -172,6 +184,11 @@ fn flatten_graphic_list<T>(content: List<Graphic>, extract_variant: fn(Graphic)
|
||||
|
||||
flatten_recursive(output, sub_list, extract_variant, lane_layer_path.as_deref());
|
||||
}
|
||||
// A bridge row's native group flattens through its legacy lowering; the arm dies with the legacy interior.
|
||||
Graphic::Group(group) => {
|
||||
let lowered = List::new_from_item(Item::from_parts(group_to_legacy_graphic(&group), attributes.clone()));
|
||||
flatten_recursive(output, lowered, extract_variant, parent_layer_path);
|
||||
}
|
||||
// A de-tabled leaf is one attr-less element; the extracted row rides with its containing lane's full attributes, paint included.
|
||||
// The enclosing group lane's own layer path overrides, one hop only, matching the native walk.
|
||||
other => {
|
||||
@@ -1086,19 +1103,6 @@ pub fn flatten_vector_rows(level: GraphicLevel<'_>) -> List<Vector> {
|
||||
out
|
||||
}
|
||||
|
||||
/// One typed run as the legacy list its `Render` impl consumes, nested
|
||||
/// groups converted to their legacy form.
|
||||
pub fn run_to_render_list<T: Clone + Send + Sync + 'static>(item: &core_types::record::GroupItem) -> Option<List<T>> {
|
||||
let mut list = run_to_legacy_list::<T>(item)?;
|
||||
if let Some(graphics) = (&mut list as &mut dyn std::any::Any).downcast_mut::<List<Graphic>>() {
|
||||
for element in graphics.iter_element_values_mut() {
|
||||
*element = map_groups_to_legacy(element);
|
||||
}
|
||||
push_lane_paint_into_interiors(graphics);
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
|
||||
/// The transitional paint placement: a lane-level fill or stroke paint
|
||||
/// attribute moves onto the vector interiors the legacy paint readers
|
||||
/// inspect, reaching as far as the pre-flip broadcast did.
|
||||
|
||||
@@ -242,11 +242,11 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
/// The materialized level as its legacy render list.
|
||||
/// The materialized level as its legacy list, content kept native.
|
||||
fn legacy_render_list_of<T: Clone + Send + Sync + 'static>(content: core_types::node::List<'_, T>) -> List<T> {
|
||||
// SAFETY: a materialized input's frames are arena-resident.
|
||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||
graphic_types::graphic::run_to_render_list::<T>(&item).expect("the run holds the row's element type")
|
||||
graphic_types::graphic::run_to_list::<T>(&item).expect("the run holds the row's element type")
|
||||
}
|
||||
|
||||
#[node_macro::node(category("General"), extent(mirror_extent))]
|
||||
@@ -474,10 +474,10 @@ fn wrap_graphic_extent<T>(_content: ListIn<'_, T>, _level: LevelIn) -> GPoll<Ext
|
||||
/// Converts graphical content into a `Graphic` level. A `Graphic` level passes through
|
||||
/// unchanged; a typed level nests as one graphic lane, keeping the pre-flip list
|
||||
/// collapse (`to_graphic_typed` serves those rows). The legacy list rows accept an
|
||||
/// unconverted producer's list value as one element.
|
||||
/// unconverted producer's list value as one element, built as a native group.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub fn to_graphic<T: Into<Graphic> + Clone + Send + Sync + core_types::CacheHash + 'static>(
|
||||
_: impl Ctx,
|
||||
pub fn to_graphic<T: graphic_types::graphic::IntoGraphicElement>(
|
||||
ctx: impl Ctx + core_types::context::BorrowArena,
|
||||
#[implementations(
|
||||
Graphic,
|
||||
List<Graphic>,
|
||||
@@ -489,16 +489,16 @@ pub fn to_graphic<T: Into<Graphic> + Clone + Send + Sync + core_types::CacheHash
|
||||
List<String>,
|
||||
)]
|
||||
content: T,
|
||||
) -> Graphic {
|
||||
content.into()
|
||||
) -> Result<Graphic, Interrupt> {
|
||||
content.into_graphic_element(ctx.borrow_arena()).ok_or_else(|| GraphError::new("the arena is exhausted").into())
|
||||
}
|
||||
|
||||
/// The elementwise `Graphic` coercion the compiler-inserted converts use: each
|
||||
/// lane's element converts on its own, so a typed wire feeds a graphic input
|
||||
/// without changing the level's shape. Registered under the convert identifier.
|
||||
#[node_macro::node(category(""))]
|
||||
pub fn to_graphic_element<T: Into<Graphic> + Clone + Send + Sync + core_types::CacheHash + 'static>(
|
||||
_: impl Ctx,
|
||||
pub fn to_graphic_element<T: graphic_types::graphic::IntoGraphicElement>(
|
||||
ctx: impl Ctx + core_types::context::BorrowArena,
|
||||
#[implementations(
|
||||
Graphic,
|
||||
Vector,
|
||||
@@ -516,8 +516,8 @@ pub fn to_graphic_element<T: Into<Graphic> + Clone + Send + Sync + core_types::C
|
||||
List<String>,
|
||||
)]
|
||||
content: T,
|
||||
) -> Graphic {
|
||||
content.into()
|
||||
) -> Result<Graphic, Interrupt> {
|
||||
content.into_graphic_element(ctx.borrow_arena()).ok_or_else(|| GraphError::new("the arena is exhausted").into())
|
||||
}
|
||||
|
||||
/// The typed-level conversion: the whole level nests as one graphic lane, as
|
||||
@@ -549,8 +549,8 @@ fn to_graphic_unit_extent(_content: core_types::extent::ValueIn<'_, ()>, _level:
|
||||
|
||||
/// The transitional level bridge: the wire's records as the legacy list an
|
||||
/// unconverted consumer expects, attributes copied through their erased
|
||||
/// reads. Registered under the legacy convert identifiers; the rows die with
|
||||
/// the last legacy consumer.
|
||||
/// reads and content kept in its native form. Registered under the legacy
|
||||
/// convert identifiers; the rows die with the last legacy consumer.
|
||||
#[node_macro::node(category(""))]
|
||||
pub fn level_to_list<T: Clone + Send + Sync + CacheHash + 'static>(
|
||||
_: impl Ctx,
|
||||
@@ -559,7 +559,7 @@ pub fn level_to_list<T: Clone + Send + Sync + CacheHash + 'static>(
|
||||
) -> List<T> {
|
||||
// SAFETY: a materialized input's frames are arena-resident.
|
||||
let item = unsafe { core_types::record::GroupItem::from_resident(value.batch()) };
|
||||
graphic_types::graphic::run_to_render_list::<T>(&item).expect("the run holds the row's element type")
|
||||
graphic_types::graphic::run_to_list::<T>(&item).expect("the run holds the row's element type")
|
||||
}
|
||||
|
||||
pub use _level_to_list_mod::level_to_list_entries;
|
||||
|
||||
@@ -128,7 +128,7 @@ fn boolean_operation<'e>(
|
||||
// SAFETY: a materialized input's frames are arena-resident.
|
||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||
let flattened = flatten_vector_run(GraphicLevel::Run(&item), DAffine2::IDENTITY, PaintReach::NONE);
|
||||
let snapshot = graphic_types::graphic::run_to_render_list::<Graphic>(&item)
|
||||
let snapshot = graphic_types::graphic::run_to_list::<Graphic>(&item)
|
||||
.expect("the run holds the row's element type")
|
||||
.into_graphic_list();
|
||||
boolean_core(ctx.arena(), flattened, snapshot, operation)
|
||||
@@ -158,7 +158,7 @@ fn boolean_operation_vector<'e>(
|
||||
// SAFETY: a materialized input's frames are arena-resident.
|
||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||
let flattened = graphic_types::graphic::run_to_list::<Vector>(&item).expect("the run holds vector lanes");
|
||||
let snapshot = graphic_types::graphic::run_to_render_list::<Vector>(&item)
|
||||
let snapshot = graphic_types::graphic::run_to_list::<Vector>(&item)
|
||||
.expect("the run holds the row's element type")
|
||||
.into_graphic_list();
|
||||
boolean_core(ctx.arena(), flattened, snapshot, operation)
|
||||
|
||||
@@ -178,11 +178,11 @@ fn assign_colors_graphic<'e>(
|
||||
if lane >= content.len() {
|
||||
return Err(GraphError::past_end().into());
|
||||
}
|
||||
let mut element = graphic_types::graphic::map_groups_to_legacy(content.element_ref(lane));
|
||||
let original = content.element_ref(lane);
|
||||
let (transform, layer_path) = carried_lane_attrs(ctx.arena(), *content.lane(lane))?;
|
||||
|
||||
if gradient.is_empty() {
|
||||
return Ok((element, transform, layer_path));
|
||||
return Ok((original.clone(), transform, layer_path));
|
||||
}
|
||||
let gradient_element = gradient.element_ref(0);
|
||||
let reversed;
|
||||
@@ -218,30 +218,31 @@ fn assign_colors_graphic<'e>(
|
||||
(entry.offsets[content.len()], entry.offsets[lane])
|
||||
};
|
||||
|
||||
// A de-tabled vector leaf carries no attributes, so the paint rides the
|
||||
// containing lane: a bare leaf wraps into a one-lane list, and a lowered
|
||||
// vector run's leaves take one color per lane.
|
||||
if graphic_types::graphic::direct_vector_len(content.element_ref(lane)) > 0 {
|
||||
let mut children = match element {
|
||||
Graphic::Graphic(children) => children,
|
||||
leaf => List::new_from_element(leaf),
|
||||
};
|
||||
let mut consumed = 0;
|
||||
for index in 0..children.len() {
|
||||
let Some(Graphic::Vector(vector)) = children.element(index) else { continue };
|
||||
let has_stroke = vector.stroke.is_some();
|
||||
let color = assign_color_at(gradient_element, position + consumed, length, randomize, seed, repeat_every);
|
||||
let paint = List::new_from_element(color).into_graphic_list();
|
||||
if fill {
|
||||
set_paint_attribute_at(&mut children, index, ATTR_FILL, paint.clone());
|
||||
// The direct vector rows as a scratch list, one color per row, rebuilt as
|
||||
// a native run; a lane without direct rows passes through untouched.
|
||||
let rows = match original {
|
||||
Graphic::Vector(vector) => Some(List::new_from_element(vector.clone())),
|
||||
Graphic::Group(group) if group.row.is_none() => graphic_types::graphic::run_to_list::<Vector>(&group.content),
|
||||
_ => None,
|
||||
};
|
||||
let element = match rows {
|
||||
Some(mut rows) => {
|
||||
for row in 0..rows.len() {
|
||||
let has_stroke = rows.element(row).is_some_and(|vector| vector.stroke.is_some());
|
||||
let color = assign_color_at(gradient_element, position + row, length, randomize, seed, repeat_every);
|
||||
let paint = List::new_from_element(color).into_graphic_list();
|
||||
if fill {
|
||||
set_paint_attribute_at(&mut rows, row, ATTR_FILL, paint.clone());
|
||||
}
|
||||
if stroke && has_stroke {
|
||||
set_paint_attribute_at(&mut rows, row, ATTR_STROKE, paint.clone());
|
||||
}
|
||||
}
|
||||
if stroke && has_stroke {
|
||||
set_paint_attribute_at(&mut children, index, ATTR_STROKE, paint.clone());
|
||||
}
|
||||
consumed += 1;
|
||||
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 })
|
||||
}
|
||||
element = Graphic::Graphic(children);
|
||||
}
|
||||
None => original.clone(),
|
||||
};
|
||||
|
||||
Ok((element, transform, layer_path))
|
||||
}
|
||||
@@ -1596,7 +1597,7 @@ where
|
||||
{
|
||||
// SAFETY: a materialized input's frames are arena-resident.
|
||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||
graphic_types::graphic::run_to_render_list::<T>(&item)
|
||||
graphic_types::graphic::run_to_list::<T>(&item)
|
||||
.expect("the run holds the row's element type")
|
||||
.into_graphic_list()
|
||||
}
|
||||
@@ -1905,7 +1906,7 @@ pub fn flatten_path<'e>(
|
||||
// SAFETY: a materialized input's frames are arena-resident.
|
||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||
let flattened = graphic_types::graphic::flatten_vector_rows(graphic_types::graphic::GraphicLevel::Run(&item));
|
||||
let snapshot = graphic_types::graphic::run_to_render_list::<Graphic>(&item).expect("the run holds the row's element type");
|
||||
let snapshot = graphic_types::graphic::run_to_list::<Graphic>(&item).expect("the run holds the row's element type");
|
||||
flatten_path_core(ctx.arena(), flattened, snapshot)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user