use crate::core::types::Rect;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RetainedScope {
Container(u64),
Region(u64),
Fragment(u64),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct InvalidateBits(u8);
impl InvalidateBits {
pub const NONE: Self = Self(0);
pub const TRANSFORM: Self = Self(1 << 0);
pub const GEOMETRY: Self = Self(1 << 1);
pub const MATERIAL: Self = Self(1 << 2);
pub const OPACITY: Self = Self(1 << 3);
pub const STRUCTURE: Self = Self(1 << 4);
pub const ALL: Self = Self(0b1_1111);
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub const fn intersects(self, other: Self) -> bool {
(self.0 & other.0) != 0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ScopeDecision {
Reuse,
Record,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RetainedCaps {
pub offscreen_targets: bool,
}
pub trait RetainedSurface {
fn caps(&self) -> RetainedCaps;
fn begin_scope(&mut self, scope: RetainedScope, size: (f64, f64)) -> ScopeDecision;
fn replay(&mut self, ctx: &mut dyn super::RenderContext, scope: RetainedScope, dst_rect: Rect) -> bool;
fn begin_record(&mut self, ctx: &mut dyn super::RenderContext, scope: RetainedScope, size: (f64, f64));
fn end_record(&mut self, ctx: &mut dyn super::RenderContext, scope: RetainedScope);
fn invalidate(&mut self, scope: RetainedScope, bits: InvalidateBits);
fn free(&mut self, ctx: &mut dyn super::RenderContext, scope: RetainedScope);
fn clear(&mut self, ctx: &mut dyn super::RenderContext);
}
#[derive(Default)]
pub struct NoRetainedCache;
impl RetainedSurface for NoRetainedCache {
fn caps(&self) -> RetainedCaps {
RetainedCaps::default()
}
fn begin_scope(&mut self, _scope: RetainedScope, _size: (f64, f64)) -> ScopeDecision {
ScopeDecision::Record
}
fn replay(&mut self, _ctx: &mut dyn super::RenderContext, _scope: RetainedScope, _dst_rect: Rect) -> bool {
false
}
fn begin_record(&mut self, _ctx: &mut dyn super::RenderContext, _scope: RetainedScope, _size: (f64, f64)) {}
fn end_record(&mut self, _ctx: &mut dyn super::RenderContext, _scope: RetainedScope) {}
fn invalidate(&mut self, _scope: RetainedScope, _bits: InvalidateBits) {}
fn free(&mut self, _ctx: &mut dyn super::RenderContext, _scope: RetainedScope) {}
fn clear(&mut self, _ctx: &mut dyn super::RenderContext) {}
}
#[cfg(test)]
mod tests {
use super::*;
struct NoImageContext;
impl crate::render::Painter for NoImageContext {
fn save(&mut self) {}
fn restore(&mut self) {}
fn translate(&mut self, _x: f64, _y: f64) {}
fn rotate(&mut self, _angle: f64) {}
fn scale(&mut self, _x: f64, _y: f64) {}
fn set_fill_color(&mut self, _color: &str) {}
fn set_global_alpha(&mut self, _alpha: f64) {}
fn set_stroke_color(&mut self, _color: &str) {}
fn set_stroke_width(&mut self, _width: f64) {}
fn set_line_dash(&mut self, _pattern: &[f64]) {}
fn set_line_cap(&mut self, _cap: &str) {}
fn set_line_join(&mut self, _join: &str) {}
fn begin_path(&mut self) {}
fn move_to(&mut self, _x: f64, _y: f64) {}
fn line_to(&mut self, _x: f64, _y: f64) {}
fn close_path(&mut self) {}
fn rect(&mut self, _x: f64, _y: f64, _w: f64, _h: f64) {}
fn arc(&mut self, _cx: f64, _cy: f64, _r: f64, _s: f64, _e: f64) {}
fn ellipse(&mut self, _cx: f64, _cy: f64, _rx: f64, _ry: f64, _rot: f64, _s: f64, _e: f64) {}
fn quadratic_curve_to(&mut self, _cpx: f64, _cpy: f64, _x: f64, _y: f64) {}
fn bezier_curve_to(&mut self, _cp1x: f64, _cp1y: f64, _cp2x: f64, _cp2y: f64, _x: f64, _y: f64) {}
fn stroke(&mut self) {}
fn fill(&mut self) {}
}
impl crate::render::TextRenderer for NoImageContext {
fn set_font(&mut self, _font: &str) {}
fn set_text_align(&mut self, _align: crate::render::TextAlign) {}
fn set_text_baseline(&mut self, _baseline: crate::render::TextBaseline) {}
fn fill_text(&mut self, _text: &str, _x: f64, _y: f64) {}
fn stroke_text(&mut self, _text: &str, _x: f64, _y: f64) {}
}
impl crate::render::TextMetrics for NoImageContext {
fn measure_text(&self, _text: &str) -> f64 {
0.0
}
fn text_bounds(&self, _text: &str, _font: &str) -> crate::render::TextBounds {
crate::render::TextBounds { x: 0.0, y: 0.0, w: 0.0, h: 0.0, ascent: 0.0, descent: 0.0 }
}
}
impl crate::render::Masking for NoImageContext {
fn clip(&mut self) {}
}
impl crate::render::Effects for NoImageContext {}
impl crate::render::ShapeHelpers for NoImageContext {
fn fill_rect(&mut self, _x: f64, _y: f64, _w: f64, _h: f64) {}
fn stroke_rect(&mut self, _x: f64, _y: f64, _w: f64, _h: f64) {}
}
impl crate::render::GradientPainter for NoImageContext {}
impl crate::render::UiEffectHelpers for NoImageContext {}
impl crate::render::BatchPainter for NoImageContext {}
impl crate::render::RenderContext for NoImageContext {
fn dpr(&self) -> f64 {
1.0
}
}
#[test]
fn caps_default_is_false() {
let cache = NoRetainedCache;
assert!(!cache.caps().offscreen_targets);
}
#[test]
fn begin_scope_always_decides_record() {
let mut cache = NoRetainedCache;
assert_eq!(
cache.begin_scope(RetainedScope::Container(1), (10.0, 10.0)),
ScopeDecision::Record
);
assert_eq!(
cache.begin_scope(RetainedScope::Region(1), (10.0, 10.0)),
ScopeDecision::Record
);
assert_eq!(
cache.begin_scope(RetainedScope::Fragment(1), (10.0, 10.0)),
ScopeDecision::Record
);
}
#[test]
fn replay_always_returns_false() {
let mut cache = NoRetainedCache;
let mut ctx = NoImageContext;
let dst = Rect::new(0.0, 0.0, 10.0, 10.0);
assert!(!cache.replay(&mut ctx, RetainedScope::Container(1), dst));
}
#[test]
fn mutating_calls_are_true_no_ops() {
let mut cache = NoRetainedCache;
let mut ctx = NoImageContext;
let scope = RetainedScope::Fragment(7);
cache.begin_record(&mut ctx, scope, (5.0, 5.0));
cache.end_record(&mut ctx, scope);
cache.invalidate(scope, InvalidateBits::ALL);
cache.free(&mut ctx, scope);
cache.clear(&mut ctx);
assert!(!cache.caps().offscreen_targets);
assert_eq!(cache.begin_scope(scope, (5.0, 5.0)), ScopeDecision::Record);
}
#[test]
fn invalidate_bits_union_and_intersects() {
let combo = InvalidateBits::GEOMETRY.union(InvalidateBits::MATERIAL);
assert!(combo.intersects(InvalidateBits::GEOMETRY));
assert!(combo.intersects(InvalidateBits::MATERIAL));
assert!(!combo.intersects(InvalidateBits::TRANSFORM));
assert!(!InvalidateBits::NONE.intersects(InvalidateBits::ALL));
assert!(InvalidateBits::ALL.intersects(InvalidateBits::STRUCTURE));
}
}