use super::*;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum RenderQuality {
Draft,
#[default]
Final,
}
#[derive(Clone, Debug)]
pub struct DrawContext {
quality: RenderQuality,
time: crate::Time,
pub(crate) device_scale: f64,
fonts: Option<crate::asset::environment::FontEnvironment>,
assets: crate::Assets,
limits: crate::ValidationLimits,
layout_cache: Option<std::sync::Arc<crate::SceneCache>>,
render_cache: Option<crate::RenderCache>,
diagnostics: crate::RequestDiagnostics,
font_usage: crate::FontUsage,
}
impl Default for DrawContext {
fn default() -> Self {
Self {
quality: RenderQuality::Final,
time: crate::Time::ZERO,
device_scale: 1.0,
fonts: None,
assets: crate::Assets::default(),
limits: crate::ValidationLimits::default(),
layout_cache: None,
render_cache: None,
diagnostics: crate::RequestDiagnostics::default(),
font_usage: crate::FontUsage::default(),
}
}
}
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 {
quality: RenderQuality::Final,
time: crate::Time::ZERO,
device_scale,
fonts: None,
assets: crate::Assets::default(),
limits: crate::ValidationLimits::default(),
layout_cache: None,
render_cache: None,
diagnostics: crate::RequestDiagnostics::default(),
font_usage: crate::FontUsage::default(),
})
}
pub fn quality(mut self, quality: RenderQuality) -> Self {
self.quality = quality;
self
}
pub fn render_quality(&self) -> RenderQuality {
self.quality
}
pub fn with_time(mut self, time: crate::Time) -> Self {
self.time = time;
self
}
pub fn time(&self) -> crate::Time {
self.time
}
pub fn device_scale(&self) -> f64 {
self.device_scale
}
pub fn with_fonts(
mut self,
fonts: impl Into<crate::asset::environment::FontEnvironment>,
) -> Self {
self.fonts = Some(fonts.into());
self
}
pub fn fonts(&self) -> Option<&crate::asset::environment::FontEnvironment> {
self.fonts.as_ref()
}
pub fn with_assets(mut self, assets: &crate::Assets) -> Self {
self.assets = assets.clone().with_diagnostics(self.diagnostics.clone());
if let Some(fonts) = assets.fonts() {
self.fonts = Some(fonts.clone());
}
self
}
pub fn assets(&self) -> &crate::Assets {
&self.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.try_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_layout_cache(mut self, cache: std::sync::Arc<crate::SceneCache>) -> Self {
self.layout_cache = Some(cache);
self
}
pub fn layout_cache(&self) -> Option<&std::sync::Arc<crate::SceneCache>> {
self.layout_cache.as_ref()
}
pub fn render_cache(mut self, cache: crate::RenderCache) -> Self {
self.layout_cache = Some(cache.partition(crate::CachePartition::Paragraph).clone());
self.render_cache = Some(cache);
self
}
pub fn configured_render_cache(&self) -> Option<&crate::RenderCache> {
self.render_cache.as_ref()
}
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.layout_cache {
Some(cache) => cache.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_diagnostics(mut self, diagnostics: crate::RequestDiagnostics) -> Self {
self.diagnostics = diagnostics;
self.assets = self.assets.with_diagnostics(self.diagnostics.clone());
self
}
pub fn diagnostics(&self) -> &crate::RequestDiagnostics {
&self.diagnostics
}
}
pub trait Drawable: Send + Sync {
fn resolve_node(&self, _context: &DrawContext) -> Result<Option<crate::Node>, ValidationError> {
Ok(None)
}
fn bounds(&self, context: &DrawContext) -> Rect;
fn try_bounds(&self, context: &DrawContext) -> Result<Rect, ValidationError> {
Ok(self.bounds(context))
}
fn baseline(&self, _context: &DrawContext) -> Option<f64> {
None
}
fn draw(&self, context: &DrawContext, canvas: &mut Canvas<'_>) -> Result<(), ValidationError>;
}
#[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().quality(RenderQuality::Draft);
assert_eq!(context.clone().render_quality(), RenderQuality::Draft);
assert_eq!(context.device_scale(), 0.5);
}
}