use ferroui_base::data::BindingPriority;
use ferroui_base::media::imaging::{PngBitmapEncoderOptions, RenderTargetBitmap};
use ferroui_base::media::{ITransform, RectangleGeometry, TranslateTransform};
use ferroui_base::reactive::IDisposable;
use ferroui_base::{PixelSize, Rect, Ref, RelativePoint, RelativeUnit, Vector, Visual};
use ferroui_controls::Control;
use std::io::Write;
use std::rc::Rc;
pub trait VisualExtensions {
fn render_to(&self, destination: &mut dyn Write) -> std::io::Result<()> {
self.render_to_with_dpi(destination, 96.0)
}
fn render_to_with_dpi(&self, destination: &mut dyn Write, dpi: f64) -> std::io::Result<()>;
}
impl VisualExtensions for Control {
fn render_to_with_dpi(&self, destination: &mut dyn Write, dpi: f64) -> std::io::Result<()> {
if self.composition_visual().is_none() {
return Ok(());
}
let Some(transform) = self.visual_root().and_then(|visual_root| self.transform_to_visual(&visual_root)) else {
return Ok(());
};
let rect = Rect::from_size(self.bounds().size()).transform_to_aabb(transform);
let top = rect.top_left();
let pixel_size = PixelSize::new(rect.width as i32, rect.height as i32);
let dpi_vector = Vector::new(dpi, dpi);
let root: Ref<Control> = self
.visual_root()
.or_else(|| self.get_visual_root())
.and_then(|root| root.cast::<Control>())
.unwrap_or_else(|| self.to_ref());
let clip_region = RectangleGeometry::with_rect(rect);
let clip_to_bounds_setter =
root.set_value_with_priority(Visual::clip_to_bounds_property(), true, BindingPriority::Animation);
let clip_setter =
root.set_value_with_priority(Visual::clip_property(), Some(clip_region.upcast()), BindingPriority::Animation);
let render_transform_origin_setter = root.set_value_with_priority(
Visual::render_transform_origin_property(),
RelativePoint::from_point(top, RelativeUnit::Absolute),
BindingPriority::Animation,
);
let translate: Rc<dyn ITransform> = (&TranslateTransform::with_offset(-top.x, -top.y)).into();
let render_transform_setter = root.set_value_with_priority(
Visual::render_transform_property(),
Some(translate),
BindingPriority::Animation,
);
let result = {
let bitmap = RenderTargetBitmap::with_dpi(pixel_size, dpi_vector);
bitmap.render(&root.clone().upcast());
let saved = bitmap.save(&mut *destination, &PngBitmapEncoderOptions::DEFAULT.into());
bitmap.dispose();
saved
};
let setters: [Option<Rc<dyn IDisposable>>; 4] =
[render_transform_setter, render_transform_origin_setter, clip_setter, clip_to_bounds_setter];
for setter in setters.into_iter().flatten() {
setter.dispose();
}
self.invalidate_visual();
result
}
}