use core_types::list::List; use core_types::registry::types::Percentage; use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_OPACITY, ATTR_OPACITY_FILL, BlendMode, Color, Ctx}; use graphic_types::Graphic; use graphic_types::Vector; use graphic_types::raster_types::{CPU, Raster}; use vector_types::GradientStops; pub(crate) trait MultiplyAlpha { fn multiply_alpha(&mut self, factor: f64); } impl MultiplyAlpha for Color { fn multiply_alpha(&mut self, factor: f64) { *self = Color::from_rgbaf32_unchecked(self.r(), self.g(), self.b(), (self.a() * factor as f32).clamp(0., 1.)) } } fn multiply_list_attribute(list: &mut List, key: &str, factor: f64) { if let Some(values) = list.iter_attribute_values_mut::(key) { for v in values { *v *= factor; } } else { for v in list.iter_attribute_values_mut_or_default::(key) { *v = factor; } } } impl MultiplyAlpha for List { fn multiply_alpha(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY, factor); } } impl MultiplyAlpha for List { fn multiply_alpha(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY, factor); } } impl MultiplyAlpha for List> { fn multiply_alpha(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY, factor); } } impl MultiplyAlpha for List { fn multiply_alpha(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY, factor); } } impl MultiplyAlpha for List { fn multiply_alpha(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY, factor); } } impl MultiplyAlpha for List { fn multiply_alpha(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY, factor); } } pub(crate) trait MultiplyFill { fn multiply_fill(&mut self, factor: f64); } impl MultiplyFill for Color { fn multiply_fill(&mut self, factor: f64) { *self = Color::from_rgbaf32_unchecked(self.r(), self.g(), self.b(), (self.a() * factor as f32).clamp(0., 1.)) } } impl MultiplyFill for List { fn multiply_fill(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY_FILL, factor); } } impl MultiplyFill for List { fn multiply_fill(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY_FILL, factor); } } impl MultiplyFill for List> { fn multiply_fill(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY_FILL, factor); } } impl MultiplyFill for List { fn multiply_fill(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY_FILL, factor); } } impl MultiplyFill for List { fn multiply_fill(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY_FILL, factor); } } impl MultiplyFill for List { fn multiply_fill(&mut self, factor: f64) { multiply_list_attribute(self, ATTR_OPACITY_FILL, factor); } } trait SetBlendMode { fn set_blend_mode(&mut self, blend_mode: BlendMode); } fn set_list_blend_mode(list: &mut List, blend_mode: BlendMode) { for v in list.iter_attribute_values_mut_or_default::(ATTR_BLEND_MODE) { *v = blend_mode; } } impl SetBlendMode for List { fn set_blend_mode(&mut self, blend_mode: BlendMode) { set_list_blend_mode(self, blend_mode); } } impl SetBlendMode for List { fn set_blend_mode(&mut self, blend_mode: BlendMode) { set_list_blend_mode(self, blend_mode); } } impl SetBlendMode for List> { fn set_blend_mode(&mut self, blend_mode: BlendMode) { set_list_blend_mode(self, blend_mode); } } impl SetBlendMode for List { fn set_blend_mode(&mut self, blend_mode: BlendMode) { set_list_blend_mode(self, blend_mode); } } impl SetBlendMode for List { fn set_blend_mode(&mut self, blend_mode: BlendMode) { set_list_blend_mode(self, blend_mode); } } impl SetBlendMode for List { fn set_blend_mode(&mut self, blend_mode: BlendMode) { set_list_blend_mode(self, blend_mode); } } trait SetClip { fn set_clip(&mut self, clip: bool); } fn set_list_clip(list: &mut List, clip: bool) { for v in list.iter_attribute_values_mut_or_default::(ATTR_CLIPPING_MASK) { *v = clip; } } impl SetClip for List { fn set_clip(&mut self, clip: bool) { set_list_clip(self, clip); } } impl SetClip for List { fn set_clip(&mut self, clip: bool) { set_list_clip(self, clip); } } impl SetClip for List> { fn set_clip(&mut self, clip: bool) { set_list_clip(self, clip); } } impl SetClip for List { fn set_clip(&mut self, clip: bool) { set_list_clip(self, clip); } } impl SetClip for List { fn set_clip(&mut self, clip: bool) { set_list_clip(self, clip); } } impl SetClip for List { fn set_clip(&mut self, clip: bool) { set_list_clip(self, clip); } } /// Applies the blend mode to the input graphics. Setting this allows for customizing how overlapping content is composited together. #[node_macro::node(category("Blending"))] fn blend_mode( _: impl Ctx, /// The layer stack that will be composited when rendering. #[implementations( List, List, List>, List, List, List, )] mut content: T, /// The choice of equation that controls how brightness and color blends between overlapping pixels. blend_mode: BlendMode, ) -> T { // TODO: Find a way to make this apply once to the list's parent (i.e. its item in its parent List or Item) rather than applying to each item in its own list, which produces the undesired result content.set_blend_mode(blend_mode); content } /// Modifies the opacity and/or fill of the input graphics by multiplying the existing values by these percentages. /// Opacity affects the transparency of the content (together with anything above which is clipped to it). /// Fill affects the transparency of the content itself, independent of any content clipped to it. #[node_macro::node(category("Blending"))] fn opacity( _: impl Ctx, /// The layer stack that will be composited when rendering. #[implementations( List, List, List>, List, List, List, )] mut content: T, /// Whether the *Opacity* property is enabled, multiplying the existing opacity by the chosen percentage. #[widget(ParsedWidgetOverride::Hidden)] #[default(true)] has_opacity: bool, /// How visible the content should be, including any content clipped to it. /// Ranges from the default of 100% (fully opaque) to 0% (fully transparent). #[widget(ParsedWidgetOverride::Custom = "optional_percentage")] #[default(100.)] opacity: Percentage, /// Whether the *Fill* property is enabled, multiplying the existing fill by the chosen percentage. #[widget(ParsedWidgetOverride::Hidden)] has_fill: bool, /// How visible the content should be, independent of any content clipped to it. /// Ranges from 0% (fully transparent) to the default of 100% (fully opaque). #[widget(ParsedWidgetOverride::Custom = "optional_percentage")] #[default(100.)] fill: Percentage, ) -> T { // TODO: Find a way to make this apply once to the list's parent (i.e. its item in its parent List or Item) rather than applying to each item in its own list, which produces the undesired result if has_opacity { content.multiply_alpha(opacity / 100.); } if has_fill { content.multiply_fill(fill / 100.); } content } /// Sets whether the input graphics inherit the alpha of the content beneath them, "clipping" them to that content. #[node_macro::node(category("Blending"))] fn clipping_mask( _: impl Ctx, /// The layer stack that will be composited when rendering. #[implementations( List, List, List>, List, List, List, )] mut content: T, /// Whether the content inherits the alpha of the content beneath it. clip: bool, ) -> T { // TODO: Find a way to make this apply once to the list's parent (i.e. its item in its parent List or Item) rather than applying to each item in its own list, which produces the undesired result content.set_clip(clip); content }