mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 23:58:13 +08:00
Fix conflicts
This commit is contained in:
@@ -77,6 +77,9 @@ pub const ATTR_SPREAD_METHOD: &str = "spread_method";
|
|||||||
/// Gradient's `GradientType` (`Linear` or `Radial`).
|
/// Gradient's `GradientType` (`Linear` or `Radial`).
|
||||||
pub const ATTR_GRADIENT_TYPE: &str = "gradient_type";
|
pub const ATTR_GRADIENT_TYPE: &str = "gradient_type";
|
||||||
|
|
||||||
|
/// Table<Graphic> data for fill.
|
||||||
|
pub const ATTR_FILL_GRAPHIC: &str = "fill_graphic";
|
||||||
|
|
||||||
// ========================
|
// ========================
|
||||||
// TRAIT: AnyAttributeValue
|
// TRAIT: AnyAttributeValue
|
||||||
// ========================
|
// ========================
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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::list::List;
|
use core_types::list::{Item, List};
|
||||||
use core_types::ops::ListConvert;
|
use core_types::ops::ListConvert;
|
||||||
use core_types::render_complexity::RenderComplexity;
|
use core_types::render_complexity::RenderComplexity;
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
@@ -170,20 +170,20 @@ fn flatten_graphic_list<T>(content: List<Graphic>, extract_variant: fn(Graphic)
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a `Fill` enum into the `Table<Graphic>` representation used as paint storage.
|
/// Converts a `Fill` enum into the `List<Graphic>` representation used as paint storage.
|
||||||
/// TODO: Remove once all paint sources flow through `Table<Graphic>` directly without going through the `Fill` enum.
|
/// TODO: Remove once all paint sources flow through `List<Graphic>` directly without going through the `Fill` enum.
|
||||||
pub fn fill_to_paint(fill: &Fill) -> Option<Table<Graphic>> {
|
pub fn fill_to_graphic_list(fill: &Fill) -> Option<List<Graphic>> {
|
||||||
match fill {
|
match fill {
|
||||||
Fill::None => None,
|
Fill::None => None,
|
||||||
Fill::Solid(color) => Some(Table::new_from_element((*color).into())),
|
Fill::Solid(color) => Some(List::new_from_element((*color).into())),
|
||||||
Fill::Gradient(gradient) => {
|
Fill::Gradient(gradient) => {
|
||||||
let gradient_row = TableRow::new_from_element(gradient.stops.clone())
|
let gradient_row = Item::new_from_element(gradient.stops.clone())
|
||||||
.with_attribute(ATTR_TRANSFORM, gradient.to_transform())
|
.with_attribute(ATTR_TRANSFORM, gradient.to_transform())
|
||||||
.with_attribute(ATTR_GRADIENT_TYPE, gradient.gradient_type)
|
.with_attribute(ATTR_GRADIENT_TYPE, gradient.gradient_type)
|
||||||
.with_attribute(ATTR_SPREAD_METHOD, gradient.spread_method);
|
.with_attribute(ATTR_SPREAD_METHOD, gradient.spread_method);
|
||||||
let gradient_table = Table::new_from_row(gradient_row);
|
let gradient_list = List::new_from_item(gradient_row);
|
||||||
|
|
||||||
Some(Table::new_from_element(Graphic::Gradient(gradient_table)))
|
Some(List::new_from_element(Graphic::Gradient(gradient_list)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::renderer::{RenderParams, format_transform_matrix};
|
use crate::renderer::{RenderParams, format_transform_matrix};
|
||||||
use core_types::table::Table;
|
use core_types::list::List;
|
||||||
use core_types::color::SRGBA8;
|
use core_types::color::SRGBA8;
|
||||||
use core_types::uuid::generate_uuid;
|
use core_types::uuid::generate_uuid;
|
||||||
use core_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD, ATTR_TRANSFORM, Color};
|
use core_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD, ATTR_TRANSFORM, Color};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphic_types::Graphic;
|
use graphic_types::Graphic;
|
||||||
use graphic_types::graphic::fill_to_paint;
|
use graphic_types::graphic::fill_to_graphic_list;
|
||||||
use graphic_types::vector_types::gradient::GradientType;
|
use graphic_types::vector_types::gradient::GradientType;
|
||||||
use graphic_types::vector_types::vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
use graphic_types::vector_types::vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
@@ -17,7 +17,7 @@ pub trait RenderExt {
|
|||||||
fn render(&self, svg_defs: &mut String, element_transform: DAffine2, stroke_transform: DAffine2, bounds: DAffine2, transformed_bounds: DAffine2, render_params: &RenderParams) -> Self::Output;
|
fn render(&self, svg_defs: &mut String, element_transform: DAffine2, stroke_transform: DAffine2, bounds: DAffine2, transformed_bounds: DAffine2, render_params: &RenderParams) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderExt for Table<Color> {
|
impl RenderExt for List<Color> {
|
||||||
type Output = String;
|
type Output = String;
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
@@ -40,7 +40,7 @@ impl RenderExt for Table<Color> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderExt for Table<GradientStops> {
|
impl RenderExt for List<GradientStops> {
|
||||||
type Output = u64;
|
type Output = u64;
|
||||||
|
|
||||||
/// Adds the gradient def through mutating the first argument, returning the gradient ID.
|
/// Adds the gradient def through mutating the first argument, returning the gradient ID.
|
||||||
@@ -118,13 +118,13 @@ impl RenderExt for Fill {
|
|||||||
|
|
||||||
/// Renders the fill, adding necessary defs through mutating the first argument.
|
/// Renders the fill, adding necessary defs through mutating the first argument.
|
||||||
fn render(&self, svg_defs: &mut String, element_transform: DAffine2, stroke_transform: DAffine2, bounds: DAffine2, transformed_bounds: DAffine2, render_params: &RenderParams) -> Self::Output {
|
fn render(&self, svg_defs: &mut String, element_transform: DAffine2, stroke_transform: DAffine2, bounds: DAffine2, transformed_bounds: DAffine2, render_params: &RenderParams) -> Self::Output {
|
||||||
let Some(paint_table) = fill_to_paint(self) else { return r#" fill="none""#.to_string() };
|
let Some(paint_list) = fill_to_graphic_list(self) else { return r#" fill="none""#.to_string() };
|
||||||
let Some(paint) = paint_table.element(0) else { return String::new() };
|
let Some(paint) = paint_list.element(0) else { return String::new() };
|
||||||
|
|
||||||
match paint {
|
match paint {
|
||||||
Graphic::Color(color_table) => color_table.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params),
|
Graphic::Color(color_list) => color_list.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params),
|
||||||
Graphic::Gradient(stops_table) => {
|
Graphic::Gradient(stops_list) => {
|
||||||
let gradient_id = stops_table.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params);
|
let gradient_id = stops_list.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params);
|
||||||
format!(r##" fill="url('#{gradient_id}')""##)
|
format!(r##" fill="url('#{gradient_id}')""##)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use core_types::bounds::BoundingBox;
|
|||||||
use core_types::bounds::RenderBoundingBox;
|
use core_types::bounds::RenderBoundingBox;
|
||||||
use core_types::color::Color;
|
use core_types::color::Color;
|
||||||
use core_types::color::SRGBA8;
|
use core_types::color::SRGBA8;
|
||||||
use core_types::list::{Item, List};
|
use core_types::list::{ATTR_FILL_GRAPHIC, Item, List};
|
||||||
use core_types::math::quad::Quad;
|
use core_types::math::quad::Quad;
|
||||||
use core_types::render_complexity::RenderComplexity;
|
use core_types::render_complexity::RenderComplexity;
|
||||||
use core_types::transform::Footprint;
|
use core_types::transform::Footprint;
|
||||||
@@ -18,7 +18,7 @@ use core_types::{
|
|||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_hash::CacheHashWrapper;
|
use graphene_hash::CacheHashWrapper;
|
||||||
use graphic_types::graphic::fill_to_paint;
|
use graphic_types::graphic::fill_to_graphic_list;
|
||||||
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster};
|
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster};
|
||||||
use graphic_types::vector_types::gradient::{GradientStops, GradientType};
|
use graphic_types::vector_types::gradient::{GradientStops, GradientType};
|
||||||
use graphic_types::vector_types::subpath::Subpath;
|
use graphic_types::vector_types::subpath::Subpath;
|
||||||
@@ -1111,7 +1111,7 @@ impl Render for List<Vector> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
use graphic_types::vector_types::vector::style::{GradientType, StrokeCap, StrokeJoin};
|
use graphic_types::vector_types::vector::style::{GradientType, StrokeCap, StrokeJoin};
|
||||||
|
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
@@ -1182,26 +1182,32 @@ impl Render for List<Vector> {
|
|||||||
let use_layer = can_draw_aligned_stroke;
|
let use_layer = can_draw_aligned_stroke;
|
||||||
let wants_stroke_below = stroke.as_ref().is_some_and(|s| s.paint_order == vector::style::PaintOrder::StrokeBelow);
|
let wants_stroke_below = stroke.as_ref().is_some_and(|s| s.paint_order == vector::style::PaintOrder::StrokeBelow);
|
||||||
|
|
||||||
// TODO: This conversion is only necessary during the transition period from Fill to Table<Graphic>
|
let do_fill_path = |scene: &mut Scene, context: &mut RenderContext, path: &kurbo::BezPath, fill_rule: peniko::Fill| {
|
||||||
let do_fill_path = |scene: &mut Scene, path: &kurbo::BezPath, fill_rule: peniko::Fill| {
|
// Try to use ATTR_FILL_GRAPHIC attribute, which is set by `fill_graphic` debug node, then fall back to Fill enum.
|
||||||
let Some(paint_table) = fill_to_paint(element.style.fill()) else {
|
// TODO: Drop the Fill fall back once the Fill node becomes ready to store corresponding Graphic list directly.
|
||||||
|
let Some(fill_graphic) = self
|
||||||
|
.attribute::<List<Graphic>>(ATTR_FILL_GRAPHIC, index)
|
||||||
|
.filter(|t| !t.is_empty())
|
||||||
|
.cloned()
|
||||||
|
.or_else(|| fill_to_graphic_list(element.style.fill()))
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
for paint_idx in 0..paint_table.len() {
|
for paint_idx in 0..fill_graphic.len() {
|
||||||
let Some(paint) = paint_table.element(paint_idx) else { continue };
|
let Some(paint) = fill_graphic.element(paint_idx) else { continue };
|
||||||
match paint {
|
match paint {
|
||||||
Graphic::Color(table) => {
|
Graphic::Color(list) => {
|
||||||
let Some(color) = table.element(0) else { continue };
|
let Some(color) = list.element(0) else { continue };
|
||||||
|
|
||||||
let fill = peniko::Brush::Solid(SRGBA8::from(*color).to_peniko_color());
|
let fill = peniko::Brush::Solid(SRGBA8::from(*color).to_peniko_color());
|
||||||
scene.fill(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), &fill, None, path);
|
scene.fill(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), &fill, None, path);
|
||||||
}
|
}
|
||||||
Graphic::Gradient(stops_table) => {
|
Graphic::Gradient(stops_list) => {
|
||||||
let Some(stops) = stops_table.element(0) else { continue };
|
let Some(stops) = stops_list.element(0) else { continue };
|
||||||
let gradient_type: GradientType = stops_table.attribute_cloned_or_default(ATTR_GRADIENT_TYPE, 0);
|
let gradient_type: GradientType = stops_list.attribute_cloned_or_default(ATTR_GRADIENT_TYPE, 0);
|
||||||
let gradient_transform: DAffine2 = stops_table.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
let gradient_transform: DAffine2 = stops_list.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||||
let spread_method: GradientSpreadMethod = stops_table.attribute_cloned_or_default(ATTR_SPREAD_METHOD, 0);
|
let spread_method: GradientSpreadMethod = stops_list.attribute_cloned_or_default(ATTR_SPREAD_METHOD, 0);
|
||||||
|
|
||||||
let mut peniko_stops = peniko::ColorStops::new();
|
let mut peniko_stops = peniko::ColorStops::new();
|
||||||
for (position, color, _) in stops.interpolated_samples() {
|
for (position, color, _) in stops.interpolated_samples() {
|
||||||
@@ -1259,14 +1265,18 @@ impl Render for List<Vector> {
|
|||||||
let brush_transform = kurbo::Affine::new((inverse_element_transform * parent_transform).to_cols_array());
|
let brush_transform = kurbo::Affine::new((inverse_element_transform * parent_transform).to_cols_array());
|
||||||
scene.fill(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), &fill, Some(brush_transform), path);
|
scene.fill(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), &fill, Some(brush_transform), path);
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
Graphic::Vector(_) | Graphic::RasterCPU(_) | Graphic::RasterGPU(_) | Graphic::Graphic(_) => {
|
||||||
|
scene.push_clip_layer(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), path);
|
||||||
|
paint.render_to_vello(scene, multiplied_transform, context, render_params);
|
||||||
|
scene.pop_layer();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Branching vectors without regions (e.g. mesh grids) need face-by-face fill rendering.
|
// Branching vectors without regions (e.g. mesh grids) need face-by-face fill rendering.
|
||||||
let use_face_fill = element.use_face_fill();
|
let use_face_fill = element.use_face_fill();
|
||||||
let do_fill = |scene: &mut Scene| {
|
let do_fill = |scene: &mut Scene, context: &mut RenderContext| {
|
||||||
if use_face_fill {
|
if use_face_fill {
|
||||||
for mut face_path in element.construct_faces().filter(|face| face.area() >= 0.) {
|
for mut face_path in element.construct_faces().filter(|face| face.area() >= 0.) {
|
||||||
face_path.apply_affine(Affine::new(applied_stroke_transform.to_cols_array()));
|
face_path.apply_affine(Affine::new(applied_stroke_transform.to_cols_array()));
|
||||||
@@ -1274,12 +1284,12 @@ impl Render for List<Vector> {
|
|||||||
for element in face_path {
|
for element in face_path {
|
||||||
kurbo_path.push(element);
|
kurbo_path.push(element);
|
||||||
}
|
}
|
||||||
do_fill_path(scene, &kurbo_path, peniko::Fill::NonZero);
|
do_fill_path(scene, context, &kurbo_path, peniko::Fill::NonZero);
|
||||||
}
|
}
|
||||||
} else if element.is_branching() {
|
} else if element.is_branching() {
|
||||||
do_fill_path(scene, &path, peniko::Fill::EvenOdd);
|
do_fill_path(scene, context, &path, peniko::Fill::EvenOdd);
|
||||||
} else {
|
} else {
|
||||||
do_fill_path(scene, &path, peniko::Fill::NonZero);
|
do_fill_path(scene, context, &path, peniko::Fill::NonZero);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1349,7 +1359,7 @@ impl Render for List<Vector> {
|
|||||||
|
|
||||||
if wants_stroke_below {
|
if wants_stroke_below {
|
||||||
scene.push_layer(peniko::Fill::NonZero, peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect);
|
scene.push_layer(peniko::Fill::NonZero, peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect);
|
||||||
vector_list.render_to_vello(scene, parent_transform, _context, &render_params.for_alignment(applied_stroke_transform));
|
vector_list.render_to_vello(scene, parent_transform, context, &render_params.for_alignment(applied_stroke_transform));
|
||||||
scene.push_layer(peniko::Fill::NonZero, peniko::BlendMode::new(peniko::Mix::Normal, compose), 1., kurbo::Affine::IDENTITY, &rect);
|
scene.push_layer(peniko::Fill::NonZero, peniko::BlendMode::new(peniko::Mix::Normal, compose), 1., kurbo::Affine::IDENTITY, &rect);
|
||||||
|
|
||||||
do_stroke(scene, 2.);
|
do_stroke(scene, 2.);
|
||||||
@@ -1357,13 +1367,13 @@ impl Render for List<Vector> {
|
|||||||
scene.pop_layer();
|
scene.pop_layer();
|
||||||
scene.pop_layer();
|
scene.pop_layer();
|
||||||
|
|
||||||
do_fill(scene);
|
do_fill(scene, context);
|
||||||
} else {
|
} else {
|
||||||
// Fill first (unclipped), then stroke (clipped) above
|
// Fill first (unclipped), then stroke (clipped) above
|
||||||
do_fill(scene);
|
do_fill(scene, context);
|
||||||
|
|
||||||
scene.push_layer(peniko::Fill::NonZero, peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect);
|
scene.push_layer(peniko::Fill::NonZero, peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect);
|
||||||
vector_list.render_to_vello(scene, parent_transform, _context, &render_params.for_alignment(applied_stroke_transform));
|
vector_list.render_to_vello(scene, parent_transform, context, &render_params.for_alignment(applied_stroke_transform));
|
||||||
scene.push_layer(peniko::Fill::NonZero, peniko::BlendMode::new(peniko::Mix::Normal, compose), 1., kurbo::Affine::IDENTITY, &rect);
|
scene.push_layer(peniko::Fill::NonZero, peniko::BlendMode::new(peniko::Mix::Normal, compose), 1., kurbo::Affine::IDENTITY, &rect);
|
||||||
|
|
||||||
do_stroke(scene, 2.);
|
do_stroke(scene, 2.);
|
||||||
@@ -1385,7 +1395,7 @@ impl Render for List<Vector> {
|
|||||||
|
|
||||||
for operation in &order {
|
for operation in &order {
|
||||||
match operation {
|
match operation {
|
||||||
Op::Fill => do_fill(scene),
|
Op::Fill => do_fill(scene, context),
|
||||||
Op::Stroke => do_stroke(scene, 1.),
|
Op::Stroke => do_stroke(scene, 1.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use core_types::Context;
|
use core_types::Context;
|
||||||
use core_types::list::List;
|
use core_types::list::{ATTR_FILL_GRAPHIC, List};
|
||||||
use core_types::registry::types::{Fraction, Percentage, PixelSize};
|
use core_types::registry::types::{Fraction, Percentage, PixelSize};
|
||||||
use core_types::transform::Footprint;
|
use core_types::transform::Footprint;
|
||||||
use core_types::{Color, Ctx, num_traits};
|
use core_types::{Color, Ctx, num_traits};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||||
use graphic_types::{Artboard, Graphic, Vector};
|
use graphic_types::{Artboard, Graphic, IntoGraphicList, Vector};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use math_parser::ast;
|
use math_parser::ast;
|
||||||
use math_parser::context::{EvalContext, NothingMap, ValueProvider};
|
use math_parser::context::{EvalContext, NothingMap, ValueProvider};
|
||||||
@@ -985,6 +985,29 @@ fn normalize(_: impl Ctx, vector: DVec2) -> DVec2 {
|
|||||||
vector.normalize_or_zero()
|
vector.normalize_or_zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the `fill_graphic` attribute on each item of the input vector list.
|
||||||
|
/// Used for testing of clipping-based fill rendering until the proper Fill node refactor lands.
|
||||||
|
#[node_macro::node(category("Debug"))]
|
||||||
|
fn fill_graphic<P: IntoGraphicList + 'n + Send>(
|
||||||
|
_: impl Ctx,
|
||||||
|
mut vectors: List<Vector>,
|
||||||
|
#[implementations(
|
||||||
|
List<Graphic>,
|
||||||
|
List<Vector>,
|
||||||
|
List<Raster<CPU>>,
|
||||||
|
List<Raster<GPU>>,
|
||||||
|
List<Color>,
|
||||||
|
List<GradientStops>,
|
||||||
|
)]
|
||||||
|
fill_graphic: P,
|
||||||
|
) -> List<Vector> {
|
||||||
|
let paint_list = fill_graphic.into_graphic_list();
|
||||||
|
for row_idx in 0..vectors.len() {
|
||||||
|
vectors.set_attribute(ATTR_FILL_GRAPHIC, row_idx, paint_list.clone());
|
||||||
|
}
|
||||||
|
vectors
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user