Skip to main content

tessera_components/
painter.rs

1//! Unified painter content for vector and raster imagery.
2//!
3//! ## Usage
4//!
5//! Pass one painter value into image-like and icon-like components.
6use std::{
7    io,
8    path::{Path, PathBuf},
9    sync::Arc,
10};
11
12use tessera_ui::{Asset, LoadState, Resource, Source};
13
14use crate::{
15    image::{ImageData, decode_raster},
16    image_vector::{ImageVectorData, decode_vector},
17};
18
19/// Shared visual content that can be rendered by image-like components.
20///
21/// Encoded SVG or raster sources are decoded off the UI thread and show
22/// nothing until they are ready.
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum Painter {
25    /// Content decoded from a source.
26    Source(Resource<Painter>),
27    /// Vector content backed by decoded vector geometry.
28    Vector(ImageVectorData),
29    /// Raster content backed by decoded image pixels.
30    Raster(ImageData),
31}
32
33impl From<ImageVectorData> for Painter {
34    fn from(data: ImageVectorData) -> Self {
35        Self::Vector(data)
36    }
37}
38
39impl From<ImageData> for Painter {
40    fn from(data: ImageData) -> Self {
41        Self::Raster(data)
42    }
43}
44
45macro_rules! painter_from_source {
46    ($($ty:ty),* $(,)?) => {$(
47        impl From<$ty> for Painter {
48            fn from(source: $ty) -> Self {
49                Self::Source(Resource::load(source, decode))
50            }
51        }
52    )*};
53}
54
55painter_from_source!(
56    Source,
57    Asset,
58    &str,
59    String,
60    &Path,
61    PathBuf,
62    &[u8],
63    Vec<u8>,
64    Arc<[u8]>
65);
66
67impl Painter {
68    pub(crate) fn resolve(self) -> Option<Self> {
69        match self {
70            Self::Source(resource) => match resource.get() {
71                LoadState::Ready(painter) => Some(painter),
72                LoadState::Loading | LoadState::Failed(_) => None,
73            },
74            painter => Some(painter),
75        }
76    }
77}
78
79fn decode(bytes: Arc<[u8]>) -> io::Result<Painter> {
80    decode_vector(&bytes)
81        .map(Painter::Vector)
82        .or_else(|vector| {
83            decode_raster(&bytes)
84                .map(Painter::Raster)
85                .map_err(|raster| {
86                    io::Error::other(format!("not SVG ({vector}) or raster ({raster})"))
87                })
88        })
89}