Add renderer support for painting vector with "fill" and "stroke" attributes with graphic List<T> types (#4111)

* Add conversion from Fill to Table<Graphic>

* Refactor Vector vello renderer for Gradient / Color

* Refactor Vector SVG renderer for Gradient / Color

* Fix conflicts

* Add basic clipping-based fill for SVG rendering

* Use Cow to avoid cloning graphic list for fill

* Cleanup for Cow usage

* format code

* Use `<pattern>` instead of `<clipPath>` for clip

This simplifies the future implementation of clipping-based rendering
for strokes, as the stroke does not support the use of a clip path but
rather paint sources from a paint server.

* Move svg pattern rendering function to RenderExt

* Fix comment

* Fix empty fill list rendering as default black

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Move opaque check function to Graphic impl

* Add color converter and debug node to use graphic

* WIP: Use List<Graphic> to render Color & Gradient

* Use `Arc<List<Vector>>` for vector_data metadata

This exposes List's attributes to message handlers, enabling them to
access the necessary attribute data such as ATTR_STROKE_PAINT_GRAPHIC
as `Fill` and `Stroke` will not have paint information in the future.

* Recurse opacity checks on nested `Graphic`

Also extracts `fill_graphic_list_at` /
`stroke_paint_graphic_list_at` to share the row-attribute
lookup across the existing call sites.

* Fix fill and stroke visibility check degradation

* Fix clipping based stroke paint positioning

* Refactor vello renderer for stroke to use graphic

* Reduce `Fill` / `Stroke.color` to `List<Graphic>` allocations

* Revert "Use `Arc<List<Vector>>` for vector_data metadata"

This reverts commit 4285243a5d

* Expose paint row attributes as dedicated metadata for vectors

Add `fill_attributes` / `stroke_paint_attributes` to `DocumentMetadata`
so the `ExpandFillStrokeOnSelectedLayers` handler can read row paint
visibility without exposing entire `List<Vector>`.

* Fix transparency check to consider fill opacity

* Fix consistency of gradient placement for SVG stroke

* Rename `stroke_paint_..` to `stroke_..`

* Remove debug nodes

* Allow to use any graphic type without casting

* Rename `fill_graphic` / `stroke_graphic` to `fill` / `stroke`

* Fix SVG pattern placement when stroke transform differs from item transform

* Fix click target fill check for empty list in graphic

* Fix blank fill/stroke attribute masking legacy style

* Fix SVG's paint order trick for vector/raster fills

* Add zero-division guard for pattern wraparound prevention

* Code review

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
YohYamasaki
2026-06-11 21:50:27 +02:00
committed by Timon
parent e524dff1a7
commit 2b8ef42086
15 changed files with 976 additions and 259 deletions

View File

@@ -3,7 +3,7 @@ use core::f64::consts::{PI, TAU};
use core::hash::{Hash, Hasher};
use core_types::blending::BlendMode;
use core_types::bounds::{BoundingBox, RenderBoundingBox};
use core_types::list::{Item, List, ListDyn};
use core_types::list::{ATTR_FILL, ATTR_STROKE, Item, List, ListDyn};
use core_types::registry::types::{Angle, Length, Multiplier, Percentage, PixelLength, Progression, SeedValue};
use core_types::transform::{Footprint, Transform};
use core_types::uuid::NodeId;
@@ -1224,13 +1224,22 @@ async fn solidify_stroke<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[
}
// If the original vector has a fill, preserve it as a separate item with the stroke cleared.
let has_fill = !vector.style.fill().is_none();
let has_attr_fill = attributes.keys().any(|k| k == ATTR_FILL);
let has_fill = has_attr_fill || !vector.style.fill().is_none();
let fill_row = has_fill.then(|| {
vector.style.clear_stroke();
Item::from_parts(vector, attributes.clone())
let mut fill_attributes = attributes.clone();
// No stroke remains on the fill row
fill_attributes.remove::<List<Graphic>>(ATTR_STROKE);
Item::from_parts(vector, fill_attributes)
});
let stroke_row = Item::from_parts(solidified_stroke, attributes);
let mut stroke_attributes = attributes;
// Drop the original fill and use the stroke paint to fill the outlined stroke
stroke_attributes.remove::<List<Graphic>>(ATTR_FILL);
stroke_attributes.rename(ATTR_STROKE, ATTR_FILL);
let stroke_row = Item::from_parts(solidified_stroke, stroke_attributes);
// Ordering based on the paint order. The first item in the `List` is rendered below the second.
match paint_order {