use crate::media::imaging::BitmapEncoderOptions;
use crate::media::immutable::ImmutablePen;
use crate::media::{Brushes, IBrush, IImmutableBrush, IPen, ImmediateDrawingContext, IntersectionResult};
use crate::platform::{IBitmapImpl, IGeometryImpl, ITransformedGeometryImpl};
use crate::rendering::composition::drawing::{RenderDataResource, RenderDataStream};
use crate::rendering::scene_graph::ICustomDrawOperation;
use crate::rendering::testing::{DrawingLog, MockDrawingContextImpl, MockGeometryImpl};
use crate::{Matrix, PixelSize, Point, Rect, RoundedRect, Vector};
use std::any::Any;
use std::rc::Rc;
use std::sync::Arc;
pub(crate) fn black() -> Rc<dyn IBrush> {
Brushes::black()
}
pub(crate) fn black_pen(thickness: f64) -> Rc<dyn IPen> {
let brush: Rc<dyn IImmutableBrush> = Brushes::black();
Rc::new(ImmutablePen::with_brush(Some(brush), thickness))
}
pub(crate) fn b(brush: &Rc<dyn IBrush>) -> Option<RenderDataResource> {
Some(RenderDataResource::Brush(brush.clone()))
}
pub(crate) fn p(pen: &Rc<dyn IPen>) -> Option<RenderDataResource> {
Some(RenderDataResource::Pen(pen.clone()))
}
pub(crate) fn rrect(x: f64, y: f64, width: f64, height: f64) -> RoundedRect {
RoundedRect::from_rect(Rect::new(x, y, width, height))
}
pub(crate) fn replay(stream: &RenderDataStream) -> Vec<String> {
let log = DrawingLog::new();
let mut context = MockDrawingContextImpl::new(log.clone());
context.log_transforms = false;
stream.replay(&mut context);
log.entries()
}
#[derive(Default)]
pub(crate) struct TestGeometryImpl {
pub fill: Vec<Point>,
pub stroke: Vec<Point>,
pub stroke_pen: Option<TestPen>,
}
pub(crate) struct TestPen(Rc<dyn IPen>);
unsafe impl Send for TestPen {}
unsafe impl Sync for TestPen {}
impl TestGeometryImpl {
pub(crate) fn new() -> Arc<TestGeometryImpl> {
Arc::new(TestGeometryImpl::default())
}
pub(crate) fn with_fill(points: &[Point]) -> Arc<TestGeometryImpl> {
Arc::new(TestGeometryImpl { fill: points.to_vec(), ..Default::default() })
}
pub(crate) fn with_stroke_pen(pen: &Rc<dyn IPen>, points: &[Point]) -> Arc<TestGeometryImpl> {
Arc::new(TestGeometryImpl { stroke: points.to_vec(), stroke_pen: Some(TestPen(pen.clone())), ..Default::default() })
}
}
impl IGeometryImpl for TestGeometryImpl {
fn bounds(&self) -> Rect {
Rect::default()
}
fn contour_length(&self) -> f64 {
0.0
}
fn get_render_bounds(&self, _pen: Option<&dyn IPen>) -> Rect {
Rect::default()
}
fn get_widened_geometry(&self, _pen: &dyn IPen) -> Arc<dyn IGeometryImpl> {
MockGeometryImpl::new(Rect::default())
}
fn fill_contains(&self, point: Point) -> bool {
self.fill.contains(&point)
}
fn get_fill_intersection_result(&self, _geometry: &dyn IGeometryImpl) -> IntersectionResult {
IntersectionResult::NotCalculated
}
fn intersect(&self, _geometry: &dyn IGeometryImpl) -> Option<Arc<dyn IGeometryImpl>> {
None
}
fn stroke_contains(&self, pen: Option<&dyn IPen>, point: Point) -> bool {
let pen_matches = match (&self.stroke_pen, pen) {
(None, _) => true,
(Some(expected), Some(pen)) => *expected.0 == *pen,
(Some(_), None) => false,
};
pen_matches && self.stroke.contains(&point)
}
fn with_transform(&self, transform: Matrix) -> Arc<dyn ITransformedGeometryImpl> {
MockGeometryImpl::new(Rect::default()).with_transform(transform)
}
fn try_get_point_at_distance(&self, _distance: f64) -> Option<Point> {
None
}
fn try_get_point_and_tangent_at_distance(&self, _distance: f64) -> Option<(Point, Point)> {
None
}
fn try_get_segment(
&self,
_start_distance: f64,
_stop_distance: f64,
_start_on_begin_figure: bool,
) -> Option<Arc<dyn IGeometryImpl>> {
None
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Default)]
pub(crate) struct TestBitmapImpl;
impl IBitmapImpl for TestBitmapImpl {
fn dpi(&self) -> Vector {
Vector::default()
}
fn pixel_size(&self) -> PixelSize {
PixelSize::default()
}
fn version(&self) -> i32 {
0
}
fn save(&self, _stream: &mut dyn std::io::Write, _options: &BitmapEncoderOptions) -> std::io::Result<()> {
Ok(())
}
fn dispose(&self) {}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Default)]
pub(crate) struct Counter(std::sync::atomic::AtomicI32);
impl Counter {
pub(crate) fn new(value: i32) -> Self {
Self(std::sync::atomic::AtomicI32::new(value))
}
pub(crate) fn get(&self) -> i32 {
self.0.load(std::sync::atomic::Ordering::SeqCst)
}
pub(crate) fn set(&self, value: i32) {
self.0.store(value, std::sync::atomic::Ordering::SeqCst);
}
}
#[derive(Default)]
pub(crate) struct TestCustomOperation {
pub bounds: Rect,
pub hits: Vec<Point>,
pub render_count: Counter,
pub dispose_count: Counter,
}
impl TestCustomOperation {
pub(crate) fn new() -> Arc<TestCustomOperation> {
Arc::new(TestCustomOperation::default())
}
pub(crate) fn with_bounds(bounds: Rect) -> Arc<TestCustomOperation> {
Arc::new(TestCustomOperation { bounds, ..Default::default() })
}
pub(crate) fn with_hits(hits: &[Point]) -> Arc<TestCustomOperation> {
Arc::new(TestCustomOperation { hits: hits.to_vec(), ..Default::default() })
}
}
impl ICustomDrawOperation for TestCustomOperation {
fn bounds(&self) -> Rect {
self.bounds
}
fn hit_test(&self, p: Point) -> bool {
self.hits.contains(&p)
}
fn render(&self, _context: &mut ImmediateDrawingContext<'_>) {
self.render_count.set(self.render_count.get() + 1);
}
fn equals(&self, other: &dyn ICustomDrawOperation) -> bool {
std::ptr::addr_eq(self as *const Self, other as *const dyn ICustomDrawOperation)
}
fn dispose(&self) {
self.dispose_count.set(self.dispose_count.get() + 1);
}
}