ferroui_base/rendering/composition/drawing/
composition_painted_brush.rs1use crate::media::immutable::{ImmutableDashStyle, ImmutablePen, ImmutableSolidColorBrush};
7use crate::media::imaging::BitmapBlendingMode;
8use crate::media::{BoxShadows, Colors, IBrush, IImmutableBrush, IPen, ITransform};
9use crate::platform::{IDrawingContextImpl, IGeometryImpl, IGlyphRunImpl};
10use crate::rendering::composition::brushes::paint_composition_brush;
11use crate::rendering::composition::server::IServerObject;
12use crate::{Point, Rect, RelativePoint, RoundedRect};
13use std::any::Any;
14use std::rc::Rc;
15
16pub struct CompositionPaintedBrush {
22 object: Rc<dyn IServerObject>,
23}
24
25#[derive(Clone, Copy)]
27pub(crate) enum PaintedShape<'a> {
28 Rectangle(RoundedRect),
29 Ellipse(Rect),
30 Geometry(&'a dyn IGeometryImpl),
31 Line(Point, Point),
32 GlyphRun(&'a dyn IGlyphRunImpl),
33}
34
35impl PaintedShape<'_> {
36 fn bounds(&self) -> Rect {
38 match *self {
39 PaintedShape::Rectangle(rect) => rect.rect,
40 PaintedShape::Ellipse(rect) => rect,
41 PaintedShape::Geometry(geometry) => geometry.bounds(),
42 PaintedShape::Line(p1, p2) => Rect::from_points(Point::new(p1.x.min(p2.x), p1.y.min(p2.y)), Point::new(p1.x.max(p2.x), p1.y.max(p2.y))),
43 PaintedShape::GlyphRun(glyph_run) => glyph_run.bounds(),
44 }
45 }
46
47 fn draw(&self, canvas: &mut dyn IDrawingContextImpl, brush: Option<&dyn IBrush>, pen: Option<&dyn IPen>) {
49 match *self {
50 PaintedShape::Rectangle(rect) => canvas.draw_rectangle(brush, pen, rect, &BoxShadows::default()),
51 PaintedShape::Ellipse(rect) => canvas.draw_ellipse(brush, pen, rect),
52 PaintedShape::Geometry(geometry) => canvas.draw_geometry(brush, pen, geometry),
53 PaintedShape::Line(p1, p2) => canvas.draw_line(pen, p1, p2),
54 PaintedShape::GlyphRun(glyph_run) => {
55 if let Some(brush) = brush {
56 canvas.draw_glyph_run(Some(brush), glyph_run);
57 }
58 }
59 }
60 }
61}
62
63fn coverage_pen(pen: &dyn IPen) -> ImmutablePen {
65 let white: Rc<dyn IImmutableBrush> = Rc::new(ImmutableSolidColorBrush::new(Colors::WHITE));
66 let dash_style = pen
67 .dash_style()
68 .map(|dash_style| Rc::new(ImmutableDashStyle::new(dash_style.dashes().as_deref(), dash_style.offset())));
69 ImmutablePen::new(Some(white), pen.thickness(), dash_style, pen.line_cap(), pen.line_join(), pen.miter_limit())
70}
71
72impl CompositionPaintedBrush {
73 pub fn new(object: Rc<dyn IServerObject>) -> CompositionPaintedBrush {
75 CompositionPaintedBrush { object }
76 }
77
78 pub fn object(&self) -> &Rc<dyn IServerObject> {
80 &self.object
81 }
82
83 pub fn of(brush: Option<&dyn IBrush>) -> Option<&CompositionPaintedBrush> {
85 brush?.as_any().downcast_ref::<CompositionPaintedBrush>()
86 }
87
88 pub fn paint(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
90 paint_composition_brush(canvas, &self.object, rect);
91 }
92
93 pub(crate) fn fill(&self, canvas: &mut dyn IDrawingContextImpl, shape: PaintedShape<'_>) {
97 match shape {
98 PaintedShape::Rectangle(rect) => {
99 canvas.push_clip_rounded(rect);
100 self.paint(canvas, rect.rect);
101 canvas.pop_clip();
102 }
103 PaintedShape::Ellipse(rect) => {
104 canvas.push_clip_rounded(RoundedRect::from_radius_xy(rect, rect.width / 2.0, rect.height / 2.0));
105 self.paint(canvas, rect);
106 canvas.pop_clip();
107 }
108 PaintedShape::Geometry(geometry) => {
109 canvas.push_geometry_clip(geometry);
110 self.paint(canvas, geometry.bounds());
111 canvas.pop_geometry_clip();
112 }
113 PaintedShape::Line(..) => {}
114 PaintedShape::GlyphRun(_) => self.paint_through_coverage(canvas, shape, shape.bounds(), None),
115 }
116 }
117
118 pub(crate) fn stroke(&self, canvas: &mut dyn IDrawingContextImpl, pen: &dyn IPen, shape: PaintedShape<'_>) {
122 let reach = pen.thickness().max(0.0) + pen.miter_limit().max(1.0) * pen.thickness().max(0.0) / 2.0;
123 let bounds = shape.bounds().inflate(reach);
124 let coverage = coverage_pen(pen);
125 self.paint_through_coverage(canvas, shape, bounds, Some(&coverage));
126 }
127
128 fn paint_through_coverage(
133 &self,
134 canvas: &mut dyn IDrawingContextImpl,
135 shape: PaintedShape<'_>,
136 bounds: Rect,
137 pen: Option<&ImmutablePen>,
138 ) {
139 if canvas.as_drawing_context_impl_with_layer_effects().is_none() || bounds.width <= 0.0 || bounds.height <= 0.0 {
140 return;
141 }
142 canvas.push_layer(bounds);
143 self.paint(canvas, bounds);
144 if let Some(layers) = canvas.as_drawing_context_impl_with_layer_effects() {
145 layers.push_blend_layer(bounds, BitmapBlendingMode::DestinationIn);
146 }
147 let white = ImmutableSolidColorBrush::new(Colors::WHITE);
148 match pen {
149 Some(pen) => shape.draw(canvas, None, Some(pen)),
150 None => shape.draw(canvas, Some(&white), None),
151 }
152 if let Some(layers) = canvas.as_drawing_context_impl_with_layer_effects() {
153 layers.pop_blend_layer();
154 }
155 canvas.pop_layer();
156 }
157}
158
159impl IBrush for CompositionPaintedBrush {
160 fn opacity(&self) -> f64 {
161 1.0
162 }
163
164 fn transform(&self) -> Option<Rc<dyn ITransform>> {
165 None
166 }
167
168 fn transform_origin(&self) -> RelativePoint {
169 RelativePoint::default()
170 }
171
172 fn relative_transform(&self) -> Option<Rc<dyn ITransform>> {
173 None
174 }
175
176 fn as_any(&self) -> &dyn Any {
177 self
178 }
179}
180
181pub(crate) fn draw_with_painted_brushes(
184 canvas: &mut dyn IDrawingContextImpl,
185 brush: Option<&dyn IBrush>,
186 pen: Option<&dyn IPen>,
187 shape: PaintedShape<'_>,
188 box_shadows: Option<&BoxShadows>,
189) -> bool {
190 let painted_fill = CompositionPaintedBrush::of(brush);
191 let pen_brush = pen.and_then(|pen| pen.brush());
192 let painted_stroke = CompositionPaintedBrush::of(pen_brush.as_deref());
193 if painted_fill.is_none() && painted_stroke.is_none() {
194 return false;
195 }
196 if let (Some(box_shadows), PaintedShape::Rectangle(rect)) = (box_shadows, shape) {
198 if box_shadows.count() > 0 {
199 canvas.draw_rectangle(None, None, rect, box_shadows);
200 }
201 }
202 match painted_fill {
203 Some(painted) => painted.fill(canvas, shape),
204 None => {
205 if brush.is_some() {
206 shape.draw(canvas, brush, None);
207 }
208 }
209 }
210 if let Some(pen) = pen {
211 match painted_stroke {
212 Some(painted) => painted.stroke(canvas, pen, shape),
213 None => shape.draw(canvas, None, Some(pen)),
214 }
215 }
216 true
217}
218
219pub(crate) fn painted_brush_of(object: &Rc<dyn IServerObject>) -> Option<Rc<dyn IBrush>> {
223 is_painted_composition_brush(&**object).then(|| Rc::new(CompositionPaintedBrush::new(object.clone())) as Rc<dyn IBrush>)
224}
225
226pub(crate) fn is_painted_composition_brush(object: &dyn IServerObject) -> bool {
229 use crate::rendering::composition::brushes::{
230 ServerCompositionBackdropBrush, ServerCompositionEffectBrush, ServerCompositionMaskBrush,
231 ServerCompositionNineGridBrush, ServerCompositionSurfaceBrush,
232 };
233 let any = object.as_any();
234 any.is::<ServerCompositionSurfaceBrush>()
235 || any.is::<ServerCompositionNineGridBrush>()
236 || any.is::<ServerCompositionMaskBrush>()
237 || any.is::<ServerCompositionEffectBrush>()
238 || any.is::<ServerCompositionBackdropBrush>()
239 || any.is::<crate::rendering::composition::brushes::ServerCompositionHostBackdropBrush>()
240 || any.is::<crate::rendering::composition::brushes::ServerXamlCompositionBrush>()
241}