mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 06:28:12 +08:00
Implement clipping masks, stroke align, and stroke paint order (#2644)
* refactor: opacity + blend_mode -> blend_style * Add code for clipping * Add alt-click masking * Clip to all colors. Fill option * Fix undo not working. Fix strokes not being white * Allow clipped to be grouped or raster * Switch to alpha mode in mask-type * add plumbing to know if clipped in frontend and add fill slider * Attempt at document upgrade code * Fix fill slider * Add clipped styling and Alt-click layer border * Use mask attr judiciously by using clip when possible * Fix breaking documents and upgrade code * Fix fixes * No-op toggle if last child of parent and don't show clip UI if last element * Fix mouse styles by plumbing clippable to frontend * Fix Clip detection by disallowed groups as clipPath according to SVG spec doesn't allow <g> * Add opacity to clippers can_use_clip check * Fix issue with clipping not working nicely with strokes by using masks * Add vello code * cleanup * Add stroke alignment hacks to SVG renderer * svg: Fix mask bounds in vector data * vello: Implement mask hacks to support stroke alignment * Move around alignment and doc upgrade code * rename Line X -> X * An attempt at fixing names not updating * svg: add stroke order with svg * vello: add stroke order with by calling one before the other explicitly * fix merge * fix svg renderer messing up transform det * Code review; reorder and rename parameters (TODO: fix tools) * Fixes to previous * Formatting * fix bug 3 * some moving around (not fixed) * fix issue 1 * fix vello * Final code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
8a3f133140
commit
e238753a35
@@ -14,9 +14,12 @@ pub mod renderer;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, DynAny, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[serde(default)]
|
||||
pub struct AlphaBlending {
|
||||
pub opacity: f32,
|
||||
pub blend_mode: BlendMode,
|
||||
pub opacity: f32,
|
||||
pub fill: f32,
|
||||
pub clip: bool,
|
||||
}
|
||||
impl Default for AlphaBlending {
|
||||
fn default() -> Self {
|
||||
@@ -26,13 +29,22 @@ impl Default for AlphaBlending {
|
||||
impl core::hash::Hash for AlphaBlending {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.opacity.to_bits().hash(state);
|
||||
self.fill.to_bits().hash(state);
|
||||
self.blend_mode.hash(state);
|
||||
self.clip.hash(state);
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AlphaBlending {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let round = |x: f32| (x * 1e3).round() / 1e3;
|
||||
write!(f, "Opacity: {}% — Blend Mode: {}", round(self.opacity * 100.), self.blend_mode)
|
||||
write!(
|
||||
f,
|
||||
"Blend Mode: {} — Opacity: {}% — Fill: {}% — Clip: {}",
|
||||
self.blend_mode,
|
||||
round(self.opacity * 100.),
|
||||
round(self.fill * 100.),
|
||||
if self.clip { "Yes" } else { "No" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +52,9 @@ impl AlphaBlending {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
opacity: 1.,
|
||||
fill: 1.,
|
||||
blend_mode: BlendMode::Normal,
|
||||
clip: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +63,9 @@ impl AlphaBlending {
|
||||
|
||||
AlphaBlending {
|
||||
opacity: lerp(self.opacity, other.opacity, t),
|
||||
fill: lerp(self.fill, other.fill, t),
|
||||
blend_mode: if t < 0.5 { self.blend_mode } else { other.blend_mode },
|
||||
clip: if t < 0.5 { self.clip } else { other.clip },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,6 +221,26 @@ impl GraphicElement {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn had_clip_enabled(&self) -> bool {
|
||||
match self {
|
||||
GraphicElement::VectorData(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip),
|
||||
GraphicElement::GraphicGroup(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip),
|
||||
GraphicElement::RasterDataCPU(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip),
|
||||
GraphicElement::RasterDataGPU(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_reduce_to_clip_path(&self) -> bool {
|
||||
match self {
|
||||
GraphicElement::VectorData(vector_data_table) => vector_data_table.instance_ref_iter().all(|instance_data| {
|
||||
let style = &instance_data.instance.style;
|
||||
let alpha_blending = &instance_data.alpha_blending;
|
||||
(alpha_blending.opacity > 1. - f32::EPSILON) && style.fill().is_opaque() && style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
||||
}),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // TODO: Rename to Raster
|
||||
@@ -448,8 +484,10 @@ async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTabl
|
||||
instance: current_element.instance.clone(),
|
||||
transform: *current_instance.transform * *current_element.transform,
|
||||
alpha_blending: AlphaBlending {
|
||||
opacity: current_instance.alpha_blending.opacity * current_element.alpha_blending.opacity,
|
||||
blend_mode: current_element.alpha_blending.blend_mode,
|
||||
opacity: current_instance.alpha_blending.opacity * current_element.alpha_blending.opacity,
|
||||
fill: current_element.alpha_blending.fill,
|
||||
clip: current_element.alpha_blending.clip,
|
||||
},
|
||||
source_node_id: reference,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user