use super::*;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum RenderQuality {
Draft,
#[default]
Final,
}
#[derive(Clone, Debug)]
pub struct DrawContext {
pub(crate) device_scale: f64,
limits: crate::ValidationLimits,
evaluation: std::sync::Arc<Evaluation>,
font_usage: crate::FontUsage,
mark_invalid_geometry: bool,
}
impl Default for DrawContext {
fn default() -> Self {
Self {
device_scale: 1.0,
limits: crate::ValidationLimits::default(),
evaluation: std::sync::Arc::default(),
font_usage: crate::FontUsage::default(),
mark_invalid_geometry: false,
}
}
}
impl DrawContext {
pub fn new(device_scale: f64) -> Result<Self, ValidationError> {
if !device_scale.is_finite() || device_scale <= 0.0 {
return Err(ValidationError::invalid(
"drawing",
"device_scale",
"Require a finite, positive device scale",
));
}
Ok(Self {
device_scale,
..Self::default()
})
}
pub fn with_evaluation(mut self, evaluation: std::sync::Arc<Evaluation>) -> Self {
self.evaluation = evaluation;
self
}
pub fn evaluation(&self) -> &std::sync::Arc<Evaluation> {
&self.evaluation
}
fn edit(mut self, edit: impl FnOnce(Evaluation) -> Evaluation) -> Self {
let current = std::sync::Arc::try_unwrap(self.evaluation)
.unwrap_or_else(|shared| Evaluation::clone(&shared));
self.evaluation = std::sync::Arc::new(edit(current));
self
}
pub fn with_quality(self, quality: RenderQuality) -> Self {
self.edit(|evaluation| evaluation.with_quality(quality))
}
pub fn render_quality(&self) -> RenderQuality {
self.evaluation.quality()
}
pub fn with_time(self, time: crate::Time) -> Self {
self.edit(|evaluation| evaluation.with_time(time))
}
pub fn time(&self) -> crate::Time {
self.evaluation.time()
}
pub fn device_scale(&self) -> f64 {
self.device_scale
}
pub fn with_fonts(self, fonts: impl Into<crate::asset::environment::FontEnvironment>) -> Self {
self.edit(|evaluation| evaluation.with_fonts(fonts))
}
pub fn fonts(&self) -> Option<&crate::asset::environment::FontEnvironment> {
self.evaluation.fonts()
}
pub fn with_assets(self, assets: &crate::Assets) -> Self {
self.edit(|evaluation| evaluation.with_assets(assets))
}
pub fn assets(&self) -> &crate::Assets {
self.evaluation.assets()
}
pub fn record(
&self,
drawable: &dyn Drawable,
limits: ValidationLimits,
) -> Result<Vec<DrawCommand>, ValidationError> {
let mut state = ValidationState::new(limits)?;
state.enter("drawable", 1)?;
validate_rect(drawable.bounds(self)?, "drawable", "bounds")?;
Canvas::new(&mut state, "drawable", 2).record(drawable, self)
}
pub fn with_validation_limits(mut self, limits: crate::ValidationLimits) -> Self {
self.limits = limits;
self
}
pub fn validation_limits(&self) -> crate::ValidationLimits {
self.limits
}
pub fn with_render_cache(self, cache: crate::RenderCache) -> Self {
self.edit(|evaluation| evaluation.with_cache(cache))
}
pub fn render_cache(&self) -> Option<&crate::RenderCache> {
self.evaluation.cache()
}
pub fn cache_layout<
Key: Eq + std::hash::Hash + Send + Sync + 'static,
Value: Send + Sync + 'static,
>(
&self,
key: Key,
build: impl FnOnce() -> Value,
cost: impl FnOnce(&Value) -> usize,
) -> std::sync::Arc<Value> {
match self.render_cache() {
Some(cache) => cache
.partition(crate::CachePartition::Paragraph)
.get_with_cost(key, build, cost),
None => std::sync::Arc::new(build()),
}
}
pub fn with_font_usage(mut self, usage: crate::FontUsage) -> Self {
self.font_usage = usage;
self
}
pub fn font_usage(&self) -> &crate::FontUsage {
&self.font_usage
}
pub fn with_invalid_geometry_marker(mut self, mark: bool) -> Self {
self.mark_invalid_geometry = mark;
self
}
pub fn marks_invalid_geometry(&self) -> bool {
self.mark_invalid_geometry
}
pub fn with_diagnostics(self, diagnostics: crate::RequestDiagnostics) -> Self {
self.edit(|evaluation| evaluation.with_diagnostics(diagnostics))
}
pub fn diagnostics(&self) -> &crate::RequestDiagnostics {
self.evaluation.diagnostics()
}
}
pub trait Drawable: Send + Sync {
fn bounds(&self, context: &DrawContext) -> Result<Rect, ValidationError>;
fn baseline(&self, _context: &DrawContext) -> Option<f64> {
None
}
fn draw(&self, context: &DrawContext, canvas: &mut Canvas<'_>) -> Result<(), ValidationError>;
}
pub trait DeferredContent: Send + Sync {
fn resolve(&self, context: &DrawContext) -> Result<crate::Node, ValidationError>;
fn baseline(&self, _context: &DrawContext) -> Option<f64> {
None
}
}
#[cfg(test)]
mod quality_tests {
use super::*;
#[test]
fn quality_is_explicit_cloned_and_final_by_default() {
assert_eq!(
DrawContext::default().render_quality(),
RenderQuality::Final
);
let context = DrawContext::new(0.5)
.unwrap()
.with_quality(RenderQuality::Draft);
assert_eq!(context.clone().render_quality(), RenderQuality::Draft);
assert_eq!(context.device_scale(), 0.5);
}
}