mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-28 21:58:11 +08:00
Look up paint through the paint markers instead of string keys
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
use crate::markers::{ATTR_FILL, ATTR_STROKE};
|
use crate::markers::{ATTR_FILL, ATTR_STROKE, Fill, Stroke};
|
||||||
|
use core_types::attribute::Attribute;
|
||||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||||
use core_types::graphene_hash::CacheHash;
|
use core_types::graphene_hash::CacheHash;
|
||||||
|
use core_types::lane::LaneSource;
|
||||||
use core_types::list::{AttributeValueDyn, Item, ItemAttributeValues, List};
|
use core_types::list::{AttributeValueDyn, Item, ItemAttributeValues, List};
|
||||||
use core_types::ops::{FromAnchorPosition, ListConvert};
|
use core_types::ops::{FromAnchorPosition, ListConvert};
|
||||||
use core_types::render_complexity::RenderComplexity;
|
use core_types::render_complexity::RenderComplexity;
|
||||||
@@ -200,22 +202,40 @@ pub fn is_paint_present(graphic_list: &List<Graphic>) -> bool {
|
|||||||
|
|
||||||
/// Look up the paint graphics stored under attribute for a vector item, in the canonical `List<Graphic>` form.
|
/// Look up the paint graphics stored under attribute for a vector item, in the canonical `List<Graphic>` form.
|
||||||
pub fn graphic_list_at<'a>(list: &'a List<Vector>, index: usize, attribute: &str) -> Option<Cow<'a, List<Graphic>>> {
|
pub fn graphic_list_at<'a>(list: &'a List<Vector>, index: usize, attribute: &str) -> Option<Cow<'a, List<Graphic>>> {
|
||||||
paint_at(list, index, attribute)
|
list.attribute::<Option<List<Graphic>>>(attribute, index)
|
||||||
|
.and_then(|optional| optional.as_ref())
|
||||||
.map(Cow::Borrowed)
|
.map(Cow::Borrowed)
|
||||||
// Treat a blank paint attribute as absent so an empty attribute doesn't count as painted
|
// Treat a blank paint attribute as absent so an empty attribute doesn't count as painted
|
||||||
.filter(|graphic_list| is_paint_present(graphic_list))
|
.filter(|graphic_list| is_paint_present(graphic_list))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The paint attribute's list. Storage is the paint marker's owned
|
|
||||||
/// `Option<List<Graphic>>` form, which every writer produces.
|
|
||||||
fn paint_at<'a, T>(list: &'a List<T>, index: usize, attribute: &str) -> Option<&'a List<Graphic>> {
|
|
||||||
list.attribute::<Option<List<Graphic>>>(attribute, index).and_then(|optional| optional.as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the item carries a non-blank canonical `List<Graphic>` paint attribute,
|
/// Whether the item carries a non-blank canonical `List<Graphic>` paint attribute,
|
||||||
/// checked by borrowing without cloning the renderable list.
|
/// checked by borrowing without cloning the renderable list.
|
||||||
pub fn has_paint_at(list: &List<Vector>, index: usize, attribute: &str) -> bool {
|
pub fn has_paint_at(list: &List<Vector>, index: usize, attribute: &str) -> bool {
|
||||||
paint_at(list, index, attribute).is_some_and(is_paint_present)
|
graphic_list_at(list, index, attribute).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look up the paint graphics stored under the marker `A`, in the canonical `List<Graphic>` form.
|
||||||
|
pub fn paint_graphics<'a, A, S>(source: &'a S, index: usize) -> Option<Cow<'a, List<Graphic>>>
|
||||||
|
where
|
||||||
|
S: LaneSource,
|
||||||
|
A: Attribute<Value<'a> = Option<&'a List<Graphic>>>,
|
||||||
|
{
|
||||||
|
source
|
||||||
|
.attr::<A>(index)
|
||||||
|
.map(Cow::Borrowed)
|
||||||
|
// Treat a blank paint attribute as absent so an empty attribute doesn't count as painted
|
||||||
|
.filter(|graphic_list| is_paint_present(graphic_list))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the item carries a non-blank canonical `List<Graphic>` paint under the marker `A`,
|
||||||
|
/// checked by borrowing without cloning the renderable list.
|
||||||
|
pub fn has_paint<'a, A, S>(source: &'a S, index: usize) -> bool
|
||||||
|
where
|
||||||
|
S: LaneSource,
|
||||||
|
A: Attribute<Value<'a> = Option<&'a List<Graphic>>>,
|
||||||
|
{
|
||||||
|
paint_graphics::<A, S>(source, index).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores a paint attribute in the paint marker's owned form, the only representation paint readers accept.
|
/// Stores a paint attribute in the paint marker's owned form, the only representation paint readers accept.
|
||||||
@@ -448,10 +468,10 @@ impl Graphic {
|
|||||||
let Some(element) = vector.element(index) else { return false };
|
let Some(element) = vector.element(index) else { return false };
|
||||||
let opacity: f64 = vector.attribute_cloned_or(ATTR_OPACITY, index, 1.);
|
let opacity: f64 = vector.attribute_cloned_or(ATTR_OPACITY, index, 1.);
|
||||||
|
|
||||||
let fill_opaque_or_absent = graphic_list_at(vector, index, ATTR_FILL).is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_opaque()));
|
let fill_opaque_or_absent = paint_graphics::<Fill, _>(vector, index).is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_opaque()));
|
||||||
|
|
||||||
let stroke_invisible_or_transparent = element.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
let stroke_invisible_or_transparent = element.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
||||||
|| graphic_list_at(vector, index, ATTR_STROKE).is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_fully_transparent()));
|
|| paint_graphics::<Stroke, _>(vector, index).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
|
||||||
}),
|
}),
|
||||||
@@ -463,15 +483,17 @@ impl Graphic {
|
|||||||
match self {
|
match self {
|
||||||
Graphic::Graphic(list) => !list.is_empty() && list.iter_element_values().all(Graphic::is_opaque),
|
Graphic::Graphic(list) => !list.is_empty() && list.iter_element_values().all(Graphic::is_opaque),
|
||||||
Graphic::Vector(list) => {
|
Graphic::Vector(list) => {
|
||||||
let is_paint_opaque_at = |key: &str, index: usize| graphic_list_at(list, index, key).is_some_and(|graphic_list| graphic_list.element(0).is_some_and(|graphic| graphic.is_opaque()));
|
fn is_paint_opaque_at<'a, A: Attribute<Value<'a> = Option<&'a List<Graphic>>>>(list: &'a List<Vector>, index: usize) -> bool {
|
||||||
|
paint_graphics::<A, _>(list, index).is_some_and(|graphic_list| graphic_list.element(0).is_some_and(|graphic| graphic.is_opaque()))
|
||||||
|
}
|
||||||
|
|
||||||
!list.is_empty()
|
!list.is_empty()
|
||||||
&& (0..list.len()).all(|i| {
|
&& (0..list.len()).all(|i| {
|
||||||
let Some(vector) = list.element(i) else { return false };
|
let Some(vector) = list.element(i) else { return false };
|
||||||
let opacity: f64 = list.attribute_cloned_or(ATTR_OPACITY, i, 1.);
|
let opacity: f64 = list.attribute_cloned_or(ATTR_OPACITY, i, 1.);
|
||||||
let opacity_fill: f64 = list.attribute_cloned_or(ATTR_OPACITY_FILL, i, 1.);
|
let opacity_fill: f64 = list.attribute_cloned_or(ATTR_OPACITY_FILL, i, 1.);
|
||||||
let fill_opaque = opacity_fill >= 1. - f64::EPSILON && is_paint_opaque_at(ATTR_FILL, i);
|
let fill_opaque = opacity_fill >= 1. - f64::EPSILON && is_paint_opaque_at::<Fill>(list, i);
|
||||||
let stroke_opaque_or_invisible = vector.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke()) || is_paint_opaque_at(ATTR_STROKE, i);
|
let stroke_opaque_or_invisible = vector.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke()) || is_paint_opaque_at::<Stroke>(list, i);
|
||||||
opacity >= 1. - f64::EPSILON && fill_opaque && stroke_opaque_or_invisible
|
opacity >= 1. - f64::EPSILON && fill_opaque && stroke_opaque_or_invisible
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -487,16 +509,17 @@ impl Graphic {
|
|||||||
Graphic::Graphic(list) => list.iter_element_values().all(Graphic::is_fully_transparent),
|
Graphic::Graphic(list) => list.iter_element_values().all(Graphic::is_fully_transparent),
|
||||||
Graphic::Vector(list) => (0..list.len()).all(|i| {
|
Graphic::Vector(list) => (0..list.len()).all(|i| {
|
||||||
let Some(vector) = list.element(i) else { return false };
|
let Some(vector) = list.element(i) else { return false };
|
||||||
let is_paint_fully_transparent_at =
|
fn is_paint_fully_transparent_at<'a, A: Attribute<Value<'a> = Option<&'a List<Graphic>>>>(list: &'a List<Vector>, index: usize) -> bool {
|
||||||
|key: &str, index: usize| graphic_list_at(list, index, key).is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_fully_transparent()));
|
paint_graphics::<A, _>(list, index).is_none_or(|graphic_list| graphic_list.element(0).is_none_or(|graphic| graphic.is_fully_transparent()))
|
||||||
|
}
|
||||||
|
|
||||||
let opacity: f64 = list.attribute_cloned_or(ATTR_OPACITY, i, 1.);
|
let opacity: f64 = list.attribute_cloned_or(ATTR_OPACITY, i, 1.);
|
||||||
if opacity <= f64::EPSILON {
|
if opacity <= f64::EPSILON {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let opacity_fill: f64 = list.attribute_cloned_or(ATTR_OPACITY_FILL, i, 1.);
|
let opacity_fill: f64 = list.attribute_cloned_or(ATTR_OPACITY_FILL, i, 1.);
|
||||||
let fill_invisible = opacity_fill <= f64::EPSILON || is_paint_fully_transparent_at(ATTR_FILL, i);
|
let fill_invisible = opacity_fill <= f64::EPSILON || is_paint_fully_transparent_at::<Fill>(list, i);
|
||||||
let stroke_invisible = vector.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke()) || is_paint_fully_transparent_at(ATTR_STROKE, i);
|
let stroke_invisible = vector.stroke.as_ref().is_none_or(|stroke| !stroke.has_renderable_stroke()) || is_paint_fully_transparent_at::<Stroke>(list, i);
|
||||||
fill_invisible && stroke_invisible
|
fill_invisible && stroke_invisible
|
||||||
}),
|
}),
|
||||||
Graphic::Color(list) => list.iter_element_values().all(|color| color.a() == 0.),
|
Graphic::Color(list) => list.iter_element_values().all(|color| color.a() == 0.),
|
||||||
@@ -713,7 +736,8 @@ pub fn run_to_render_list<T: Clone + Send + Sync + 'static>(item: &core_types::r
|
|||||||
fn push_lane_paint_into_interiors(list: &mut List<Graphic>) {
|
fn push_lane_paint_into_interiors(list: &mut List<Graphic>) {
|
||||||
for index in 0..list.len() {
|
for index in 0..list.len() {
|
||||||
for key in [ATTR_FILL, ATTR_STROKE] {
|
for key in [ATTR_FILL, ATTR_STROKE] {
|
||||||
let Some(paint) = paint_at(list, index, key).filter(|paint| is_paint_present(paint)).cloned() else {
|
let stored = list.attribute::<Option<List<Graphic>>>(key, index).and_then(|optional| optional.as_ref());
|
||||||
|
let Some(paint) = stored.filter(|paint| is_paint_present(paint)).cloned() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let Some(element) = list.element_mut(index) else { continue };
|
let Some(element) = list.element_mut(index) else { continue };
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ fn boolean_core<'e>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let element = result_vector_list.element(0).cloned().unwrap_or_default();
|
let element = result_vector_list.element(0).cloned().unwrap_or_default();
|
||||||
let fill = park_paint(graphic_types::graphic::graphic_list_at(&result_vector_list, 0, graphic_types::ATTR_FILL).map(|paint| paint.into_owned()))?;
|
let fill = park_paint(graphic_types::graphic::paint_graphics::<Fill, _>(&result_vector_list, 0).map(|paint| paint.into_owned()))?;
|
||||||
let stroke = park_paint(graphic_types::graphic::graphic_list_at(&result_vector_list, 0, graphic_types::ATTR_STROKE).map(|paint| paint.into_owned()))?;
|
let stroke = park_paint(graphic_types::graphic::paint_graphics::<Stroke, _>(&result_vector_list, 0).map(|paint| paint.into_owned()))?;
|
||||||
let layer_path: Vec<NodeId> = result_vector_list.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, 0).map(|path| path.clone()).unwrap_or_default();
|
let layer_path: Vec<NodeId> = result_vector_list.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, 0).map(|path| path.clone()).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
|
||||||
|
|||||||
@@ -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::graphic::{bake_paint_transforms, graphic_list_at, has_paint_at, is_paint_present, set_paint_attribute_at};
|
use graphic_types::graphic::{bake_paint_transforms, has_paint, is_paint_present, paint_graphics, set_paint_attribute_at};
|
||||||
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke as StrokeAttr};
|
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke as StrokeAttr};
|
||||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||||
use graphic_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_FILL, ATTR_STROKE, Graphic, IntoGraphicList};
|
use graphic_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_FILL, ATTR_STROKE, Graphic, IntoGraphicList};
|
||||||
@@ -1367,7 +1367,7 @@ fn solidify_stroke_core(graphic_list: List<Graphic>) -> List<Vector> {
|
|||||||
let flattened: List<Vector> = graphic_list.clone().into_flattened_list();
|
let flattened: List<Vector> = graphic_list.clone().into_flattened_list();
|
||||||
|
|
||||||
// A fill exists when the canonical attribute carries paint
|
// A fill exists when the canonical attribute carries paint
|
||||||
let has_fills: Vec<bool> = (0..flattened.len()).map(|index| has_paint_at(&flattened, index, ATTR_FILL)).collect();
|
let has_fills: Vec<bool> = (0..flattened.len()).map(|index| has_paint::<Fill, _>(&flattened, index)).collect();
|
||||||
|
|
||||||
let mut output: List<Vector> = flattened
|
let mut output: List<Vector> = flattened
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -3038,13 +3038,13 @@ fn morph_core(content: List<Graphic>, progression: f64, reverse: bool, distribut
|
|||||||
let mut vector = Vector { stroke, ..Default::default() };
|
let mut vector = Vector { stroke, ..Default::default() };
|
||||||
|
|
||||||
let fill_paint = {
|
let fill_paint = {
|
||||||
let source = graphic_list_at(&content, source_index, ATTR_FILL);
|
let source = paint_graphics::<Fill, _>(&content, source_index);
|
||||||
let target = graphic_list_at(&content, target_index, ATTR_FILL);
|
let target = paint_graphics::<Fill, _>(&content, target_index);
|
||||||
lerp_graphic(source.as_deref(), target.as_deref(), time)
|
lerp_graphic(source.as_deref(), target.as_deref(), time)
|
||||||
};
|
};
|
||||||
let stroke_paint = {
|
let stroke_paint = {
|
||||||
let source = graphic_list_at(&content, source_index, ATTR_STROKE);
|
let source = paint_graphics::<StrokeAttr, _>(&content, source_index);
|
||||||
let target = graphic_list_at(&content, target_index, ATTR_STROKE);
|
let target = paint_graphics::<StrokeAttr, _>(&content, target_index);
|
||||||
lerp_graphic(source.as_deref(), target.as_deref(), time)
|
lerp_graphic(source.as_deref(), target.as_deref(), time)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3912,7 +3912,7 @@ mod test {
|
|||||||
|
|
||||||
let morphed = super::morph_core(content.into_graphic_list(), 0.5, false, InterpolationDistribution::default(), List::default());
|
let morphed = super::morph_core(content.into_graphic_list(), 0.5, false, InterpolationDistribution::default(), List::default());
|
||||||
|
|
||||||
let fill = graphic_list_at(&morphed, 0, ATTR_FILL).expect("Morph should keep the fill paint at the midpoint");
|
let fill = paint_graphics::<Fill, _>(&morphed, 0).expect("Morph should keep the fill paint at the midpoint");
|
||||||
|
|
||||||
// Interpolated color between red and blue should have >0 value on both R and B
|
// Interpolated color between red and blue should have >0 value on both R and B
|
||||||
let Some(Graphic::Color(colors)) = fill.element(0) else {
|
let Some(Graphic::Color(colors)) = fill.element(0) else {
|
||||||
|
|||||||
Reference in New Issue
Block a user